From 8e57337c60da14c3801413026f6936524d6960a9 Mon Sep 17 00:00:00 2001 From: Janghyuck Kim Date: Wed, 4 Apr 2018 17:07:19 +0900 Subject: [PATCH] [COMMON] iommu/exynos: add checking SBB entries This patch added SBB entries checking that comparing to page table, and displays message when it cached different address. Change-Id: I562633813afa771419e5124811469d431d866a96 Signed-off-by: Janghyuck Kim --- drivers/iommu/exynos-iommu-reg.h | 58 ++++++++++++++++++++++++++------ drivers/iommu/exynos-iommu.c | 2 +- drivers/iommu/exynos-iommu.h | 3 ++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/exynos-iommu-reg.h b/drivers/iommu/exynos-iommu-reg.h index 85c65c5b83c8..744cc713caff 100644 --- a/drivers/iommu/exynos-iommu-reg.h +++ b/drivers/iommu/exynos-iommu-reg.h @@ -122,10 +122,39 @@ static inline void dump_sysmmu_tlb_way(struct sysmmu_drvdata *drvdata) pr_crit(">> No Valid SBB Entries\n"); } -static inline void dump_sysmmu_tlb_port(struct sysmmu_drvdata *drvdata) +static inline void sysmmu_sbb_compare(u32 sbb_vpn, u32 sbb_link, + phys_addr_t pgtable) +{ + sysmmu_pte_t *entry; + unsigned long vaddr = MMU_VADDR_FROM_SBB(sbb_vpn); + unsigned long paddr = MMU_PADDR_FROM_SBB((unsigned long)sbb_link); + unsigned long phys = 0; + + if (!pgtable) + return; + + entry = section_entry(phys_to_virt(pgtable), vaddr); + + if (lv1ent_page(entry)) { + phys = lv2table_base(entry); + + if (paddr != phys) { + pr_crit(">> SBB mismatch detected!\n"); + pr_crit(" entry addr: %lx / SBB addr %lx\n", + paddr, phys); + } + } else { + pr_crit(">> Invalid address detected! entry: %#lx", + (unsigned long)*entry); + } +} + +static inline void dump_sysmmu_tlb_port(struct sysmmu_drvdata *drvdata, + phys_addr_t pgtable) { int t, i, j, k; u32 capa0, capa1, info; + u32 sbb_vpn, sbb_link; unsigned int cnt; int num_tlb, num_port, num_sbb; void __iomem *sfrbase = drvdata->sfrbase; @@ -162,14 +191,17 @@ static inline void dump_sysmmu_tlb_port(struct sysmmu_drvdata *drvdata) if (!cnt) pr_crit(">> No Valid TLB Entries\n"); - pr_crit("--- SBB(Second-Level Page Table Base Address Buffer ---\n"); + pr_crit("--- SBB(Second-Level Page Table Base Address Buffer) ---\n"); for (i = 0, cnt = 0; i < num_sbb; i++) { __raw_writel(i, sfrbase + REG_CAPA1_SBB_READ); if (MMU_SBB_ENTRY_VALID(__raw_readl(sfrbase + REG_CAPA1_SBB_VPN))) { - pr_crit("[%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x\n", - i, __raw_readl(sfrbase + REG_CAPA1_SBB_VPN), - __raw_readl(sfrbase + REG_CAPA1_SBB_LINK), + sbb_vpn = __raw_readl(sfrbase + REG_CAPA1_SBB_VPN); + sbb_link = __raw_readl(sfrbase + REG_CAPA1_SBB_LINK); + + pr_crit("[%02d] VPN: %#010x, PPN: %#010x, ATTR: %#010x", + i, sbb_vpn, sbb_link, __raw_readl(sfrbase + REG_CAPA1_SBB_ATTR)); + sysmmu_sbb_compare(sbb_vpn, sbb_link, pgtable); cnt++; } } @@ -186,7 +218,8 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { "UNKNOWN FAULT" }; -static inline void dump_sysmmu_status(struct sysmmu_drvdata *drvdata) +static inline void dump_sysmmu_status(struct sysmmu_drvdata *drvdata, + phys_addr_t pgtable) { int info; pgd_t *pgd; @@ -235,7 +268,7 @@ static inline void dump_sysmmu_status(struct sysmmu_drvdata *drvdata) if (IS_TLB_WAY_TYPE(drvdata)) dump_sysmmu_tlb_way(drvdata); else if(IS_TLB_PORT_TYPE(drvdata)) - dump_sysmmu_tlb_port(drvdata); + dump_sysmmu_tlb_port(drvdata, pgtable); } static inline void show_secure_fault_information(struct sysmmu_drvdata *drvdata, @@ -334,11 +367,9 @@ static inline void show_fault_information(struct sysmmu_drvdata *drvdata, pr_crit("Page table base of driver: %pa\n", &drvdata->pgtable); - if (fault_id == SYSMMU_FAULT_PTW_ACCESS) - pr_crit("System MMU has failed to access page table\n"); - if (!pfn_valid(pgtable >> PAGE_SHIFT)) { pr_crit("Page table base is not in a valid memory region\n"); + pgtable = 0; } else { sysmmu_pte_t *ent; ent = section_entry(phys_to_virt(pgtable), fault_addr); @@ -350,7 +381,12 @@ static inline void show_fault_information(struct sysmmu_drvdata *drvdata, } } - dump_sysmmu_status(drvdata); + if (fault_id == SYSMMU_FAULT_PTW_ACCESS) { + pr_crit("System MMU has failed to access page table\n"); + pgtable = 0; + } + + dump_sysmmu_status(drvdata, pgtable); finish: pr_crit("----------------------------------------------------------\n"); diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c2d9de9d3bab..1e9e2b3be45b 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1325,7 +1325,7 @@ void exynos_sysmmu_show_status(struct device *master) } dev_info(drvdata->sysmmu, "Dumping status.\n"); - dump_sysmmu_status(drvdata); + dump_sysmmu_status(drvdata, 0); spin_unlock_irqrestore(&drvdata->lock, flags); } diff --git a/drivers/iommu/exynos-iommu.h b/drivers/iommu/exynos-iommu.h index bdf77fed3694..03d71aba4b61 100644 --- a/drivers/iommu/exynos-iommu.h +++ b/drivers/iommu/exynos-iommu.h @@ -200,6 +200,9 @@ typedef u32 sysmmu_pte_t; #define MMU_TLB_ENTRY_VALID(reg) ((reg) >> 28) #define MMU_SBB_ENTRY_VALID(reg) ((reg) >> 28) +#define MMU_VADDR_FROM_SBB(reg) (((reg) & 0xFFFFF) << 12) +#define MMU_PADDR_FROM_SBB(reg) (((reg) & 0x3FFFFFF) << 10) + #define MMU_FAULT_INFO_READ_REQUEST 0 #define MMU_FAULT_INFO_WRITE_REQUEST 1 #define MMU_IS_READ_FAULT(reg) \ -- 2.20.1