aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYifan Zhang <zhangyf@marvell.com>2014-01-03 07:01:26 -0500
committerWill Deacon <will.deacon@arm.com>2014-02-10 12:00:47 -0500
commit97a644208d1a08b7104d1fe2ace8cef011222711 (patch)
tree8278f4f2db8fec61cd8d4c8c1edbaeefe74b7ba2
parentb28a960c42fcd9cfc987441fa6d1c1a471f0f9ed (diff)
iommu/arm-smmu: fix pud/pmd entry fill sequence
The ARM SMMU driver's population of puds and pmds is broken, since we iterate over the next level of table repeatedly setting the current level descriptor to point at the pmd being initialised. This is clearly wrong when dealing with multiple pmds/puds. This patch fixes the problem by moving the pud/pmd population out of the loop and instead performing it when we allocate the next level (like we correctly do for ptes already). The starting address for the next level is then calculated prior to entering the loop. Cc: <stable@vger.kernel.org> Signed-off-by: Yifan Zhang <zhangyf@marvell.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--drivers/iommu/arm-smmu.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 8911850c9444..9f210de6537e 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1320,6 +1320,11 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
1320 pmd = pmd_alloc_one(NULL, addr); 1320 pmd = pmd_alloc_one(NULL, addr);
1321 if (!pmd) 1321 if (!pmd)
1322 return -ENOMEM; 1322 return -ENOMEM;
1323
1324 pud_populate(NULL, pud, pmd);
1325 arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud));
1326
1327 pmd += pmd_index(addr);
1323 } else 1328 } else
1324#endif 1329#endif
1325 pmd = pmd_offset(pud, addr); 1330 pmd = pmd_offset(pud, addr);
@@ -1328,8 +1333,6 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
1328 next = pmd_addr_end(addr, end); 1333 next = pmd_addr_end(addr, end);
1329 ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn, 1334 ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn,
1330 flags, stage); 1335 flags, stage);
1331 pud_populate(NULL, pud, pmd);
1332 arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud));
1333 phys += next - addr; 1336 phys += next - addr;
1334 } while (pmd++, addr = next, addr < end); 1337 } while (pmd++, addr = next, addr < end);
1335 1338
@@ -1349,6 +1352,11 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
1349 pud = pud_alloc_one(NULL, addr); 1352 pud = pud_alloc_one(NULL, addr);
1350 if (!pud) 1353 if (!pud)
1351 return -ENOMEM; 1354 return -ENOMEM;
1355
1356 pgd_populate(NULL, pgd, pud);
1357 arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd));
1358
1359 pud += pud_index(addr);
1352 } else 1360 } else
1353#endif 1361#endif
1354 pud = pud_offset(pgd, addr); 1362 pud = pud_offset(pgd, addr);
@@ -1357,8 +1365,6 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
1357 next = pud_addr_end(addr, end); 1365 next = pud_addr_end(addr, end);
1358 ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys, 1366 ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys,
1359 flags, stage); 1367 flags, stage);
1360 pgd_populate(NULL, pud, pgd);
1361 arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd));
1362 phys += next - addr; 1368 phys += next - addr;
1363 } while (pud++, addr = next, addr < end); 1369 } while (pud++, addr = next, addr < end);
1364 1370