aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu/arm-smmu.c
diff options
context:
space:
mode:
authorRobin Murphy <robin.murphy@arm.com>2015-12-07 13:18:52 -0500
committerWill Deacon <will.deacon@arm.com>2015-12-17 07:05:35 -0500
commit75df1386557c25188bd2383bbe8dd14a5ac81c06 (patch)
tree95850b0e6af3a2e4c5e15333922aba99f254542e /drivers/iommu/arm-smmu.c
parent06c610e8f32ba2fe41d57e1718611c2ec5024878 (diff)
iommu/arm-smmu: Invalidate TLBs properly
When invalidating an IOVA range potentially spanning multiple pages, such as when removing an entire intermediate-level table, we currently only issue an invalidation for the first IOVA of that range. Since the architecture specifies that address-based TLB maintenance operations target a single entry, an SMMU could feasibly retain live entries for subsequent pages within that unmapped range, which is not good. Make sure we hit every possible entry by iterating over the whole range at the granularity provided by the pagetable implementation. Signed-off-by: Robin Murphy <robin.murphy@arm.com> [will: added missing semicolons...] Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers/iommu/arm-smmu.c')
-rw-r--r--drivers/iommu/arm-smmu.c16
1 files changed, 13 insertions, 3 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 7e04bf5640ae..59ee4b8a3236 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -597,12 +597,18 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
597 if (!IS_ENABLED(CONFIG_64BIT) || smmu->version == ARM_SMMU_V1) { 597 if (!IS_ENABLED(CONFIG_64BIT) || smmu->version == ARM_SMMU_V1) {
598 iova &= ~12UL; 598 iova &= ~12UL;
599 iova |= ARM_SMMU_CB_ASID(cfg); 599 iova |= ARM_SMMU_CB_ASID(cfg);
600 writel_relaxed(iova, reg); 600 do {
601 writel_relaxed(iova, reg);
602 iova += granule;
603 } while (size -= granule);
601#ifdef CONFIG_64BIT 604#ifdef CONFIG_64BIT
602 } else { 605 } else {
603 iova >>= 12; 606 iova >>= 12;
604 iova |= (u64)ARM_SMMU_CB_ASID(cfg) << 48; 607 iova |= (u64)ARM_SMMU_CB_ASID(cfg) << 48;
605 writeq_relaxed(iova, reg); 608 do {
609 writeq_relaxed(iova, reg);
610 iova += granule >> 12;
611 } while (size -= granule);
606#endif 612#endif
607 } 613 }
608#ifdef CONFIG_64BIT 614#ifdef CONFIG_64BIT
@@ -610,7 +616,11 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
610 reg = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); 616 reg = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
611 reg += leaf ? ARM_SMMU_CB_S2_TLBIIPAS2L : 617 reg += leaf ? ARM_SMMU_CB_S2_TLBIIPAS2L :
612 ARM_SMMU_CB_S2_TLBIIPAS2; 618 ARM_SMMU_CB_S2_TLBIIPAS2;
613 writeq_relaxed(iova >> 12, reg); 619 iova >>= 12;
620 do {
621 writeq_relaxed(iova, reg);
622 iova += granule >> 12;
623 } while (size -= granule);
614#endif 624#endif
615 } else { 625 } else {
616 reg = ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_TLBIVMID; 626 reg = ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_TLBIVMID;