aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2015-05-08 12:44:22 -0400
committerJoerg Roedel <jroedel@suse.de>2015-05-11 12:36:20 -0400
commit5dc5616ee850eaba055bb469a6c4a471d489140e (patch)
tree5c369d17001018c564828b9f95a99d411f316621
parentd9e7eb152bb24f06028a0d10b054e39ebdf14f9c (diff)
iommu/arm-smmu: Fix sign-extension of upstream bus addresses at stage 1
Stage 1 translation is controlled by two sets of page tables (TTBR0 and TTBR1) which grow up and down from zero respectively in the ARMv8 translation regime. For the SMMU, we only care about TTBR0 and, in the case of a 48-bit virtual space, we expect to map virtual addresses 0x0 through to 0xffff_ffff_ffff. Given that some masters may be incapable of emitting virtual addresses targetting TTBR1 (e.g. because they sit on a 48-bit bus), the SMMU architecture allows bit 47 to be sign-extended, halving the virtual range of TTBR0 but allowing TTBR1 to be used. This is controlled by the SEP field in TTBCR2. The SMMU driver incorrectly enables this sign-extension feature, which causes problems when userspace addresses are programmed into a master device with the SMMU expecting to map the incoming transactions via TTBR0; if the top bit of address is set, we will instead get a translation fault since TTBR1 walks are disabled in the TTBCR. This patch fixes the issue by disabling sign-extension of a fixed virtual address bit and instead basing the behaviour on the upstream bus size: the incoming address is zero extended unless the upstream bus is only 49 bits wide, in which case bit 48 is used as the sign bit and is replicated to the upper bits. Cc: <stable@vger.kernel.org> # v4.0+ Reported-by: Varun Sethi <varun.sethi@freescale.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r--drivers/iommu/arm-smmu.c30
1 files changed, 2 insertions, 28 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 9f7e1d34a32b..66a803b9dd3a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -224,14 +224,7 @@
224#define RESUME_TERMINATE (1 << 0) 224#define RESUME_TERMINATE (1 << 0)
225 225
226#define TTBCR2_SEP_SHIFT 15 226#define TTBCR2_SEP_SHIFT 15
227#define TTBCR2_SEP_MASK 0x7 227#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
228
229#define TTBCR2_ADDR_32 0
230#define TTBCR2_ADDR_36 1
231#define TTBCR2_ADDR_40 2
232#define TTBCR2_ADDR_42 3
233#define TTBCR2_ADDR_44 4
234#define TTBCR2_ADDR_48 5
235 228
236#define TTBRn_HI_ASID_SHIFT 16 229#define TTBRn_HI_ASID_SHIFT 16
237 230
@@ -793,26 +786,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
793 writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); 786 writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
794 if (smmu->version > ARM_SMMU_V1) { 787 if (smmu->version > ARM_SMMU_V1) {
795 reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; 788 reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
796 switch (smmu->va_size) { 789 reg |= TTBCR2_SEP_UPSTREAM;
797 case 32:
798 reg |= (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT);
799 break;
800 case 36:
801 reg |= (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT);
802 break;
803 case 40:
804 reg |= (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT);
805 break;
806 case 42:
807 reg |= (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT);
808 break;
809 case 44:
810 reg |= (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT);
811 break;
812 case 48:
813 reg |= (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT);
814 break;
815 }
816 writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); 790 writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2);
817 } 791 }
818 } else { 792 } else {