Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommu/io-pgtable-arm: Prepare for TTBR1 usage

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>

authored by

Robin Murphy and committed by
Will Deacon
db690301 ac4b80e5

+23 -6
+19 -6
drivers/iommu/io-pgtable-arm.c
··· 104 104 #define ARM_LPAE_TCR_TG0_64K 1 105 105 #define ARM_LPAE_TCR_TG0_16K 2 106 106 107 + #define ARM_LPAE_TCR_TG1_16K 1 108 + #define ARM_LPAE_TCR_TG1_4K 2 109 + #define ARM_LPAE_TCR_TG1_64K 3 110 + 107 111 #define ARM_LPAE_TCR_SH_NS 0 108 112 #define ARM_LPAE_TCR_SH_OS 2 109 113 #define ARM_LPAE_TCR_SH_IS 3 ··· 468 464 arm_lpae_iopte *ptep = data->pgd; 469 465 int ret, lvl = data->start_level; 470 466 arm_lpae_iopte prot; 467 + long iaext = (long)iova >> cfg->ias; 471 468 472 469 /* If no access, then nothing to do */ 473 470 if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) ··· 477 472 if (WARN_ON(!size || (size & cfg->pgsize_bitmap) != size)) 478 473 return -EINVAL; 479 474 480 - if (WARN_ON(iova >> data->iop.cfg.ias || paddr >> data->iop.cfg.oas)) 475 + if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) 476 + iaext = ~iaext; 477 + if (WARN_ON(iaext || paddr >> cfg->oas)) 481 478 return -ERANGE; 482 479 483 480 prot = arm_lpae_prot_to_pte(data, iommu_prot); ··· 645 638 struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); 646 639 struct io_pgtable_cfg *cfg = &data->iop.cfg; 647 640 arm_lpae_iopte *ptep = data->pgd; 641 + long iaext = (long)iova >> cfg->ias; 648 642 649 643 if (WARN_ON(!size || (size & cfg->pgsize_bitmap) != size)) 650 644 return 0; 651 645 652 - if (WARN_ON(iova >> data->iop.cfg.ias)) 646 + if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) 647 + iaext = ~iaext; 648 + if (WARN_ON(iaext)) 653 649 return 0; 654 650 655 651 return __arm_lpae_unmap(data, gather, iova, size, data->start_level, ptep); ··· 788 778 u64 reg; 789 779 struct arm_lpae_io_pgtable *data; 790 780 typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr; 781 + bool tg1; 791 782 792 783 if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS | 793 - IO_PGTABLE_QUIRK_NON_STRICT)) 784 + IO_PGTABLE_QUIRK_NON_STRICT | 785 + IO_PGTABLE_QUIRK_ARM_TTBR1)) 794 786 return NULL; 795 787 796 788 data = arm_lpae_alloc_pgtable(cfg); ··· 810 798 tcr->orgn = ARM_LPAE_TCR_RGN_NC; 811 799 } 812 800 801 + tg1 = cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1; 813 802 switch (ARM_LPAE_GRANULE(data)) { 814 803 case SZ_4K: 815 - tcr->tg = ARM_LPAE_TCR_TG0_4K; 804 + tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_4K : ARM_LPAE_TCR_TG0_4K; 816 805 break; 817 806 case SZ_16K: 818 - tcr->tg = ARM_LPAE_TCR_TG0_16K; 807 + tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_16K : ARM_LPAE_TCR_TG0_16K; 819 808 break; 820 809 case SZ_64K: 821 - tcr->tg = ARM_LPAE_TCR_TG0_64K; 810 + tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_64K : ARM_LPAE_TCR_TG0_64K; 822 811 break; 823 812 } 824 813
+4
include/linux/io-pgtable.h
··· 83 83 * IO_PGTABLE_QUIRK_NON_STRICT: Skip issuing synchronous leaf TLBIs 84 84 * on unmap, for DMA domains using the flush queue mechanism for 85 85 * delayed invalidation. 86 + * 87 + * IO_PGTABLE_QUIRK_ARM_TTBR1: (ARM LPAE format) Configure the table 88 + * for use in the upper half of a split address space. 86 89 */ 87 90 #define IO_PGTABLE_QUIRK_ARM_NS BIT(0) 88 91 #define IO_PGTABLE_QUIRK_NO_PERMS BIT(1) 89 92 #define IO_PGTABLE_QUIRK_TLBI_ON_MAP BIT(2) 90 93 #define IO_PGTABLE_QUIRK_ARM_MTK_EXT BIT(3) 91 94 #define IO_PGTABLE_QUIRK_NON_STRICT BIT(4) 95 + #define IO_PGTABLE_QUIRK_ARM_TTBR1 BIT(5) 92 96 unsigned long quirks; 93 97 unsigned long pgsize_bitmap; 94 98 unsigned int ias;