]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Apr 2022 05:28:05 +0000 (07:28 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Apr 2022 05:28:05 +0000 (07:28 +0200)
added patches:
iommu-dma-account-for-min_align_mask-w-swiotlb.patch
iommu-dma-check-config_swiotlb-more-broadly.patch
iommu-dma-fold-_swiotlb-helpers-into-callers.patch
iommu-dma-skip-extra-sync-during-unmap-w-swiotlb.patch
swiotlb-support-aligned-swiotlb-buffers.patch

queue-5.15/iommu-dma-account-for-min_align_mask-w-swiotlb.patch [new file with mode: 0644]
queue-5.15/iommu-dma-check-config_swiotlb-more-broadly.patch [new file with mode: 0644]
queue-5.15/iommu-dma-fold-_swiotlb-helpers-into-callers.patch [new file with mode: 0644]
queue-5.15/iommu-dma-skip-extra-sync-during-unmap-w-swiotlb.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/swiotlb-support-aligned-swiotlb-buffers.patch [new file with mode: 0644]

diff --git a/queue-5.15/iommu-dma-account-for-min_align_mask-w-swiotlb.patch b/queue-5.15/iommu-dma-account-for-min_align_mask-w-swiotlb.patch
new file mode 100644 (file)
index 0000000..bdf3872
--- /dev/null
@@ -0,0 +1,61 @@
+From 2cbc61a1b1665c84282dbf2b1747ffa0b6248639 Mon Sep 17 00:00:00 2001
+From: David Stevens <stevensd@chromium.org>
+Date: Wed, 29 Sep 2021 11:33:00 +0900
+Subject: iommu/dma: Account for min_align_mask w/swiotlb
+
+From: David Stevens <stevensd@chromium.org>
+
+commit 2cbc61a1b1665c84282dbf2b1747ffa0b6248639 upstream.
+
+Pass the non-aligned size to __iommu_dma_map when using swiotlb bounce
+buffers in iommu_dma_map_page, to account for min_align_mask.
+
+To deal with granule alignment, __iommu_dma_map maps iova_align(size +
+iova_off) bytes starting at phys - iova_off. If iommu_dma_map_page
+passes aligned size when using swiotlb, then this becomes
+iova_align(iova_align(orig_size) + iova_off). Normally iova_off will be
+zero when using swiotlb. However, this is not the case for devices that
+set min_align_mask. When iova_off is non-zero, __iommu_dma_map ends up
+mapping an extra page at the end of the buffer. Beyond just being a
+security issue, the extra page is not cleaned up by __iommu_dma_unmap.
+This causes problems when the IOVA is reused, due to collisions in the
+iommu driver.  Just passing the original size is sufficient, since
+__iommu_dma_map will take care of granule alignment.
+
+Fixes: 1f221a0d0dbf ("swiotlb: respect min_align_mask")
+Signed-off-by: David Stevens <stevensd@chromium.org>
+Link: https://lore.kernel.org/r/20210929023300.335969-8-stevensd@google.com
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/dma-iommu.c |    5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -806,7 +806,6 @@ static dma_addr_t iommu_dma_map_page(str
+       struct iommu_domain *domain = iommu_get_dma_domain(dev);
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
+-      size_t aligned_size = size;
+       dma_addr_t iova, dma_mask = dma_get_mask(dev);
+       /*
+@@ -815,7 +814,7 @@ static dma_addr_t iommu_dma_map_page(str
+        */
+       if (dev_use_swiotlb(dev) && iova_offset(iovad, phys | size)) {
+               void *padding_start;
+-              size_t padding_size;
++              size_t padding_size, aligned_size;
+               aligned_size = iova_align(iovad, size);
+               phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size,
+@@ -840,7 +839,7 @@ static dma_addr_t iommu_dma_map_page(str
+       if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+               arch_sync_dma_for_device(phys, size, dir);
+-      iova = __iommu_dma_map(dev, phys, aligned_size, prot, dma_mask);
++      iova = __iommu_dma_map(dev, phys, size, prot, dma_mask);
+       if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
+               swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
+       return iova;
diff --git a/queue-5.15/iommu-dma-check-config_swiotlb-more-broadly.patch b/queue-5.15/iommu-dma-check-config_swiotlb-more-broadly.patch
new file mode 100644 (file)
index 0000000..e51a8dd
--- /dev/null
@@ -0,0 +1,103 @@
+From 2e727bffbe93750a13d2414f3ce43de2f21600d2 Mon Sep 17 00:00:00 2001
+From: David Stevens <stevensd@chromium.org>
+Date: Wed, 29 Sep 2021 11:32:58 +0900
+Subject: iommu/dma: Check CONFIG_SWIOTLB more broadly
+
+From: David Stevens <stevensd@chromium.org>
+
+commit 2e727bffbe93750a13d2414f3ce43de2f21600d2 upstream.
+
+Introduce a new dev_use_swiotlb function to guard swiotlb code, instead
+of overloading dev_is_untrusted. This allows CONFIG_SWIOTLB to be
+checked more broadly, so the swiotlb related code can be removed more
+aggressively.
+
+Signed-off-by: David Stevens <stevensd@chromium.org>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20210929023300.335969-6-stevensd@google.com
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Cc: Mario Limonciello <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/dma-iommu.c |   20 ++++++++++++--------
+ 1 file changed, 12 insertions(+), 8 deletions(-)
+
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -317,6 +317,11 @@ static bool dev_is_untrusted(struct devi
+       return dev_is_pci(dev) && to_pci_dev(dev)->untrusted;
+ }
++static bool dev_use_swiotlb(struct device *dev)
++{
++      return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev);
++}
++
+ /* sysfs updates are serialised by the mutex of the group owning @domain */
+ int iommu_dma_init_fq(struct iommu_domain *domain)
+ {
+@@ -731,7 +736,7 @@ static void iommu_dma_sync_single_for_cp
+ {
+       phys_addr_t phys;
+-      if (dev_is_dma_coherent(dev) && !dev_is_untrusted(dev))
++      if (dev_is_dma_coherent(dev) && !dev_use_swiotlb(dev))
+               return;
+       phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle);
+@@ -747,7 +752,7 @@ static void iommu_dma_sync_single_for_de
+ {
+       phys_addr_t phys;
+-      if (dev_is_dma_coherent(dev) && !dev_is_untrusted(dev))
++      if (dev_is_dma_coherent(dev) && !dev_use_swiotlb(dev))
+               return;
+       phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle);
+@@ -765,7 +770,7 @@ static void iommu_dma_sync_sg_for_cpu(st
+       struct scatterlist *sg;
+       int i;
+-      if (dev_is_untrusted(dev))
++      if (dev_use_swiotlb(dev))
+               for_each_sg(sgl, sg, nelems, i)
+                       iommu_dma_sync_single_for_cpu(dev, sg_dma_address(sg),
+                                                     sg->length, dir);
+@@ -781,7 +786,7 @@ static void iommu_dma_sync_sg_for_device
+       struct scatterlist *sg;
+       int i;
+-      if (dev_is_untrusted(dev))
++      if (dev_use_swiotlb(dev))
+               for_each_sg(sgl, sg, nelems, i)
+                       iommu_dma_sync_single_for_device(dev,
+                                                        sg_dma_address(sg),
+@@ -808,8 +813,7 @@ static dma_addr_t iommu_dma_map_page(str
+        * If both the physical buffer start address and size are
+        * page aligned, we don't need to use a bounce page.
+        */
+-      if (IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev) &&
+-          iova_offset(iovad, phys | size)) {
++      if (dev_use_swiotlb(dev) && iova_offset(iovad, phys | size)) {
+               void *padding_start;
+               size_t padding_size;
+@@ -995,7 +999,7 @@ static int iommu_dma_map_sg(struct devic
+                       goto out;
+       }
+-      if (dev_is_untrusted(dev))
++      if (dev_use_swiotlb(dev))
+               return iommu_dma_map_sg_swiotlb(dev, sg, nents, dir, attrs);
+       if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+@@ -1073,7 +1077,7 @@ static void iommu_dma_unmap_sg(struct de
+       struct scatterlist *tmp;
+       int i;
+-      if (dev_is_untrusted(dev)) {
++      if (dev_use_swiotlb(dev)) {
+               iommu_dma_unmap_sg_swiotlb(dev, sg, nents, dir, attrs);
+               return;
+       }
diff --git a/queue-5.15/iommu-dma-fold-_swiotlb-helpers-into-callers.patch b/queue-5.15/iommu-dma-fold-_swiotlb-helpers-into-callers.patch
new file mode 100644 (file)
index 0000000..9577595
--- /dev/null
@@ -0,0 +1,202 @@
+From 9b49bbc2c4dfd0431bf7ff4e862171189cf94b7e Mon Sep 17 00:00:00 2001
+From: David Stevens <stevensd@chromium.org>
+Date: Wed, 29 Sep 2021 11:32:57 +0900
+Subject: iommu/dma: Fold _swiotlb helpers into callers
+
+From: David Stevens <stevensd@chromium.org>
+
+commit 9b49bbc2c4dfd0431bf7ff4e862171189cf94b7e upstream.
+
+Fold the _swiotlb helper functions into the respective _page functions,
+since recent fixes have moved all logic from the _page functions to the
+_swiotlb functions.
+
+Signed-off-by: David Stevens <stevensd@chromium.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Link: https://lore.kernel.org/r/20210929023300.335969-5-stevensd@google.com
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Cc: Mario Limonciello <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/dma-iommu.c |  135 ++++++++++++++++++++--------------------------
+ 1 file changed, 59 insertions(+), 76 deletions(-)
+
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -510,26 +510,6 @@ static void __iommu_dma_unmap(struct dev
+       iommu_dma_free_iova(cookie, dma_addr, size, &iotlb_gather);
+ }
+-static void __iommu_dma_unmap_swiotlb(struct device *dev, dma_addr_t dma_addr,
+-              size_t size, enum dma_data_direction dir,
+-              unsigned long attrs)
+-{
+-      struct iommu_domain *domain = iommu_get_dma_domain(dev);
+-      phys_addr_t phys;
+-
+-      phys = iommu_iova_to_phys(domain, dma_addr);
+-      if (WARN_ON(!phys))
+-              return;
+-
+-      if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev))
+-              arch_sync_dma_for_cpu(phys, size, dir);
+-
+-      __iommu_dma_unmap(dev, dma_addr, size);
+-
+-      if (unlikely(is_swiotlb_buffer(dev, phys)))
+-              swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
+-}
+-
+ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
+               size_t size, int prot, u64 dma_mask)
+ {
+@@ -556,55 +536,6 @@ static dma_addr_t __iommu_dma_map(struct
+       return iova + iova_off;
+ }
+-static dma_addr_t __iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys,
+-              size_t org_size, dma_addr_t dma_mask, bool coherent,
+-              enum dma_data_direction dir, unsigned long attrs)
+-{
+-      int prot = dma_info_to_prot(dir, coherent, attrs);
+-      struct iommu_domain *domain = iommu_get_dma_domain(dev);
+-      struct iommu_dma_cookie *cookie = domain->iova_cookie;
+-      struct iova_domain *iovad = &cookie->iovad;
+-      size_t aligned_size = org_size;
+-      void *padding_start;
+-      size_t padding_size;
+-      dma_addr_t iova;
+-
+-      /*
+-       * If both the physical buffer start address and size are
+-       * page aligned, we don't need to use a bounce page.
+-       */
+-      if (IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev) &&
+-          iova_offset(iovad, phys | org_size)) {
+-              aligned_size = iova_align(iovad, org_size);
+-              phys = swiotlb_tbl_map_single(dev, phys, org_size,
+-                                            aligned_size, dir, attrs);
+-
+-              if (phys == DMA_MAPPING_ERROR)
+-                      return DMA_MAPPING_ERROR;
+-
+-              /* Cleanup the padding area. */
+-              padding_start = phys_to_virt(phys);
+-              padding_size = aligned_size;
+-
+-              if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+-                  (dir == DMA_TO_DEVICE ||
+-                   dir == DMA_BIDIRECTIONAL)) {
+-                      padding_start += org_size;
+-                      padding_size -= org_size;
+-              }
+-
+-              memset(padding_start, 0, padding_size);
+-      }
+-
+-      if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+-              arch_sync_dma_for_device(phys, org_size, dir);
+-
+-      iova = __iommu_dma_map(dev, phys, aligned_size, prot, dma_mask);
+-      if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
+-              swiotlb_tbl_unmap_single(dev, phys, org_size, dir, attrs);
+-      return iova;
+-}
+-
+ static void __iommu_dma_free_pages(struct page **pages, int count)
+ {
+       while (count--)
+@@ -866,15 +797,68 @@ static dma_addr_t iommu_dma_map_page(str
+ {
+       phys_addr_t phys = page_to_phys(page) + offset;
+       bool coherent = dev_is_dma_coherent(dev);
++      int prot = dma_info_to_prot(dir, coherent, attrs);
++      struct iommu_domain *domain = iommu_get_dma_domain(dev);
++      struct iommu_dma_cookie *cookie = domain->iova_cookie;
++      struct iova_domain *iovad = &cookie->iovad;
++      size_t aligned_size = size;
++      dma_addr_t iova, dma_mask = dma_get_mask(dev);
++
++      /*
++       * If both the physical buffer start address and size are
++       * page aligned, we don't need to use a bounce page.
++       */
++      if (IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev) &&
++          iova_offset(iovad, phys | size)) {
++              void *padding_start;
++              size_t padding_size;
++
++              aligned_size = iova_align(iovad, size);
++              phys = swiotlb_tbl_map_single(dev, phys, size,
++                                            aligned_size, dir, attrs);
++
++              if (phys == DMA_MAPPING_ERROR)
++                      return DMA_MAPPING_ERROR;
+-      return __iommu_dma_map_swiotlb(dev, phys, size, dma_get_mask(dev),
+-                      coherent, dir, attrs);
++              /* Cleanup the padding area. */
++              padding_start = phys_to_virt(phys);
++              padding_size = aligned_size;
++
++              if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
++                  (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) {
++                      padding_start += size;
++                      padding_size -= size;
++              }
++
++              memset(padding_start, 0, padding_size);
++      }
++
++      if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
++              arch_sync_dma_for_device(phys, size, dir);
++
++      iova = __iommu_dma_map(dev, phys, aligned_size, prot, dma_mask);
++      if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
++              swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
++      return iova;
+ }
+ static void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
+               size_t size, enum dma_data_direction dir, unsigned long attrs)
+ {
+-      __iommu_dma_unmap_swiotlb(dev, dma_handle, size, dir, attrs);
++      struct iommu_domain *domain = iommu_get_dma_domain(dev);
++      phys_addr_t phys;
++
++      phys = iommu_iova_to_phys(domain, dma_handle);
++      if (WARN_ON(!phys))
++              return;
++
++      if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev))
++              arch_sync_dma_for_cpu(phys, size, dir);
++
++      __iommu_dma_unmap(dev, dma_handle, size);
++
++      if (unlikely(is_swiotlb_buffer(dev, phys)))
++              swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
+ }
+ /*
+@@ -959,7 +943,7 @@ static void iommu_dma_unmap_sg_swiotlb(s
+       int i;
+       for_each_sg(sg, s, nents, i)
+-              __iommu_dma_unmap_swiotlb(dev, sg_dma_address(s),
++              iommu_dma_unmap_page(dev, sg_dma_address(s),
+                               sg_dma_len(s), dir, attrs);
+ }
+@@ -970,9 +954,8 @@ static int iommu_dma_map_sg_swiotlb(stru
+       int i;
+       for_each_sg(sg, s, nents, i) {
+-              sg_dma_address(s) = __iommu_dma_map_swiotlb(dev, sg_phys(s),
+-                              s->length, dma_get_mask(dev),
+-                              dev_is_dma_coherent(dev), dir, attrs);
++              sg_dma_address(s) = iommu_dma_map_page(dev, sg_page(s),
++                              s->offset, s->length, dir, attrs);
+               if (sg_dma_address(s) == DMA_MAPPING_ERROR)
+                       goto out_unmap;
+               sg_dma_len(s) = s->length;
diff --git a/queue-5.15/iommu-dma-skip-extra-sync-during-unmap-w-swiotlb.patch b/queue-5.15/iommu-dma-skip-extra-sync-during-unmap-w-swiotlb.patch
new file mode 100644 (file)
index 0000000..f8f1e98
--- /dev/null
@@ -0,0 +1,65 @@
+From ee9d4097cc145dcaebedf6b113d17c91c21333a0 Mon Sep 17 00:00:00 2001
+From: David Stevens <stevensd@chromium.org>
+Date: Wed, 29 Sep 2021 11:32:56 +0900
+Subject: iommu/dma: Skip extra sync during unmap w/swiotlb
+
+From: David Stevens <stevensd@chromium.org>
+
+commit ee9d4097cc145dcaebedf6b113d17c91c21333a0 upstream.
+
+Calling the iommu_dma_sync_*_for_cpu functions during unmap can cause
+two copies out of the swiotlb buffer. Do the arch sync directly in
+__iommu_dma_unmap_swiotlb instead to avoid this. This makes the call to
+iommu_dma_sync_sg_for_cpu for untrusted devices in iommu_dma_unmap_sg no
+longer necessary, so move that invocation later in the function.
+
+Signed-off-by: David Stevens <stevensd@chromium.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Link: https://lore.kernel.org/r/20210929023300.335969-4-stevensd@google.com
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Cc: Mario Limonciello <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/dma-iommu.c |   11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -521,6 +521,9 @@ static void __iommu_dma_unmap_swiotlb(st
+       if (WARN_ON(!phys))
+               return;
++      if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev))
++              arch_sync_dma_for_cpu(phys, size, dir);
++
+       __iommu_dma_unmap(dev, dma_addr, size);
+       if (unlikely(is_swiotlb_buffer(dev, phys)))
+@@ -871,8 +874,6 @@ static dma_addr_t iommu_dma_map_page(str
+ static void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
+               size_t size, enum dma_data_direction dir, unsigned long attrs)
+ {
+-      if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+-              iommu_dma_sync_single_for_cpu(dev, dma_handle, size, dir);
+       __iommu_dma_unmap_swiotlb(dev, dma_handle, size, dir, attrs);
+ }
+@@ -1089,14 +1090,14 @@ static void iommu_dma_unmap_sg(struct de
+       struct scatterlist *tmp;
+       int i;
+-      if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+-              iommu_dma_sync_sg_for_cpu(dev, sg, nents, dir);
+-
+       if (dev_is_untrusted(dev)) {
+               iommu_dma_unmap_sg_swiotlb(dev, sg, nents, dir, attrs);
+               return;
+       }
++      if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
++              iommu_dma_sync_sg_for_cpu(dev, sg, nents, dir);
++
+       /*
+        * The scatterlist segments are mapped into a single
+        * contiguous IOVA allocation, so this is incredibly easy.
index 8ed0ec614da356147d85583fd11a8e24bfadc34a..761dbb74d8f47c7db466e0364b79603f1061581d 100644 (file)
@@ -902,3 +902,8 @@ n64cart-convert-bi_disk-to-bi_bdev-bd_disk-fix-build.patch
 mmc-rtsx-let-mmc-core-handle-runtime-pm.patch
 mmc-rtsx-fix-build-errors-warnings-for-unused-variable.patch
 kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch
+iommu-dma-skip-extra-sync-during-unmap-w-swiotlb.patch
+iommu-dma-fold-_swiotlb-helpers-into-callers.patch
+iommu-dma-check-config_swiotlb-more-broadly.patch
+swiotlb-support-aligned-swiotlb-buffers.patch
+iommu-dma-account-for-min_align_mask-w-swiotlb.patch
diff --git a/queue-5.15/swiotlb-support-aligned-swiotlb-buffers.patch b/queue-5.15/swiotlb-support-aligned-swiotlb-buffers.patch
new file mode 100644 (file)
index 0000000..073a0d2
--- /dev/null
@@ -0,0 +1,122 @@
+From e81e99bacc9f9347bda7808a949c1ce9fcc2bbf4 Mon Sep 17 00:00:00 2001
+From: David Stevens <stevensd@chromium.org>
+Date: Wed, 29 Sep 2021 11:32:59 +0900
+Subject: swiotlb: Support aligned swiotlb buffers
+
+From: David Stevens <stevensd@chromium.org>
+
+commit e81e99bacc9f9347bda7808a949c1ce9fcc2bbf4 upstream.
+
+Add an argument to swiotlb_tbl_map_single that specifies the desired
+alignment of the allocated buffer. This is used by dma-iommu to ensure
+the buffer is aligned to the iova granule size when using swiotlb with
+untrusted sub-granule mappings. This addresses an issue where adjacent
+slots could be exposed to the untrusted device if IO_TLB_SIZE < iova
+granule < PAGE_SIZE.
+
+Signed-off-by: David Stevens <stevensd@chromium.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20210929023300.335969-7-stevensd@google.com
+Signed-off-by: Joerg Roedel <jroedel@suse.de>
+Cc: Mario Limonciello <Mario.Limonciello@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/dma-iommu.c |    4 ++--
+ drivers/xen/swiotlb-xen.c |    2 +-
+ include/linux/swiotlb.h   |    3 ++-
+ kernel/dma/swiotlb.c      |   13 ++++++++-----
+ 4 files changed, 13 insertions(+), 9 deletions(-)
+
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -818,8 +818,8 @@ static dma_addr_t iommu_dma_map_page(str
+               size_t padding_size;
+               aligned_size = iova_align(iovad, size);
+-              phys = swiotlb_tbl_map_single(dev, phys, size,
+-                                            aligned_size, dir, attrs);
++              phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size,
++                                            iova_mask(iovad), dir, attrs);
+               if (phys == DMA_MAPPING_ERROR)
+                       return DMA_MAPPING_ERROR;
+--- a/drivers/xen/swiotlb-xen.c
++++ b/drivers/xen/swiotlb-xen.c
+@@ -380,7 +380,7 @@ static dma_addr_t xen_swiotlb_map_page(s
+        */
+       trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
+-      map = swiotlb_tbl_map_single(dev, phys, size, size, dir, attrs);
++      map = swiotlb_tbl_map_single(dev, phys, size, size, 0, dir, attrs);
+       if (map == (phys_addr_t)DMA_MAPPING_ERROR)
+               return DMA_MAPPING_ERROR;
+--- a/include/linux/swiotlb.h
++++ b/include/linux/swiotlb.h
+@@ -45,7 +45,8 @@ extern void __init swiotlb_update_mem_at
+ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev, phys_addr_t phys,
+               size_t mapping_size, size_t alloc_size,
+-              enum dma_data_direction dir, unsigned long attrs);
++              unsigned int alloc_aligned_mask, enum dma_data_direction dir,
++              unsigned long attrs);
+ extern void swiotlb_tbl_unmap_single(struct device *hwdev,
+                                    phys_addr_t tlb_addr,
+--- a/kernel/dma/swiotlb.c
++++ b/kernel/dma/swiotlb.c
+@@ -459,7 +459,7 @@ static unsigned int wrap_index(struct io
+  * allocate a buffer from that IO TLB pool.
+  */
+ static int swiotlb_find_slots(struct device *dev, phys_addr_t orig_addr,
+-                            size_t alloc_size)
++                            size_t alloc_size, unsigned int alloc_align_mask)
+ {
+       struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+       unsigned long boundary_mask = dma_get_seg_boundary(dev);
+@@ -483,6 +483,7 @@ static int swiotlb_find_slots(struct dev
+       stride = (iotlb_align_mask >> IO_TLB_SHIFT) + 1;
+       if (alloc_size >= PAGE_SIZE)
+               stride = max(stride, stride << (PAGE_SHIFT - IO_TLB_SHIFT));
++      stride = max(stride, (alloc_align_mask >> IO_TLB_SHIFT) + 1);
+       spin_lock_irqsave(&mem->lock, flags);
+       if (unlikely(nslots > mem->nslabs - mem->used))
+@@ -541,7 +542,8 @@ found:
+ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
+               size_t mapping_size, size_t alloc_size,
+-              enum dma_data_direction dir, unsigned long attrs)
++              unsigned int alloc_align_mask, enum dma_data_direction dir,
++              unsigned long attrs)
+ {
+       struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+       unsigned int offset = swiotlb_align_offset(dev, orig_addr);
+@@ -561,7 +563,8 @@ phys_addr_t swiotlb_tbl_map_single(struc
+               return (phys_addr_t)DMA_MAPPING_ERROR;
+       }
+-      index = swiotlb_find_slots(dev, orig_addr, alloc_size + offset);
++      index = swiotlb_find_slots(dev, orig_addr,
++                                 alloc_size + offset, alloc_align_mask);
+       if (index == -1) {
+               if (!(attrs & DMA_ATTR_NO_WARN))
+                       dev_warn_ratelimited(dev,
+@@ -680,7 +683,7 @@ dma_addr_t swiotlb_map(struct device *de
+       trace_swiotlb_bounced(dev, phys_to_dma(dev, paddr), size,
+                             swiotlb_force);
+-      swiotlb_addr = swiotlb_tbl_map_single(dev, paddr, size, size, dir,
++      swiotlb_addr = swiotlb_tbl_map_single(dev, paddr, size, size, 0, dir,
+                       attrs);
+       if (swiotlb_addr == (phys_addr_t)DMA_MAPPING_ERROR)
+               return DMA_MAPPING_ERROR;
+@@ -764,7 +767,7 @@ struct page *swiotlb_alloc(struct device
+       if (!mem)
+               return NULL;
+-      index = swiotlb_find_slots(dev, 0, size);
++      index = swiotlb_find_slots(dev, 0, size, 0);
+       if (index == -1)
+               return NULL;