diff options
| author | Rob Herring <robh@kernel.org> | 2019-02-21 15:23:25 -0500 |
|---|---|---|
| committer | Rob Herring <robh@kernel.org> | 2019-04-12 13:52:38 -0400 |
| commit | d08d42de6432d5064045159aed060e3db9fa7807 (patch) | |
| tree | 2019c00522f05dfd8df93440401a2042cd085da0 | |
| parent | 0eb2766dd6f366d42448121c383420bb0307bcc7 (diff) | |
iommu: io-pgtable: Add ARM Mali midgard MMU page table format
ARM Mali midgard GPU is similar to standard 64-bit stage 1 page tables, but
have a few differences. Add a new format type to represent the format. The
input address size is 48-bits and the output address size is 40-bits (and
possibly less?). Note that the later bifrost GPUs follow the standard
64-bit stage 1 format.
The differences in the format compared to 64-bit stage 1 format are:
The 3rd level page entry bits are 0x1 instead of 0x3 for page entries.
The access flags are not read-only and unprivileged, but read and write.
This is similar to stage 2 entries, but the memory attributes field matches
stage 1 being an index.
The nG bit is not set by the vendor driver. This one didn't seem to matter,
but we'll keep it aligned to the vendor driver.
Cc: Will Deacon <will.deacon@arm.com>
Acked-by: Robin Murphy <robin.murphy@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: iommu@lists.linux-foundation.org
Acked-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Acked-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Rob Herring <robh@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20190409205427.6943-2-robh@kernel.org
| -rw-r--r-- | drivers/iommu/io-pgtable-arm.c | 91 | ||||
| -rw-r--r-- | drivers/iommu/io-pgtable.c | 1 | ||||
| -rw-r--r-- | include/linux/io-pgtable.h | 7 |
3 files changed, 77 insertions, 22 deletions
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index d3700ec15cbd..4e21efbc4459 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c | |||
| @@ -172,6 +172,10 @@ | |||
| 172 | #define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 | 172 | #define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 |
| 173 | #define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 | 173 | #define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 |
| 174 | 174 | ||
| 175 | #define ARM_MALI_LPAE_TTBR_ADRMODE_TABLE (3u << 0) | ||
| 176 | #define ARM_MALI_LPAE_TTBR_READ_INNER BIT(2) | ||
| 177 | #define ARM_MALI_LPAE_TTBR_SHARE_OUTER BIT(4) | ||
| 178 | |||
| 175 | /* IOPTE accessors */ | 179 | /* IOPTE accessors */ |
| 176 | #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d)) | 180 | #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d)) |
| 177 | 181 | ||
| @@ -180,11 +184,6 @@ | |||
| 180 | 184 | ||
| 181 | #define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) | 185 | #define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) |
| 182 | 186 | ||
| 183 | #define iopte_leaf(pte,l) \ | ||
| 184 | (l == (ARM_LPAE_MAX_LEVELS - 1) ? \ | ||
| 185 | (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \ | ||
| 186 | (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK)) | ||
| 187 | |||
| 188 | struct arm_lpae_io_pgtable { | 187 | struct arm_lpae_io_pgtable { |
| 189 | struct io_pgtable iop; | 188 | struct io_pgtable iop; |
| 190 | 189 | ||
| @@ -198,6 +197,15 @@ struct arm_lpae_io_pgtable { | |||
| 198 | 197 | ||
| 199 | typedef u64 arm_lpae_iopte; | 198 | typedef u64 arm_lpae_iopte; |
| 200 | 199 | ||
| 200 | static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl, | ||
| 201 | enum io_pgtable_fmt fmt) | ||
| 202 | { | ||
| 203 | if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE) | ||
| 204 | return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_PAGE; | ||
| 205 | |||
| 206 | return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_BLOCK; | ||
| 207 | } | ||
| 208 | |||
| 201 | static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr, | 209 | static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr, |
| 202 | struct arm_lpae_io_pgtable *data) | 210 | struct arm_lpae_io_pgtable *data) |
| 203 | { | 211 | { |
| @@ -303,12 +311,14 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, | |||
| 303 | if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) | 311 | if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) |
| 304 | pte |= ARM_LPAE_PTE_NS; | 312 | pte |= ARM_LPAE_PTE_NS; |
| 305 | 313 | ||
| 306 | if (lvl == ARM_LPAE_MAX_LEVELS - 1) | 314 | if (data->iop.fmt != ARM_MALI_LPAE && lvl == ARM_LPAE_MAX_LEVELS - 1) |
| 307 | pte |= ARM_LPAE_PTE_TYPE_PAGE; | 315 | pte |= ARM_LPAE_PTE_TYPE_PAGE; |
| 308 | else | 316 | else |
| 309 | pte |= ARM_LPAE_PTE_TYPE_BLOCK; | 317 | pte |= ARM_LPAE_PTE_TYPE_BLOCK; |
| 310 | 318 | ||
| 311 | pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS; | 319 | if (data->iop.fmt != ARM_MALI_LPAE) |
| 320 | pte |= ARM_LPAE_PTE_AF; | ||
| 321 | pte |= ARM_LPAE_PTE_SH_IS; | ||
| 312 | pte |= paddr_to_iopte(paddr, data); | 322 | pte |= paddr_to_iopte(paddr, data); |
| 313 | 323 | ||
| 314 | __arm_lpae_set_pte(ptep, pte, &data->iop.cfg); | 324 | __arm_lpae_set_pte(ptep, pte, &data->iop.cfg); |
| @@ -321,7 +331,7 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, | |||
| 321 | { | 331 | { |
| 322 | arm_lpae_iopte pte = *ptep; | 332 | arm_lpae_iopte pte = *ptep; |
| 323 | 333 | ||
| 324 | if (iopte_leaf(pte, lvl)) { | 334 | if (iopte_leaf(pte, lvl, data->iop.fmt)) { |
| 325 | /* We require an unmap first */ | 335 | /* We require an unmap first */ |
| 326 | WARN_ON(!selftest_running); | 336 | WARN_ON(!selftest_running); |
| 327 | return -EEXIST; | 337 | return -EEXIST; |
| @@ -409,7 +419,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, | |||
| 409 | __arm_lpae_sync_pte(ptep, cfg); | 419 | __arm_lpae_sync_pte(ptep, cfg); |
| 410 | } | 420 | } |
| 411 | 421 | ||
| 412 | if (pte && !iopte_leaf(pte, lvl)) { | 422 | if (pte && !iopte_leaf(pte, lvl, data->iop.fmt)) { |
| 413 | cptep = iopte_deref(pte, data); | 423 | cptep = iopte_deref(pte, data); |
| 414 | } else if (pte) { | 424 | } else if (pte) { |
| 415 | /* We require an unmap first */ | 425 | /* We require an unmap first */ |
| @@ -429,31 +439,37 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, | |||
| 429 | if (data->iop.fmt == ARM_64_LPAE_S1 || | 439 | if (data->iop.fmt == ARM_64_LPAE_S1 || |
| 430 | data->iop.fmt == ARM_32_LPAE_S1) { | 440 | data->iop.fmt == ARM_32_LPAE_S1) { |
| 431 | pte = ARM_LPAE_PTE_nG; | 441 | pte = ARM_LPAE_PTE_nG; |
| 432 | |||
| 433 | if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) | 442 | if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) |
| 434 | pte |= ARM_LPAE_PTE_AP_RDONLY; | 443 | pte |= ARM_LPAE_PTE_AP_RDONLY; |
| 435 | |||
| 436 | if (!(prot & IOMMU_PRIV)) | 444 | if (!(prot & IOMMU_PRIV)) |
| 437 | pte |= ARM_LPAE_PTE_AP_UNPRIV; | 445 | pte |= ARM_LPAE_PTE_AP_UNPRIV; |
| 438 | |||
| 439 | if (prot & IOMMU_MMIO) | ||
| 440 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV | ||
| 441 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 442 | else if (prot & IOMMU_CACHE) | ||
| 443 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE | ||
| 444 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 445 | } else { | 446 | } else { |
| 446 | pte = ARM_LPAE_PTE_HAP_FAULT; | 447 | pte = ARM_LPAE_PTE_HAP_FAULT; |
| 447 | if (prot & IOMMU_READ) | 448 | if (prot & IOMMU_READ) |
| 448 | pte |= ARM_LPAE_PTE_HAP_READ; | 449 | pte |= ARM_LPAE_PTE_HAP_READ; |
| 449 | if (prot & IOMMU_WRITE) | 450 | if (prot & IOMMU_WRITE) |
| 450 | pte |= ARM_LPAE_PTE_HAP_WRITE; | 451 | pte |= ARM_LPAE_PTE_HAP_WRITE; |
| 452 | } | ||
| 453 | |||
| 454 | /* | ||
| 455 | * Note that this logic is structured to accommodate Mali LPAE | ||
| 456 | * having stage-1-like attributes but stage-2-like permissions. | ||
| 457 | */ | ||
| 458 | if (data->iop.fmt == ARM_64_LPAE_S2 || | ||
| 459 | data->iop.fmt == ARM_32_LPAE_S2) { | ||
| 451 | if (prot & IOMMU_MMIO) | 460 | if (prot & IOMMU_MMIO) |
| 452 | pte |= ARM_LPAE_PTE_MEMATTR_DEV; | 461 | pte |= ARM_LPAE_PTE_MEMATTR_DEV; |
| 453 | else if (prot & IOMMU_CACHE) | 462 | else if (prot & IOMMU_CACHE) |
| 454 | pte |= ARM_LPAE_PTE_MEMATTR_OIWB; | 463 | pte |= ARM_LPAE_PTE_MEMATTR_OIWB; |
| 455 | else | 464 | else |
| 456 | pte |= ARM_LPAE_PTE_MEMATTR_NC; | 465 | pte |= ARM_LPAE_PTE_MEMATTR_NC; |
| 466 | } else { | ||
| 467 | if (prot & IOMMU_MMIO) | ||
| 468 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV | ||
| 469 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 470 | else if (prot & IOMMU_CACHE) | ||
| 471 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE | ||
| 472 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 457 | } | 473 | } |
| 458 | 474 | ||
| 459 | if (prot & IOMMU_NOEXEC) | 475 | if (prot & IOMMU_NOEXEC) |
| @@ -511,7 +527,7 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, | |||
| 511 | while (ptep != end) { | 527 | while (ptep != end) { |
| 512 | arm_lpae_iopte pte = *ptep++; | 528 | arm_lpae_iopte pte = *ptep++; |
| 513 | 529 | ||
| 514 | if (!pte || iopte_leaf(pte, lvl)) | 530 | if (!pte || iopte_leaf(pte, lvl, data->iop.fmt)) |
| 515 | continue; | 531 | continue; |
| 516 | 532 | ||
| 517 | __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); | 533 | __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); |
| @@ -602,7 +618,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, | |||
| 602 | if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) { | 618 | if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) { |
| 603 | __arm_lpae_set_pte(ptep, 0, &iop->cfg); | 619 | __arm_lpae_set_pte(ptep, 0, &iop->cfg); |
| 604 | 620 | ||
| 605 | if (!iopte_leaf(pte, lvl)) { | 621 | if (!iopte_leaf(pte, lvl, iop->fmt)) { |
| 606 | /* Also flush any partial walks */ | 622 | /* Also flush any partial walks */ |
| 607 | io_pgtable_tlb_add_flush(iop, iova, size, | 623 | io_pgtable_tlb_add_flush(iop, iova, size, |
| 608 | ARM_LPAE_GRANULE(data), false); | 624 | ARM_LPAE_GRANULE(data), false); |
| @@ -621,7 +637,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, | |||
| 621 | } | 637 | } |
| 622 | 638 | ||
| 623 | return size; | 639 | return size; |
| 624 | } else if (iopte_leaf(pte, lvl)) { | 640 | } else if (iopte_leaf(pte, lvl, iop->fmt)) { |
| 625 | /* | 641 | /* |
| 626 | * Insert a table at the next level to map the old region, | 642 | * Insert a table at the next level to map the old region, |
| 627 | * minus the part we want to unmap | 643 | * minus the part we want to unmap |
| @@ -669,7 +685,7 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, | |||
| 669 | return 0; | 685 | return 0; |
| 670 | 686 | ||
| 671 | /* Leaf entry? */ | 687 | /* Leaf entry? */ |
| 672 | if (iopte_leaf(pte,lvl)) | 688 | if (iopte_leaf(pte, lvl, data->iop.fmt)) |
| 673 | goto found_translation; | 689 | goto found_translation; |
| 674 | 690 | ||
| 675 | /* Take it to the next level */ | 691 | /* Take it to the next level */ |
| @@ -995,6 +1011,32 @@ arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) | |||
| 995 | return iop; | 1011 | return iop; |
| 996 | } | 1012 | } |
| 997 | 1013 | ||
| 1014 | static struct io_pgtable * | ||
| 1015 | arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) | ||
| 1016 | { | ||
| 1017 | struct io_pgtable *iop; | ||
| 1018 | |||
| 1019 | if (cfg->ias != 48 || cfg->oas > 40) | ||
| 1020 | return NULL; | ||
| 1021 | |||
| 1022 | cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); | ||
| 1023 | iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie); | ||
| 1024 | if (iop) { | ||
| 1025 | u64 mair, ttbr; | ||
| 1026 | |||
| 1027 | /* Copy values as union fields overlap */ | ||
| 1028 | mair = cfg->arm_lpae_s1_cfg.mair[0]; | ||
| 1029 | ttbr = cfg->arm_lpae_s1_cfg.ttbr[0]; | ||
| 1030 | |||
| 1031 | cfg->arm_mali_lpae_cfg.memattr = mair; | ||
| 1032 | cfg->arm_mali_lpae_cfg.transtab = ttbr | | ||
| 1033 | ARM_MALI_LPAE_TTBR_READ_INNER | | ||
| 1034 | ARM_MALI_LPAE_TTBR_ADRMODE_TABLE; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | return iop; | ||
| 1038 | } | ||
| 1039 | |||
| 998 | struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { | 1040 | struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { |
| 999 | .alloc = arm_64_lpae_alloc_pgtable_s1, | 1041 | .alloc = arm_64_lpae_alloc_pgtable_s1, |
| 1000 | .free = arm_lpae_free_pgtable, | 1042 | .free = arm_lpae_free_pgtable, |
| @@ -1015,6 +1057,11 @@ struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = { | |||
| 1015 | .free = arm_lpae_free_pgtable, | 1057 | .free = arm_lpae_free_pgtable, |
| 1016 | }; | 1058 | }; |
| 1017 | 1059 | ||
| 1060 | struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = { | ||
| 1061 | .alloc = arm_mali_lpae_alloc_pgtable, | ||
| 1062 | .free = arm_lpae_free_pgtable, | ||
| 1063 | }; | ||
| 1064 | |||
| 1018 | #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST | 1065 | #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST |
| 1019 | 1066 | ||
| 1020 | static struct io_pgtable_cfg *cfg_cookie; | 1067 | static struct io_pgtable_cfg *cfg_cookie; |
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c index 93f2880be6c6..5227cfdbb65b 100644 --- a/drivers/iommu/io-pgtable.c +++ b/drivers/iommu/io-pgtable.c | |||
| @@ -30,6 +30,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = { | |||
| 30 | [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, | 30 | [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, |
| 31 | [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, | 31 | [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, |
| 32 | [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, | 32 | [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, |
| 33 | [ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns, | ||
| 33 | #endif | 34 | #endif |
| 34 | #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S | 35 | #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S |
| 35 | [ARM_V7S] = &io_pgtable_arm_v7s_init_fns, | 36 | [ARM_V7S] = &io_pgtable_arm_v7s_init_fns, |
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h index 47d5ae559329..76969a564831 100644 --- a/include/linux/io-pgtable.h +++ b/include/linux/io-pgtable.h | |||
| @@ -12,6 +12,7 @@ enum io_pgtable_fmt { | |||
| 12 | ARM_64_LPAE_S1, | 12 | ARM_64_LPAE_S1, |
| 13 | ARM_64_LPAE_S2, | 13 | ARM_64_LPAE_S2, |
| 14 | ARM_V7S, | 14 | ARM_V7S, |
| 15 | ARM_MALI_LPAE, | ||
| 15 | IO_PGTABLE_NUM_FMTS, | 16 | IO_PGTABLE_NUM_FMTS, |
| 16 | }; | 17 | }; |
| 17 | 18 | ||
| @@ -108,6 +109,11 @@ struct io_pgtable_cfg { | |||
| 108 | u32 nmrr; | 109 | u32 nmrr; |
| 109 | u32 prrr; | 110 | u32 prrr; |
| 110 | } arm_v7s_cfg; | 111 | } arm_v7s_cfg; |
| 112 | |||
| 113 | struct { | ||
| 114 | u64 transtab; | ||
| 115 | u64 memattr; | ||
| 116 | } arm_mali_lpae_cfg; | ||
| 111 | }; | 117 | }; |
| 112 | }; | 118 | }; |
| 113 | 119 | ||
| @@ -209,5 +215,6 @@ extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns; | |||
| 209 | extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns; | 215 | extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns; |
| 210 | extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns; | 216 | extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns; |
| 211 | extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns; | 217 | extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns; |
| 218 | extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns; | ||
| 212 | 219 | ||
| 213 | #endif /* __IO_PGTABLE_H */ | 220 | #endif /* __IO_PGTABLE_H */ |
