18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Debug helper to dump the current kernel pagetables of the system 48c2ecf20Sopenharmony_ci * so that we can see what the various memory ranges are set to. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Derived from x86 implementation: 78c2ecf20Sopenharmony_ci * (C) Copyright 2008 Intel Corporation 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Arjan van de Ven <arjan@linux.intel.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 128c2ecf20Sopenharmony_ci#include <linux/fs.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/domain.h> 178c2ecf20Sopenharmony_ci#include <asm/fixmap.h> 188c2ecf20Sopenharmony_ci#include <asm/memory.h> 198c2ecf20Sopenharmony_ci#include <asm/ptdump.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic struct addr_marker address_markers[] = { 228c2ecf20Sopenharmony_ci { MODULES_VADDR, "Modules" }, 238c2ecf20Sopenharmony_ci { PAGE_OFFSET, "Kernel Mapping" }, 248c2ecf20Sopenharmony_ci { 0, "vmalloc() Area" }, 258c2ecf20Sopenharmony_ci { VMALLOC_END, "vmalloc() End" }, 268c2ecf20Sopenharmony_ci { FIXADDR_START, "Fixmap Area" }, 278c2ecf20Sopenharmony_ci { VECTORS_BASE, "Vectors" }, 288c2ecf20Sopenharmony_ci { VECTORS_BASE + PAGE_SIZE * 2, "Vectors End" }, 298c2ecf20Sopenharmony_ci { -1, NULL }, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define pt_dump_seq_printf(m, fmt, args...) \ 338c2ecf20Sopenharmony_ci({ \ 348c2ecf20Sopenharmony_ci if (m) \ 358c2ecf20Sopenharmony_ci seq_printf(m, fmt, ##args); \ 368c2ecf20Sopenharmony_ci}) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define pt_dump_seq_puts(m, fmt) \ 398c2ecf20Sopenharmony_ci({ \ 408c2ecf20Sopenharmony_ci if (m) \ 418c2ecf20Sopenharmony_ci seq_printf(m, fmt); \ 428c2ecf20Sopenharmony_ci}) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct pg_state { 458c2ecf20Sopenharmony_ci struct seq_file *seq; 468c2ecf20Sopenharmony_ci const struct addr_marker *marker; 478c2ecf20Sopenharmony_ci unsigned long start_address; 488c2ecf20Sopenharmony_ci unsigned level; 498c2ecf20Sopenharmony_ci u64 current_prot; 508c2ecf20Sopenharmony_ci bool check_wx; 518c2ecf20Sopenharmony_ci unsigned long wx_pages; 528c2ecf20Sopenharmony_ci const char *current_domain; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct prot_bits { 568c2ecf20Sopenharmony_ci u64 mask; 578c2ecf20Sopenharmony_ci u64 val; 588c2ecf20Sopenharmony_ci const char *set; 598c2ecf20Sopenharmony_ci const char *clear; 608c2ecf20Sopenharmony_ci bool ro_bit; 618c2ecf20Sopenharmony_ci bool nx_bit; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const struct prot_bits pte_bits[] = { 658c2ecf20Sopenharmony_ci { 668c2ecf20Sopenharmony_ci .mask = L_PTE_USER, 678c2ecf20Sopenharmony_ci .val = L_PTE_USER, 688c2ecf20Sopenharmony_ci .set = "USR", 698c2ecf20Sopenharmony_ci .clear = " ", 708c2ecf20Sopenharmony_ci }, { 718c2ecf20Sopenharmony_ci .mask = L_PTE_RDONLY, 728c2ecf20Sopenharmony_ci .val = L_PTE_RDONLY, 738c2ecf20Sopenharmony_ci .set = "ro", 748c2ecf20Sopenharmony_ci .clear = "RW", 758c2ecf20Sopenharmony_ci .ro_bit = true, 768c2ecf20Sopenharmony_ci }, { 778c2ecf20Sopenharmony_ci .mask = L_PTE_XN, 788c2ecf20Sopenharmony_ci .val = L_PTE_XN, 798c2ecf20Sopenharmony_ci .set = "NX", 808c2ecf20Sopenharmony_ci .clear = "x ", 818c2ecf20Sopenharmony_ci .nx_bit = true, 828c2ecf20Sopenharmony_ci }, { 838c2ecf20Sopenharmony_ci .mask = L_PTE_SHARED, 848c2ecf20Sopenharmony_ci .val = L_PTE_SHARED, 858c2ecf20Sopenharmony_ci .set = "SHD", 868c2ecf20Sopenharmony_ci .clear = " ", 878c2ecf20Sopenharmony_ci }, { 888c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 898c2ecf20Sopenharmony_ci .val = L_PTE_MT_UNCACHED, 908c2ecf20Sopenharmony_ci .set = "SO/UNCACHED", 918c2ecf20Sopenharmony_ci }, { 928c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 938c2ecf20Sopenharmony_ci .val = L_PTE_MT_BUFFERABLE, 948c2ecf20Sopenharmony_ci .set = "MEM/BUFFERABLE/WC", 958c2ecf20Sopenharmony_ci }, { 968c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 978c2ecf20Sopenharmony_ci .val = L_PTE_MT_WRITETHROUGH, 988c2ecf20Sopenharmony_ci .set = "MEM/CACHED/WT", 998c2ecf20Sopenharmony_ci }, { 1008c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1018c2ecf20Sopenharmony_ci .val = L_PTE_MT_WRITEBACK, 1028c2ecf20Sopenharmony_ci .set = "MEM/CACHED/WBRA", 1038c2ecf20Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 1048c2ecf20Sopenharmony_ci }, { 1058c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1068c2ecf20Sopenharmony_ci .val = L_PTE_MT_MINICACHE, 1078c2ecf20Sopenharmony_ci .set = "MEM/MINICACHE", 1088c2ecf20Sopenharmony_ci#endif 1098c2ecf20Sopenharmony_ci }, { 1108c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1118c2ecf20Sopenharmony_ci .val = L_PTE_MT_WRITEALLOC, 1128c2ecf20Sopenharmony_ci .set = "MEM/CACHED/WBWA", 1138c2ecf20Sopenharmony_ci }, { 1148c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1158c2ecf20Sopenharmony_ci .val = L_PTE_MT_DEV_SHARED, 1168c2ecf20Sopenharmony_ci .set = "DEV/SHARED", 1178c2ecf20Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 1188c2ecf20Sopenharmony_ci }, { 1198c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1208c2ecf20Sopenharmony_ci .val = L_PTE_MT_DEV_NONSHARED, 1218c2ecf20Sopenharmony_ci .set = "DEV/NONSHARED", 1228c2ecf20Sopenharmony_ci#endif 1238c2ecf20Sopenharmony_ci }, { 1248c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1258c2ecf20Sopenharmony_ci .val = L_PTE_MT_DEV_WC, 1268c2ecf20Sopenharmony_ci .set = "DEV/WC", 1278c2ecf20Sopenharmony_ci }, { 1288c2ecf20Sopenharmony_ci .mask = L_PTE_MT_MASK, 1298c2ecf20Sopenharmony_ci .val = L_PTE_MT_DEV_CACHED, 1308c2ecf20Sopenharmony_ci .set = "DEV/CACHED", 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct prot_bits section_bits[] = { 1358c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci .mask = PMD_SECT_USER, 1388c2ecf20Sopenharmony_ci .val = PMD_SECT_USER, 1398c2ecf20Sopenharmony_ci .set = "USR", 1408c2ecf20Sopenharmony_ci }, { 1418c2ecf20Sopenharmony_ci .mask = L_PMD_SECT_RDONLY | PMD_SECT_AP2, 1428c2ecf20Sopenharmony_ci .val = L_PMD_SECT_RDONLY | PMD_SECT_AP2, 1438c2ecf20Sopenharmony_ci .set = "ro", 1448c2ecf20Sopenharmony_ci .clear = "RW", 1458c2ecf20Sopenharmony_ci .ro_bit = true, 1468c2ecf20Sopenharmony_ci#elif __LINUX_ARM_ARCH__ >= 6 1478c2ecf20Sopenharmony_ci { 1488c2ecf20Sopenharmony_ci .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1498c2ecf20Sopenharmony_ci .val = PMD_SECT_APX | PMD_SECT_AP_WRITE, 1508c2ecf20Sopenharmony_ci .set = " ro", 1518c2ecf20Sopenharmony_ci .ro_bit = true, 1528c2ecf20Sopenharmony_ci }, { 1538c2ecf20Sopenharmony_ci .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1548c2ecf20Sopenharmony_ci .val = PMD_SECT_AP_WRITE, 1558c2ecf20Sopenharmony_ci .set = " RW", 1568c2ecf20Sopenharmony_ci }, { 1578c2ecf20Sopenharmony_ci .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1588c2ecf20Sopenharmony_ci .val = PMD_SECT_AP_READ, 1598c2ecf20Sopenharmony_ci .set = "USR ro", 1608c2ecf20Sopenharmony_ci }, { 1618c2ecf20Sopenharmony_ci .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1628c2ecf20Sopenharmony_ci .val = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1638c2ecf20Sopenharmony_ci .set = "USR RW", 1648c2ecf20Sopenharmony_ci#else /* ARMv4/ARMv5 */ 1658c2ecf20Sopenharmony_ci /* These are approximate */ 1668c2ecf20Sopenharmony_ci { 1678c2ecf20Sopenharmony_ci .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1688c2ecf20Sopenharmony_ci .val = 0, 1698c2ecf20Sopenharmony_ci .set = " ro", 1708c2ecf20Sopenharmony_ci .ro_bit = true, 1718c2ecf20Sopenharmony_ci }, { 1728c2ecf20Sopenharmony_ci .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1738c2ecf20Sopenharmony_ci .val = PMD_SECT_AP_WRITE, 1748c2ecf20Sopenharmony_ci .set = " RW", 1758c2ecf20Sopenharmony_ci }, { 1768c2ecf20Sopenharmony_ci .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1778c2ecf20Sopenharmony_ci .val = PMD_SECT_AP_READ, 1788c2ecf20Sopenharmony_ci .set = "USR ro", 1798c2ecf20Sopenharmony_ci }, { 1808c2ecf20Sopenharmony_ci .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1818c2ecf20Sopenharmony_ci .val = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE, 1828c2ecf20Sopenharmony_ci .set = "USR RW", 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci }, { 1858c2ecf20Sopenharmony_ci .mask = PMD_SECT_XN, 1868c2ecf20Sopenharmony_ci .val = PMD_SECT_XN, 1878c2ecf20Sopenharmony_ci .set = "NX", 1888c2ecf20Sopenharmony_ci .clear = "x ", 1898c2ecf20Sopenharmony_ci .nx_bit = true, 1908c2ecf20Sopenharmony_ci }, { 1918c2ecf20Sopenharmony_ci .mask = PMD_SECT_S, 1928c2ecf20Sopenharmony_ci .val = PMD_SECT_S, 1938c2ecf20Sopenharmony_ci .set = "SHD", 1948c2ecf20Sopenharmony_ci .clear = " ", 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct pg_level { 1998c2ecf20Sopenharmony_ci const struct prot_bits *bits; 2008c2ecf20Sopenharmony_ci size_t num; 2018c2ecf20Sopenharmony_ci u64 mask; 2028c2ecf20Sopenharmony_ci const struct prot_bits *ro_bit; 2038c2ecf20Sopenharmony_ci const struct prot_bits *nx_bit; 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic struct pg_level pg_level[] = { 2078c2ecf20Sopenharmony_ci { 2088c2ecf20Sopenharmony_ci }, { /* pgd */ 2098c2ecf20Sopenharmony_ci }, { /* p4d */ 2108c2ecf20Sopenharmony_ci }, { /* pud */ 2118c2ecf20Sopenharmony_ci }, { /* pmd */ 2128c2ecf20Sopenharmony_ci .bits = section_bits, 2138c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(section_bits), 2148c2ecf20Sopenharmony_ci }, { /* pte */ 2158c2ecf20Sopenharmony_ci .bits = pte_bits, 2168c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(pte_bits), 2178c2ecf20Sopenharmony_ci }, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void dump_prot(struct pg_state *st, const struct prot_bits *bits, size_t num) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci unsigned i; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci for (i = 0; i < num; i++, bits++) { 2258c2ecf20Sopenharmony_ci const char *s; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if ((st->current_prot & bits->mask) == bits->val) 2288c2ecf20Sopenharmony_ci s = bits->set; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci s = bits->clear; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (s) 2338c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, " %s", s); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void note_prot_wx(struct pg_state *st, unsigned long addr) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci if (!st->check_wx) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci if ((st->current_prot & pg_level[st->level].ro_bit->mask) == 2428c2ecf20Sopenharmony_ci pg_level[st->level].ro_bit->val) 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci if ((st->current_prot & pg_level[st->level].nx_bit->mask) == 2458c2ecf20Sopenharmony_ci pg_level[st->level].nx_bit->val) 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci WARN_ONCE(1, "arm/mm: Found insecure W+X mapping at address %pS\n", 2498c2ecf20Sopenharmony_ci (void *)st->start_address); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci st->wx_pages += (addr - st->start_address) / PAGE_SIZE; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void note_page(struct pg_state *st, unsigned long addr, 2558c2ecf20Sopenharmony_ci unsigned int level, u64 val, const char *domain) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci static const char units[] = "KMGTPE"; 2588c2ecf20Sopenharmony_ci u64 prot = val & pg_level[level].mask; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!st->level) { 2618c2ecf20Sopenharmony_ci st->level = level; 2628c2ecf20Sopenharmony_ci st->current_prot = prot; 2638c2ecf20Sopenharmony_ci st->current_domain = domain; 2648c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); 2658c2ecf20Sopenharmony_ci } else if (prot != st->current_prot || level != st->level || 2668c2ecf20Sopenharmony_ci domain != st->current_domain || 2678c2ecf20Sopenharmony_ci addr >= st->marker[1].start_address) { 2688c2ecf20Sopenharmony_ci const char *unit = units; 2698c2ecf20Sopenharmony_ci unsigned long delta; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (st->current_prot) { 2728c2ecf20Sopenharmony_ci note_prot_wx(st, addr); 2738c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, "0x%08lx-0x%08lx ", 2748c2ecf20Sopenharmony_ci st->start_address, addr); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci delta = (addr - st->start_address) >> 10; 2778c2ecf20Sopenharmony_ci while (!(delta & 1023) && unit[1]) { 2788c2ecf20Sopenharmony_ci delta >>= 10; 2798c2ecf20Sopenharmony_ci unit++; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, "%9lu%c", delta, *unit); 2828c2ecf20Sopenharmony_ci if (st->current_domain) 2838c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, " %s", 2848c2ecf20Sopenharmony_ci st->current_domain); 2858c2ecf20Sopenharmony_ci if (pg_level[st->level].bits) 2868c2ecf20Sopenharmony_ci dump_prot(st, pg_level[st->level].bits, pg_level[st->level].num); 2878c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, "\n"); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (addr >= st->marker[1].start_address) { 2918c2ecf20Sopenharmony_ci st->marker++; 2928c2ecf20Sopenharmony_ci pt_dump_seq_printf(st->seq, "---[ %s ]---\n", 2938c2ecf20Sopenharmony_ci st->marker->name); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci st->start_address = addr; 2968c2ecf20Sopenharmony_ci st->current_prot = prot; 2978c2ecf20Sopenharmony_ci st->current_domain = domain; 2988c2ecf20Sopenharmony_ci st->level = level; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start, 3038c2ecf20Sopenharmony_ci const char *domain) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci pte_t *pte = pte_offset_kernel(pmd, 0); 3068c2ecf20Sopenharmony_ci unsigned long addr; 3078c2ecf20Sopenharmony_ci unsigned i; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; i++, pte++) { 3108c2ecf20Sopenharmony_ci addr = start + i * PAGE_SIZE; 3118c2ecf20Sopenharmony_ci note_page(st, addr, 5, pte_val(*pte), domain); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const char *get_domain_name(pmd_t *pmd) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 3188c2ecf20Sopenharmony_ci switch (pmd_val(*pmd) & PMD_DOMAIN_MASK) { 3198c2ecf20Sopenharmony_ci case PMD_DOMAIN(DOMAIN_KERNEL): 3208c2ecf20Sopenharmony_ci return "KERNEL "; 3218c2ecf20Sopenharmony_ci case PMD_DOMAIN(DOMAIN_USER): 3228c2ecf20Sopenharmony_ci return "USER "; 3238c2ecf20Sopenharmony_ci case PMD_DOMAIN(DOMAIN_IO): 3248c2ecf20Sopenharmony_ci return "IO "; 3258c2ecf20Sopenharmony_ci case PMD_DOMAIN(DOMAIN_VECTORS): 3268c2ecf20Sopenharmony_ci return "VECTORS"; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci return "unknown"; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci#endif 3318c2ecf20Sopenharmony_ci return NULL; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci pmd_t *pmd = pmd_offset(pud, 0); 3378c2ecf20Sopenharmony_ci unsigned long addr; 3388c2ecf20Sopenharmony_ci unsigned i; 3398c2ecf20Sopenharmony_ci const char *domain; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PMD; i++, pmd++) { 3428c2ecf20Sopenharmony_ci addr = start + i * PMD_SIZE; 3438c2ecf20Sopenharmony_ci domain = get_domain_name(pmd); 3448c2ecf20Sopenharmony_ci if (pmd_none(*pmd) || pmd_large(*pmd) || !pmd_present(*pmd)) 3458c2ecf20Sopenharmony_ci note_page(st, addr, 4, pmd_val(*pmd), domain); 3468c2ecf20Sopenharmony_ci else 3478c2ecf20Sopenharmony_ci walk_pte(st, pmd, addr, domain); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])) { 3508c2ecf20Sopenharmony_ci addr += SECTION_SIZE; 3518c2ecf20Sopenharmony_ci pmd++; 3528c2ecf20Sopenharmony_ci domain = get_domain_name(pmd); 3538c2ecf20Sopenharmony_ci note_page(st, addr, 4, pmd_val(*pmd), domain); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic void walk_pud(struct pg_state *st, p4d_t *p4d, unsigned long start) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci pud_t *pud = pud_offset(p4d, 0); 3618c2ecf20Sopenharmony_ci unsigned long addr; 3628c2ecf20Sopenharmony_ci unsigned i; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PUD; i++, pud++) { 3658c2ecf20Sopenharmony_ci addr = start + i * PUD_SIZE; 3668c2ecf20Sopenharmony_ci if (!pud_none(*pud)) { 3678c2ecf20Sopenharmony_ci walk_pmd(st, pud, addr); 3688c2ecf20Sopenharmony_ci } else { 3698c2ecf20Sopenharmony_ci note_page(st, addr, 3, pud_val(*pud), NULL); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void walk_p4d(struct pg_state *st, pgd_t *pgd, unsigned long start) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci p4d_t *p4d = p4d_offset(pgd, 0); 3778c2ecf20Sopenharmony_ci unsigned long addr; 3788c2ecf20Sopenharmony_ci unsigned i; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_P4D; i++, p4d++) { 3818c2ecf20Sopenharmony_ci addr = start + i * P4D_SIZE; 3828c2ecf20Sopenharmony_ci if (!p4d_none(*p4d)) { 3838c2ecf20Sopenharmony_ci walk_pud(st, p4d, addr); 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci note_page(st, addr, 2, p4d_val(*p4d), NULL); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void walk_pgd(struct pg_state *st, struct mm_struct *mm, 3918c2ecf20Sopenharmony_ci unsigned long start) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci pgd_t *pgd = pgd_offset(mm, 0UL); 3948c2ecf20Sopenharmony_ci unsigned i; 3958c2ecf20Sopenharmony_ci unsigned long addr; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PGD; i++, pgd++) { 3988c2ecf20Sopenharmony_ci addr = start + i * PGDIR_SIZE; 3998c2ecf20Sopenharmony_ci if (!pgd_none(*pgd)) { 4008c2ecf20Sopenharmony_ci walk_p4d(st, pgd, addr); 4018c2ecf20Sopenharmony_ci } else { 4028c2ecf20Sopenharmony_ci note_page(st, addr, 1, pgd_val(*pgd), NULL); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_civoid ptdump_walk_pgd(struct seq_file *m, struct ptdump_info *info) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct pg_state st = { 4108c2ecf20Sopenharmony_ci .seq = m, 4118c2ecf20Sopenharmony_ci .marker = info->markers, 4128c2ecf20Sopenharmony_ci .check_wx = false, 4138c2ecf20Sopenharmony_ci }; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci walk_pgd(&st, info->mm, info->base_addr); 4168c2ecf20Sopenharmony_ci note_page(&st, 0, 0, 0, NULL); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic void ptdump_initialize(void) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci unsigned i, j; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pg_level); i++) 4248c2ecf20Sopenharmony_ci if (pg_level[i].bits) 4258c2ecf20Sopenharmony_ci for (j = 0; j < pg_level[i].num; j++) { 4268c2ecf20Sopenharmony_ci pg_level[i].mask |= pg_level[i].bits[j].mask; 4278c2ecf20Sopenharmony_ci if (pg_level[i].bits[j].ro_bit) 4288c2ecf20Sopenharmony_ci pg_level[i].ro_bit = &pg_level[i].bits[j]; 4298c2ecf20Sopenharmony_ci if (pg_level[i].bits[j].nx_bit) 4308c2ecf20Sopenharmony_ci pg_level[i].nx_bit = &pg_level[i].bits[j]; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci address_markers[2].start_address = VMALLOC_START; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic struct ptdump_info kernel_ptdump_info = { 4378c2ecf20Sopenharmony_ci .mm = &init_mm, 4388c2ecf20Sopenharmony_ci .markers = address_markers, 4398c2ecf20Sopenharmony_ci .base_addr = 0, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_civoid ptdump_check_wx(void) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct pg_state st = { 4458c2ecf20Sopenharmony_ci .seq = NULL, 4468c2ecf20Sopenharmony_ci .marker = (struct addr_marker[]) { 4478c2ecf20Sopenharmony_ci { 0, NULL}, 4488c2ecf20Sopenharmony_ci { -1, NULL}, 4498c2ecf20Sopenharmony_ci }, 4508c2ecf20Sopenharmony_ci .check_wx = true, 4518c2ecf20Sopenharmony_ci }; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci walk_pgd(&st, &init_mm, 0); 4548c2ecf20Sopenharmony_ci note_page(&st, 0, 0, 0, NULL); 4558c2ecf20Sopenharmony_ci if (st.wx_pages) 4568c2ecf20Sopenharmony_ci pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found\n", 4578c2ecf20Sopenharmony_ci st.wx_pages); 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci pr_info("Checked W+X mappings: passed, no W+X pages found\n"); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int ptdump_init(void) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci ptdump_initialize(); 4658c2ecf20Sopenharmony_ci ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables"); 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci__initcall(ptdump_init); 469