[COMMON] iommu/exynos: add checking SBB entries
authorJanghyuck Kim <janghyuck.kim@samsung.com>
Wed, 4 Apr 2018 08:07:19 +0000 (17:07 +0900)
committerJanghyuck Kim <janghyuck.kim@samsung.com>
Mon, 23 Jul 2018 05:39:04 +0000 (14:39 +0900)
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 <janghyuck.kim@samsung.com>
drivers/iommu/exynos-iommu-reg.h
drivers/iommu/exynos-iommu.c
drivers/iommu/exynos-iommu.h

index 85c65c5b83c8fbf3ba6c19ecfca85a7bbacfb2c3..744cc713caff1687727245ebf45bf1031ec586f2 100644 (file)
@@ -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");
index c2d9de9d3babd09380e861ccea4691262190fe7d..1e9e2b3be45b015338de17dd916cab6230683037 100644 (file)
@@ -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);
        }
index bdf77fed36941b156c07fa3cd9c42004a31cc682..03d71aba4b61c1d9577289a25875388f5414aa0c 100644 (file)
@@ -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)         \