[RFC 1/2] iommu/dma: Restrict IOVAs to physical memory layout

Robin Murphy robin.murphy at arm.com
Tue Jun 28 09:18:57 PDT 2016


Certain peripherals may be bestowed with knowledge of the physical
memory map of the system in which they live, and refuse to handle
addresses that they do not think are memory, which causes issues when
remapping to arbitrary IOVAs. Sidestep the issue by restricting IOVA
domains to only allocate addresses within ranges which match the
physical memory layout.

Signed-off-by: Robin Murphy <robin.murphy at arm.com>
---

Posting this as an RFC because it's something I've been having to use
on Juno for all the PCI IOMMU development - it's pretty horrible, but I
can't easily think of a nicer solution...

 drivers/iommu/dma-iommu.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index ea5a9ebf0f78..2385fab382d8 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -25,6 +25,7 @@
 #include <linux/huge_mm.h>
 #include <linux/iommu.h>
 #include <linux/iova.h>
+#include <linux/memblock.h>
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
@@ -74,6 +75,23 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL(iommu_put_dma_cookie);
 
+static void iova_match_mem(struct iova_domain *iovad)
+{
+	struct memblock_region *reg;
+	unsigned long blk, base = iovad->start_pfn;
+	unsigned long shift = iova_shift(iovad);
+
+	for_each_memblock(memory, reg) {
+		blk = (reg->base + iova_mask(iovad)) >> shift;
+		if (blk > base)
+			reserve_iova(iovad, base, blk - 1);
+		base = (reg->base + reg->size) >> shift;
+	}
+	blk = -1UL >> shift;
+	if (blk > base)
+		reserve_iova(iovad, base, blk);
+}
+
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
@@ -123,6 +141,7 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
 		iovad->dma_32bit_pfn = end_pfn;
 	} else {
 		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
+		iova_match_mem(iovad);
 	}
 	return 0;
 }
-- 
2.8.1.dirty




More information about the linux-arm-kernel mailing list