From: Sven Peter <sven@xxxxxxxxxxxxx> The DARTs present in the M1 Pro/Max/Ultra SoC use a diffent PTE format. They support a 42bit physical address space by shifting the paddr and extending its mask inside the PTE. PTE flags are incompatible with iopte_type() since BIT(1) in the PTE tags "uncached" mappings. They also come with mandatory sub-page protection now which we just configure to always allow access to the entire page. This feature is already present but optional on the previous DARTs which allows to unconditionally configure it. Signed-off-by: Sven Peter <sven@xxxxxxxxxxxxx> Co-developed-by: Janne Grunau <j@xxxxxxxxxx> Signed-off-by: Janne Grunau <j@xxxxxxxxxx> --- Changes since v1: - add APPLE_DART2 PTE format --- drivers/iommu/io-pgtable-arm.c | 68 ++++++++++++++++++++++++++++++++-- drivers/iommu/io-pgtable.c | 1 + include/linux/io-pgtable.h | 3 ++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 71570bbc9096..f9121251fb57 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -134,9 +134,20 @@ #define APPLE_DART_PTE_PROT_NO_WRITE (1<<7) #define APPLE_DART_PTE_PROT_NO_READ (1<<8) +#define APPLE_DART_PTE_VALID BIT(0) // identical to ARM_LPAE_PTE_TYPE_BLOCK + +#define APPLE_DART2_PTE_PROT_NO_CACHE BIT(1) +#define APPLE_DART2_PTE_PROT_NO_WRITE BIT(2) +#define APPLE_DART2_PTE_PROT_NO_READ BIT(3) + #define APPLE_DART_PTE_SUBPAGE_START GENMASK_ULL(63, 52) #define APPLE_DART_PTE_SUBPAGE_END GENMASK_ULL(51, 40) +#define APPLE_DART_PADDR_MASK_PS_36BIT GENMASK_ULL(35, 12) +#define APPLE_DART_PADDR_SHIFT_PS_36BIT (0) +#define APPLE_DART_PADDR_MASK_PS_42BIT GENMASK_ULL(37, 10) +#define APPLE_DART_PADDR_SHIFT_PS_42BIT (4) + /* IOPTE accessors */ #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d)) @@ -160,6 +171,10 @@ typedef u64 arm_lpae_iopte; static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl, enum io_pgtable_fmt fmt) { + if (fmt == APPLE_DART2) + return lvl == (ARM_LPAE_MAX_LEVELS - 1) && + FIELD_GET(APPLE_DART_PTE_VALID, pte); + if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE) return iopte_type(pte) == ARM_LPAE_PTE_TYPE_PAGE; @@ -171,6 +186,13 @@ static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr, { arm_lpae_iopte pte = paddr; + if (data->iop.fmt == APPLE_DART || data->iop.fmt == APPLE_DART2) { + pte = paddr >> data->iop.cfg.apple_dart_cfg.paddr_shift; + pte &= data->iop.cfg.apple_dart_cfg.paddr_mask; + + return pte; + } + /* Of the bits which overlap, either 51:48 or 15:12 are always RES0 */ return (pte | (pte >> (48 - 12))) & ARM_LPAE_PTE_ADDR_MASK; } @@ -180,6 +202,12 @@ static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte, { u64 paddr = pte & ARM_LPAE_PTE_ADDR_MASK; + if (data->iop.fmt == APPLE_DART || data->iop.fmt == APPLE_DART2) { + paddr = pte & data->iop.cfg.apple_dart_cfg.paddr_mask; + paddr <<= data->iop.cfg.apple_dart_cfg.paddr_shift; + return paddr; + } + if (ARM_LPAE_GRANULE(data) < SZ_64K) return paddr; @@ -272,12 +300,13 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data); int i; - if (data->iop.fmt != ARM_MALI_LPAE && lvl == ARM_LPAE_MAX_LEVELS - 1) + if ((data->iop.fmt != ARM_MALI_LPAE && data->iop.fmt != APPLE_DART2) && + lvl == ARM_LPAE_MAX_LEVELS - 1) pte |= ARM_LPAE_PTE_TYPE_PAGE; else pte |= ARM_LPAE_PTE_TYPE_BLOCK; - if (data->iop.fmt == APPLE_DART) { + if (data->iop.fmt == APPLE_DART || data->iop.fmt == APPLE_DART2) { /* subpage protection: always allow access to the entire page */ pte |= FIELD_PREP(APPLE_DART_PTE_SUBPAGE_START, 0); pte |= FIELD_PREP(APPLE_DART_PTE_SUBPAGE_END, 0xfff); @@ -330,7 +359,18 @@ static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table, arm_lpae_iopte old, new; struct io_pgtable_cfg *cfg = &data->iop.cfg; - new = paddr_to_iopte(__pa(table), data) | ARM_LPAE_PTE_TYPE_TABLE; + new = paddr_to_iopte(__pa(table), data); + /* + * The APPLE_DART2 PTE format is incompatible with ARM_LPAE_PTE_TYPE_* + * since BIT(1) is used to tag "uncached" mappings. + * This is the only place where ARM_LPAE_PTE_TYPE_TABLE has to be + * taken into account since APPLE_DART2 supports only a single block + * size. + */ + if (data->iop.fmt == APPLE_DART2) + new |= APPLE_DART_PTE_VALID; + else + new |= ARM_LPAE_PTE_TYPE_TABLE; if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS) new |= ARM_LPAE_PTE_NSTABLE; @@ -424,6 +464,16 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, pte |= APPLE_DART_PTE_PROT_NO_READ; return pte; } + if (data->iop.fmt == APPLE_DART2) { + pte = 0; + if (!(prot & IOMMU_WRITE)) + pte |= APPLE_DART2_PTE_PROT_NO_WRITE; + if (!(prot & IOMMU_READ)) + pte |= APPLE_DART2_PTE_PROT_NO_READ; + if (!(prot & IOMMU_CACHE)) + pte |= APPLE_DART2_PTE_PROT_NO_CACHE; + return pte; + } if (data->iop.fmt == ARM_64_LPAE_S1 || data->iop.fmt == ARM_32_LPAE_S1) { @@ -1123,8 +1173,18 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) struct arm_lpae_io_pgtable *data; int i; - if (cfg->oas > 36) + switch (cfg->oas) { + case 36: + cfg->apple_dart_cfg.paddr_shift = APPLE_DART_PADDR_SHIFT_PS_36BIT; + cfg->apple_dart_cfg.paddr_mask = APPLE_DART_PADDR_MASK_PS_36BIT; + break; + case 42: + cfg->apple_dart_cfg.paddr_shift = APPLE_DART_PADDR_SHIFT_PS_42BIT; + cfg->apple_dart_cfg.paddr_mask = APPLE_DART_PADDR_MASK_PS_42BIT; + break; + default: return NULL; + } data = arm_lpae_alloc_pgtable(cfg); if (!data) diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c index f4bfcef98297..a217d2e85520 100644 --- a/drivers/iommu/io-pgtable.c +++ b/drivers/iommu/io-pgtable.c @@ -21,6 +21,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = { [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, [ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns, [APPLE_DART] = &io_pgtable_apple_dart_init_fns, + [APPLE_DART2] = &io_pgtable_apple_dart_init_fns, #endif #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S [ARM_V7S] = &io_pgtable_arm_v7s_init_fns, diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h index 86af6f0a00a2..36f251dc6f6b 100644 --- a/include/linux/io-pgtable.h +++ b/include/linux/io-pgtable.h @@ -17,6 +17,7 @@ enum io_pgtable_fmt { ARM_MALI_LPAE, AMD_IOMMU_V1, APPLE_DART, + APPLE_DART2, IO_PGTABLE_NUM_FMTS, }; @@ -136,6 +137,8 @@ struct io_pgtable_cfg { struct { u64 ttbr[4]; u32 n_ttbrs; + u32 paddr_shift; + u64 paddr_mask; } apple_dart_cfg; }; }; -- 2.35.1