iommu/io-pgtable-arm: Prepare for TTBR1 usage
authorRobin Murphy <robin.murphy@arm.com>
Fri, 25 Oct 2019 18:08:39 +0000 (19:08 +0100)
committerWill Deacon <will@kernel.org>
Fri, 10 Jan 2020 15:52:25 +0000 (15:52 +0000)
Now that we can correctly extract top-level indices without relying on
the remaining upper bits being zero, the only remaining impediments to
using a given table for TTBR1 are the address validation on map/unmap
and the awkward TCR translation granule format. Add a quirk so that we
can do the right thing at those points.

Tested-by: Jordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/io-pgtable-arm.c
include/linux/io-pgtable.h

index 2ae4da4..983b084 100644 (file)
 #define ARM_LPAE_TCR_TG0_64K           1
 #define ARM_LPAE_TCR_TG0_16K           2
 
+#define ARM_LPAE_TCR_TG1_16K           1
+#define ARM_LPAE_TCR_TG1_4K            2
+#define ARM_LPAE_TCR_TG1_64K           3
+
 #define ARM_LPAE_TCR_SH_NS             0
 #define ARM_LPAE_TCR_SH_OS             2
 #define ARM_LPAE_TCR_SH_IS             3
@@ -464,6 +468,7 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
        arm_lpae_iopte *ptep = data->pgd;
        int ret, lvl = data->start_level;
        arm_lpae_iopte prot;
+       long iaext = (long)iova >> cfg->ias;
 
        /* If no access, then nothing to do */
        if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
@@ -472,7 +477,9 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
        if (WARN_ON(!size || (size & cfg->pgsize_bitmap) != size))
                return -EINVAL;
 
-       if (WARN_ON(iova >> data->iop.cfg.ias || paddr >> data->iop.cfg.oas))
+       if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)
+               iaext = ~iaext;
+       if (WARN_ON(iaext || paddr >> cfg->oas))
                return -ERANGE;
 
        prot = arm_lpae_prot_to_pte(data, iommu_prot);
@@ -638,11 +645,14 @@ static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
        struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
        struct io_pgtable_cfg *cfg = &data->iop.cfg;
        arm_lpae_iopte *ptep = data->pgd;
+       long iaext = (long)iova >> cfg->ias;
 
        if (WARN_ON(!size || (size & cfg->pgsize_bitmap) != size))
                return 0;
 
-       if (WARN_ON(iova >> data->iop.cfg.ias))
+       if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)
+               iaext = ~iaext;
+       if (WARN_ON(iaext))
                return 0;
 
        return __arm_lpae_unmap(data, gather, iova, size, data->start_level, ptep);
@@ -778,9 +788,11 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
        u64 reg;
        struct arm_lpae_io_pgtable *data;
        typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
+       bool tg1;
 
        if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS |
-                           IO_PGTABLE_QUIRK_NON_STRICT))
+                           IO_PGTABLE_QUIRK_NON_STRICT |
+                           IO_PGTABLE_QUIRK_ARM_TTBR1))
                return NULL;
 
        data = arm_lpae_alloc_pgtable(cfg);
@@ -798,15 +810,16 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
                tcr->orgn = ARM_LPAE_TCR_RGN_NC;
        }
 
+       tg1 = cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1;
        switch (ARM_LPAE_GRANULE(data)) {
        case SZ_4K:
-               tcr->tg = ARM_LPAE_TCR_TG0_4K;
+               tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_4K : ARM_LPAE_TCR_TG0_4K;
                break;
        case SZ_16K:
-               tcr->tg = ARM_LPAE_TCR_TG0_16K;
+               tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_16K : ARM_LPAE_TCR_TG0_16K;
                break;
        case SZ_64K:
-               tcr->tg = ARM_LPAE_TCR_TG0_64K;
+               tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_64K : ARM_LPAE_TCR_TG0_64K;
                break;
        }
 
index 40c1b77..53d53c6 100644 (file)
@@ -83,12 +83,16 @@ struct io_pgtable_cfg {
         * IO_PGTABLE_QUIRK_NON_STRICT: Skip issuing synchronous leaf TLBIs
         *      on unmap, for DMA domains using the flush queue mechanism for
         *      delayed invalidation.
+        *
+        * IO_PGTABLE_QUIRK_ARM_TTBR1: (ARM LPAE format) Configure the table
+        *      for use in the upper half of a split address space.
         */
        #define IO_PGTABLE_QUIRK_ARM_NS         BIT(0)
        #define IO_PGTABLE_QUIRK_NO_PERMS       BIT(1)
        #define IO_PGTABLE_QUIRK_TLBI_ON_MAP    BIT(2)
        #define IO_PGTABLE_QUIRK_ARM_MTK_EXT    BIT(3)
        #define IO_PGTABLE_QUIRK_NON_STRICT     BIT(4)
+       #define IO_PGTABLE_QUIRK_ARM_TTBR1      BIT(5)
        unsigned long                   quirks;
        unsigned long                   pgsize_bitmap;
        unsigned int                    ias;