]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/iommu/io-pgtable-arm.c
Merge tag 'mfd-fixes-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
[karo-tx-linux.git] / drivers / iommu / io-pgtable-arm.c
index 06176872fb787ed78265343de370564d4b48bb4e..7df97777662d4d8a9284a8f2cae4cc0e8891210a 100644 (file)
@@ -202,9 +202,9 @@ typedef u64 arm_lpae_iopte;
 
 static bool selftest_running = false;
 
-static dma_addr_t __arm_lpae_dma_addr(struct device *dev, void *pages)
+static dma_addr_t __arm_lpae_dma_addr(void *pages)
 {
-       return phys_to_dma(dev, virt_to_phys(pages));
+       return (dma_addr_t)virt_to_phys(pages);
 }
 
 static void *__arm_lpae_alloc_pages(size_t size, gfp_t gfp,
@@ -223,10 +223,10 @@ static void *__arm_lpae_alloc_pages(size_t size, gfp_t gfp,
                        goto out_free;
                /*
                 * We depend on the IOMMU being able to work with any physical
-                * address directly, so if the DMA layer suggests it can't by
-                * giving us back some translation, that bodes very badly...
+                * address directly, so if the DMA layer suggests otherwise by
+                * translating or truncating them, that bodes very badly...
                 */
-               if (dma != __arm_lpae_dma_addr(dev, pages))
+               if (dma != virt_to_phys(pages))
                        goto out_unmap;
        }
 
@@ -243,10 +243,8 @@ out_free:
 static void __arm_lpae_free_pages(void *pages, size_t size,
                                  struct io_pgtable_cfg *cfg)
 {
-       struct device *dev = cfg->iommu_dev;
-
        if (!selftest_running)
-               dma_unmap_single(dev, __arm_lpae_dma_addr(dev, pages),
+               dma_unmap_single(cfg->iommu_dev, __arm_lpae_dma_addr(pages),
                                 size, DMA_TO_DEVICE);
        free_pages_exact(pages, size);
 }
@@ -254,15 +252,18 @@ static void __arm_lpae_free_pages(void *pages, size_t size,
 static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte,
                               struct io_pgtable_cfg *cfg)
 {
-       struct device *dev = cfg->iommu_dev;
-
        *ptep = pte;
 
        if (!selftest_running)
-               dma_sync_single_for_device(dev, __arm_lpae_dma_addr(dev, ptep),
+               dma_sync_single_for_device(cfg->iommu_dev,
+                                          __arm_lpae_dma_addr(ptep),
                                           sizeof(pte), DMA_TO_DEVICE);
 }
 
+static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
+                           unsigned long iova, size_t size, int lvl,
+                           arm_lpae_iopte *ptep);
+
 static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
                             unsigned long iova, phys_addr_t paddr,
                             arm_lpae_iopte prot, int lvl,
@@ -271,10 +272,21 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
        arm_lpae_iopte pte = prot;
        struct io_pgtable_cfg *cfg = &data->iop.cfg;
 
-       /* We require an unmap first */
        if (iopte_leaf(*ptep, lvl)) {
+               /* We require an unmap first */
                WARN_ON(!selftest_running);
                return -EEXIST;
+       } else if (iopte_type(*ptep, lvl) == ARM_LPAE_PTE_TYPE_TABLE) {
+               /*
+                * We need to unmap and free the old table before
+                * overwriting it with a block entry.
+                */
+               arm_lpae_iopte *tblp;
+               size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
+
+               tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data);
+               if (WARN_ON(__arm_lpae_unmap(data, iova, sz, lvl, tblp) != sz))
+                       return -EINVAL;
        }
 
        if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
@@ -614,6 +626,11 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
        if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS)
                return NULL;
 
+       if (!selftest_running && cfg->iommu_dev->dma_pfn_offset) {
+               dev_err(cfg->iommu_dev, "Cannot accommodate DMA offset for IOMMU page tables\n");
+               return NULL;
+       }
+
        data = kmalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return NULL;
@@ -884,16 +901,10 @@ static void dummy_tlb_sync(void *cookie)
        WARN_ON(cookie != cfg_cookie);
 }
 
-static void dummy_flush_pgtable(void *ptr, size_t size, void *cookie)
-{
-       WARN_ON(cookie != cfg_cookie);
-}
-
 static struct iommu_gather_ops dummy_tlb_ops __initdata = {
        .tlb_flush_all  = dummy_tlb_flush_all,
        .tlb_add_flush  = dummy_tlb_add_flush,
        .tlb_sync       = dummy_tlb_sync,
-       .flush_pgtable  = dummy_flush_pgtable,
 };
 
 static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops)