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 <[email protected]>
---

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

_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to