Merge tag 'iommu-fixes-v5.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Aug 2019 17:16:59 +0000 (10:16 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Aug 2019 17:16:59 +0000 (10:16 -0700)
Pull iommu fixes from Joerg Roedel:

 - A couple more fixes for the Intel VT-d driver for bugs introduced
   during the recent conversion of this driver to use IOMMU core default
   domains.

 - Fix for common dma-iommu code to make sure MSI mappings happen in the
   correct domain for a device.

 - Fix a corner case in the handling of sg-lists in dma-iommu code that
   might cause dma_length to be truncated.

 - Mark a switch as fall-through in arm-smmu code.

* tag 'iommu-fixes-v5.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu:
  iommu/vt-d: Fix possible use-after-free of private domain
  iommu/vt-d: Detach domain before using a private one
  iommu/dma: Handle SG length overflow better
  iommu/vt-d: Correctly check format of page table in debugfs
  iommu/vt-d: Detach domain when move device out of group
  iommu/arm-smmu: Mark expected switch fall-through
  iommu/dma: Handle MSI mappings separately

drivers/iommu/arm-smmu-v3.c
drivers/iommu/dma-iommu.c
drivers/iommu/intel-iommu-debugfs.c
drivers/iommu/intel-iommu.c

index a9a9fabd396804a26b77039d0fe804cea3dc56dc..c5c93e48b4dbdf7409ca3b8008a24a4ab3214a49 100644 (file)
@@ -1186,8 +1186,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
                        ste_live = true;
                        break;
                case STRTAB_STE_0_CFG_ABORT:
-                       if (disable_bypass)
-                               break;
+                       BUG_ON(!disable_bypass);
+                       break;
                default:
                        BUG(); /* STE corruption */
                }
index a7f9c3edbcb299f83f8d4c6093c274a0bc5b3387..4ea9cf02ba2df68268fbece7189877f60a468f54 100644 (file)
@@ -459,13 +459,11 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
 {
        struct iommu_domain *domain = iommu_get_dma_domain(dev);
        struct iommu_dma_cookie *cookie = domain->iova_cookie;
-       size_t iova_off = 0;
+       struct iova_domain *iovad = &cookie->iovad;
+       size_t iova_off = iova_offset(iovad, phys);
        dma_addr_t iova;
 
-       if (cookie->type == IOMMU_DMA_IOVA_COOKIE) {
-               iova_off = iova_offset(&cookie->iovad, phys);
-               size = iova_align(&cookie->iovad, size + iova_off);
-       }
+       size = iova_align(iovad, size + iova_off);
 
        iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
        if (!iova)
@@ -764,7 +762,7 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
                 * - and wouldn't make the resulting output segment too long
                 */
                if (cur_len && !s_iova_off && (dma_addr & seg_mask) &&
-                   (cur_len + s_length <= max_len)) {
+                   (max_len - cur_len >= s_length)) {
                        /* ...then concatenate it with the previous one */
                        cur_len += s_length;
                } else {
@@ -1147,16 +1145,21 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
        if (!msi_page)
                return NULL;
 
-       iova = __iommu_dma_map(dev, msi_addr, size, prot);
-       if (iova == DMA_MAPPING_ERROR)
+       iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+       if (!iova)
                goto out_free_page;
 
+       if (iommu_map(domain, iova, msi_addr, size, prot))
+               goto out_free_iova;
+
        INIT_LIST_HEAD(&msi_page->list);
        msi_page->phys = msi_addr;
        msi_page->iova = iova;
        list_add(&msi_page->list, &cookie->msi_page_list);
        return msi_page;
 
+out_free_iova:
+       iommu_dma_free_iova(cookie, iova, size);
 out_free_page:
        kfree(msi_page);
        return NULL;
index 2b25d9c5933604eb70d6a83a7e1a91daa10e29d7..471f05d452e0198f3d2837f2a83e13d0984e1a12 100644 (file)
@@ -235,7 +235,7 @@ static void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus)
                tbl_wlk.ctx_entry = context;
                m->private = &tbl_wlk;
 
-               if (pasid_supported(iommu) && is_pasid_enabled(context)) {
+               if (dmar_readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT) {
                        pasid_dir_ptr = context->lo & VTD_PAGE_MASK;
                        pasid_dir_size = get_pasid_dir_size(context);
                        pasid_dir_walk(m, pasid_dir_ptr, pasid_dir_size);
index bdaed2da8a55e5a96904d0a182b9d44db8843777..12d094d08c0a2e982fb712b9310ceadf7213013d 100644 (file)
@@ -3449,6 +3449,7 @@ static bool iommu_need_mapping(struct device *dev)
                                dmar_domain = to_dmar_domain(domain);
                                dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
                        }
+                       dmar_remove_one_dev_info(dev);
                        get_private_domain_for_dev(dev);
                }
 
@@ -4790,7 +4791,8 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info)
 
        /* free the private domain */
        if (domain->flags & DOMAIN_FLAG_LOSE_CHILDREN &&
-           !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY))
+           !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
+           list_empty(&domain->devices))
                domain_exit(info->domain);
 
        free_devinfo_mem(info);
@@ -4803,7 +4805,8 @@ static void dmar_remove_one_dev_info(struct device *dev)
 
        spin_lock_irqsave(&device_domain_lock, flags);
        info = dev->archdata.iommu;
-       __dmar_remove_one_dev_info(info);
+       if (info)
+               __dmar_remove_one_dev_info(info);
        spin_unlock_irqrestore(&device_domain_lock, flags);
 }
 
@@ -5281,6 +5284,7 @@ static int intel_iommu_add_device(struct device *dev)
                if (device_def_domain_type(dev) == IOMMU_DOMAIN_IDENTITY) {
                        ret = iommu_request_dm_for_dev(dev);
                        if (ret) {
+                               dmar_remove_one_dev_info(dev);
                                dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
                                domain_add_dev_info(si_domain, dev);
                                dev_info(dev,
@@ -5291,6 +5295,7 @@ static int intel_iommu_add_device(struct device *dev)
                if (device_def_domain_type(dev) == IOMMU_DOMAIN_DMA) {
                        ret = iommu_request_dma_domain_for_dev(dev);
                        if (ret) {
+                               dmar_remove_one_dev_info(dev);
                                dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
                                if (!get_private_domain_for_dev(dev)) {
                                        dev_warn(dev,
@@ -5316,6 +5321,8 @@ static void intel_iommu_remove_device(struct device *dev)
        if (!iommu)
                return;
 
+       dmar_remove_one_dev_info(dev);
+
        iommu_group_remove_device(dev);
 
        iommu_device_unlink(&iommu->iommu, dev);