| From 97a644208d1a08b7104d1fe2ace8cef011222711 Mon Sep 17 00:00:00 2001 |
| From: Yifan Zhang <zhangyf@marvell.com> |
| Date: Fri, 3 Jan 2014 12:01:26 +0000 |
| Subject: iommu/arm-smmu: fix pud/pmd entry fill sequence |
| |
| From: Yifan Zhang <zhangyf@marvell.com> |
| |
| commit 97a644208d1a08b7104d1fe2ace8cef011222711 upstream. |
| |
| 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. |
| |
| Signed-off-by: Yifan Zhang <zhangyf@marvell.com> |
| Signed-off-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/iommu/arm-smmu.c | 14 ++++++++++---- |
| 1 file changed, 10 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/iommu/arm-smmu.c |
| +++ b/drivers/iommu/arm-smmu.c |
| @@ -1317,6 +1317,11 @@ static int arm_smmu_alloc_init_pmd(struc |
| pmd = pmd_alloc_one(NULL, addr); |
| if (!pmd) |
| return -ENOMEM; |
| + |
| + pud_populate(NULL, pud, pmd); |
| + arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud)); |
| + |
| + pmd += pmd_index(addr); |
| } else |
| #endif |
| pmd = pmd_offset(pud, addr); |
| @@ -1325,8 +1330,6 @@ static int arm_smmu_alloc_init_pmd(struc |
| next = pmd_addr_end(addr, end); |
| ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn, |
| flags, stage); |
| - pud_populate(NULL, pud, pmd); |
| - arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud)); |
| phys += next - addr; |
| } while (pmd++, addr = next, addr < end); |
| |
| @@ -1346,6 +1349,11 @@ static int arm_smmu_alloc_init_pud(struc |
| pud = pud_alloc_one(NULL, addr); |
| if (!pud) |
| return -ENOMEM; |
| + |
| + pgd_populate(NULL, pgd, pud); |
| + arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd)); |
| + |
| + pud += pud_index(addr); |
| } else |
| #endif |
| pud = pud_offset(pgd, addr); |
| @@ -1354,8 +1362,6 @@ static int arm_smmu_alloc_init_pud(struc |
| next = pud_addr_end(addr, end); |
| ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys, |
| flags, stage); |
| - pgd_populate(NULL, pud, pgd); |
| - arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd)); |
| phys += next - addr; |
| } while (pud++, addr = next, addr < end); |
| |