1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21fd15b87SRussell King /*
31fd15b87SRussell King * Debug helper to dump the current kernel pagetables of the system
41fd15b87SRussell King * so that we can see what the various memory ranges are set to.
51fd15b87SRussell King *
61fd15b87SRussell King * Derived from x86 implementation:
71fd15b87SRussell King * (C) Copyright 2008 Intel Corporation
81fd15b87SRussell King *
91fd15b87SRussell King * Author: Arjan van de Ven <arjan@linux.intel.com>
101fd15b87SRussell King */
111fd15b87SRussell King #include <linux/debugfs.h>
121fd15b87SRussell King #include <linux/fs.h>
131fd15b87SRussell King #include <linux/mm.h>
141fd15b87SRussell King #include <linux/seq_file.h>
151fd15b87SRussell King
16dd59f974SKees Cook #include <asm/domain.h>
171fd15b87SRussell King #include <asm/fixmap.h>
18*a9ff6961SLinus Walleij #include <asm/page.h>
194fb69cc4SJinbum Park #include <asm/ptdump.h>
201fd15b87SRussell King
211fd15b87SRussell King static struct addr_marker address_markers[] = {
22b53a9edcSHailong Liu #ifdef CONFIG_KASAN
23b53a9edcSHailong Liu { KASAN_SHADOW_START, "Kasan shadow start"},
24b53a9edcSHailong Liu { KASAN_SHADOW_END, "Kasan shadow end"},
25b53a9edcSHailong Liu #endif
261fd15b87SRussell King { MODULES_VADDR, "Modules" },
271fd15b87SRussell King { PAGE_OFFSET, "Kernel Mapping" },
281fd15b87SRussell King { 0, "vmalloc() Area" },
29afd1efa1SWang Kefeng { FDT_FIXED_BASE, "FDT Area" },
301fd15b87SRussell King { FIXADDR_START, "Fixmap Area" },
31d2ca5f24SAfzal Mohammed { VECTORS_BASE, "Vectors" },
32d2ca5f24SAfzal Mohammed { VECTORS_BASE + PAGE_SIZE * 2, "Vectors End" },
331fd15b87SRussell King { -1, NULL },
341fd15b87SRussell King };
351fd15b87SRussell King
36d02ca6d7SJinbum Park #define pt_dump_seq_printf(m, fmt, args...) \
37d02ca6d7SJinbum Park ({ \
38d02ca6d7SJinbum Park if (m) \
39d02ca6d7SJinbum Park seq_printf(m, fmt, ##args); \
40d02ca6d7SJinbum Park })
41d02ca6d7SJinbum Park
42d02ca6d7SJinbum Park #define pt_dump_seq_puts(m, fmt) \
43d02ca6d7SJinbum Park ({ \
44d02ca6d7SJinbum Park if (m) \
45d02ca6d7SJinbum Park seq_printf(m, fmt); \
46d02ca6d7SJinbum Park })
47d02ca6d7SJinbum Park
481fd15b87SRussell King struct pg_state {
491fd15b87SRussell King struct seq_file *seq;
501fd15b87SRussell King const struct addr_marker *marker;
511fd15b87SRussell King unsigned long start_address;
521fd15b87SRussell King unsigned level;
531fd15b87SRussell King u64 current_prot;
54a8e53c15SJinbum Park bool check_wx;
55a8e53c15SJinbum Park unsigned long wx_pages;
56dd59f974SKees Cook const char *current_domain;
571fd15b87SRussell King };
581fd15b87SRussell King
591fd15b87SRussell King struct prot_bits {
601fd15b87SRussell King u64 mask;
611fd15b87SRussell King u64 val;
621fd15b87SRussell King const char *set;
631fd15b87SRussell King const char *clear;
64a8e53c15SJinbum Park bool ro_bit;
65a8e53c15SJinbum Park bool nx_bit;
661fd15b87SRussell King };
671fd15b87SRussell King
681fd15b87SRussell King static const struct prot_bits pte_bits[] = {
691fd15b87SRussell King {
701fd15b87SRussell King .mask = L_PTE_USER,
711fd15b87SRussell King .val = L_PTE_USER,
721fd15b87SRussell King .set = "USR",
731fd15b87SRussell King .clear = " ",
741fd15b87SRussell King }, {
751fd15b87SRussell King .mask = L_PTE_RDONLY,
761fd15b87SRussell King .val = L_PTE_RDONLY,
771fd15b87SRussell King .set = "ro",
781fd15b87SRussell King .clear = "RW",
79a8e53c15SJinbum Park .ro_bit = true,
801fd15b87SRussell King }, {
811fd15b87SRussell King .mask = L_PTE_XN,
821fd15b87SRussell King .val = L_PTE_XN,
831fd15b87SRussell King .set = "NX",
841fd15b87SRussell King .clear = "x ",
85a8e53c15SJinbum Park .nx_bit = true,
861fd15b87SRussell King }, {
871fd15b87SRussell King .mask = L_PTE_SHARED,
881fd15b87SRussell King .val = L_PTE_SHARED,
891fd15b87SRussell King .set = "SHD",
901fd15b87SRussell King .clear = " ",
911fd15b87SRussell King }, {
921fd15b87SRussell King .mask = L_PTE_MT_MASK,
931fd15b87SRussell King .val = L_PTE_MT_UNCACHED,
941fd15b87SRussell King .set = "SO/UNCACHED",
951fd15b87SRussell King }, {
961fd15b87SRussell King .mask = L_PTE_MT_MASK,
971fd15b87SRussell King .val = L_PTE_MT_BUFFERABLE,
981fd15b87SRussell King .set = "MEM/BUFFERABLE/WC",
991fd15b87SRussell King }, {
1001fd15b87SRussell King .mask = L_PTE_MT_MASK,
1011fd15b87SRussell King .val = L_PTE_MT_WRITETHROUGH,
1021fd15b87SRussell King .set = "MEM/CACHED/WT",
1031fd15b87SRussell King }, {
1041fd15b87SRussell King .mask = L_PTE_MT_MASK,
1051fd15b87SRussell King .val = L_PTE_MT_WRITEBACK,
1061fd15b87SRussell King .set = "MEM/CACHED/WBRA",
1071fd15b87SRussell King #ifndef CONFIG_ARM_LPAE
1081fd15b87SRussell King }, {
1091fd15b87SRussell King .mask = L_PTE_MT_MASK,
1101fd15b87SRussell King .val = L_PTE_MT_MINICACHE,
1111fd15b87SRussell King .set = "MEM/MINICACHE",
1121fd15b87SRussell King #endif
1131fd15b87SRussell King }, {
1141fd15b87SRussell King .mask = L_PTE_MT_MASK,
1151fd15b87SRussell King .val = L_PTE_MT_WRITEALLOC,
1161fd15b87SRussell King .set = "MEM/CACHED/WBWA",
1171fd15b87SRussell King }, {
1181fd15b87SRussell King .mask = L_PTE_MT_MASK,
1191fd15b87SRussell King .val = L_PTE_MT_DEV_SHARED,
1201fd15b87SRussell King .set = "DEV/SHARED",
1211fd15b87SRussell King #ifndef CONFIG_ARM_LPAE
1221fd15b87SRussell King }, {
1231fd15b87SRussell King .mask = L_PTE_MT_MASK,
1241fd15b87SRussell King .val = L_PTE_MT_DEV_NONSHARED,
1251fd15b87SRussell King .set = "DEV/NONSHARED",
1261fd15b87SRussell King #endif
1271fd15b87SRussell King }, {
1281fd15b87SRussell King .mask = L_PTE_MT_MASK,
1291fd15b87SRussell King .val = L_PTE_MT_DEV_WC,
1301fd15b87SRussell King .set = "DEV/WC",
1311fd15b87SRussell King }, {
1321fd15b87SRussell King .mask = L_PTE_MT_MASK,
1331fd15b87SRussell King .val = L_PTE_MT_DEV_CACHED,
1341fd15b87SRussell King .set = "DEV/CACHED",
1351fd15b87SRussell King },
1361fd15b87SRussell King };
1371fd15b87SRussell King
1381fd15b87SRussell King static const struct prot_bits section_bits[] = {
139fff00db8SKees Cook #ifdef CONFIG_ARM_LPAE
140fff00db8SKees Cook {
141fff00db8SKees Cook .mask = PMD_SECT_USER,
142fff00db8SKees Cook .val = PMD_SECT_USER,
143fff00db8SKees Cook .set = "USR",
144fff00db8SKees Cook }, {
1453b0c0c92SPhilip Derrin .mask = L_PMD_SECT_RDONLY | PMD_SECT_AP2,
1463b0c0c92SPhilip Derrin .val = L_PMD_SECT_RDONLY | PMD_SECT_AP2,
147fff00db8SKees Cook .set = "ro",
148fff00db8SKees Cook .clear = "RW",
149a8e53c15SJinbum Park .ro_bit = true,
150fff00db8SKees Cook #elif __LINUX_ARM_ARCH__ >= 6
151fff00db8SKees Cook {
152fff00db8SKees Cook .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
153fff00db8SKees Cook .val = PMD_SECT_APX | PMD_SECT_AP_WRITE,
154fff00db8SKees Cook .set = " ro",
155a8e53c15SJinbum Park .ro_bit = true,
156fff00db8SKees Cook }, {
157fff00db8SKees Cook .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
158fff00db8SKees Cook .val = PMD_SECT_AP_WRITE,
159fff00db8SKees Cook .set = " RW",
160fff00db8SKees Cook }, {
161fff00db8SKees Cook .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
162fff00db8SKees Cook .val = PMD_SECT_AP_READ,
163fff00db8SKees Cook .set = "USR ro",
164fff00db8SKees Cook }, {
165fff00db8SKees Cook .mask = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
166fff00db8SKees Cook .val = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
167fff00db8SKees Cook .set = "USR RW",
168fff00db8SKees Cook #else /* ARMv4/ARMv5 */
1691fd15b87SRussell King /* These are approximate */
1701fd15b87SRussell King {
1711fd15b87SRussell King .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
1721fd15b87SRussell King .val = 0,
1731fd15b87SRussell King .set = " ro",
174a8e53c15SJinbum Park .ro_bit = true,
1751fd15b87SRussell King }, {
1761fd15b87SRussell King .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
1771fd15b87SRussell King .val = PMD_SECT_AP_WRITE,
1781fd15b87SRussell King .set = " RW",
1791fd15b87SRussell King }, {
1801fd15b87SRussell King .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
1811fd15b87SRussell King .val = PMD_SECT_AP_READ,
1821fd15b87SRussell King .set = "USR ro",
1831fd15b87SRussell King }, {
1841fd15b87SRussell King .mask = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
1851fd15b87SRussell King .val = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
1861fd15b87SRussell King .set = "USR RW",
1871fd15b87SRussell King #endif
1881fd15b87SRussell King }, {
1891fd15b87SRussell King .mask = PMD_SECT_XN,
1901fd15b87SRussell King .val = PMD_SECT_XN,
1911fd15b87SRussell King .set = "NX",
1921fd15b87SRussell King .clear = "x ",
193a8e53c15SJinbum Park .nx_bit = true,
1941fd15b87SRussell King }, {
1951fd15b87SRussell King .mask = PMD_SECT_S,
1961fd15b87SRussell King .val = PMD_SECT_S,
1971fd15b87SRussell King .set = "SHD",
1981fd15b87SRussell King .clear = " ",
1991fd15b87SRussell King },
2001fd15b87SRussell King };
2011fd15b87SRussell King
2021fd15b87SRussell King struct pg_level {
203e66372ecSWang Kefeng const char *name;
2041fd15b87SRussell King const struct prot_bits *bits;
2051fd15b87SRussell King size_t num;
2061fd15b87SRussell King u64 mask;
207a8e53c15SJinbum Park const struct prot_bits *ro_bit;
208a8e53c15SJinbum Park const struct prot_bits *nx_bit;
2091fd15b87SRussell King };
2101fd15b87SRussell King
2111fd15b87SRussell King static struct pg_level pg_level[] = {
2121fd15b87SRussell King {
2131fd15b87SRussell King }, { /* pgd */
21484e6ffb2SMike Rapoport }, { /* p4d */
2151fd15b87SRussell King }, { /* pud */
2161fd15b87SRussell King }, { /* pmd */
217e66372ecSWang Kefeng .name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
2181fd15b87SRussell King .bits = section_bits,
2191fd15b87SRussell King .num = ARRAY_SIZE(section_bits),
2201fd15b87SRussell King }, { /* pte */
221e66372ecSWang Kefeng .name = "PTE",
2221fd15b87SRussell King .bits = pte_bits,
2231fd15b87SRussell King .num = ARRAY_SIZE(pte_bits),
2241fd15b87SRussell King },
2251fd15b87SRussell King };
2261fd15b87SRussell King
dump_prot(struct pg_state * st,const struct prot_bits * bits,size_t num)2271fd15b87SRussell King static void dump_prot(struct pg_state *st, const struct prot_bits *bits, size_t num)
2281fd15b87SRussell King {
2291fd15b87SRussell King unsigned i;
2301fd15b87SRussell King
2311fd15b87SRussell King for (i = 0; i < num; i++, bits++) {
2321fd15b87SRussell King const char *s;
2331fd15b87SRussell King
2341fd15b87SRussell King if ((st->current_prot & bits->mask) == bits->val)
2351fd15b87SRussell King s = bits->set;
2361fd15b87SRussell King else
2371fd15b87SRussell King s = bits->clear;
2381fd15b87SRussell King
2391fd15b87SRussell King if (s)
240d02ca6d7SJinbum Park pt_dump_seq_printf(st->seq, " %s", s);
2411fd15b87SRussell King }
2421fd15b87SRussell King }
2431fd15b87SRussell King
note_prot_wx(struct pg_state * st,unsigned long addr)244a8e53c15SJinbum Park static void note_prot_wx(struct pg_state *st, unsigned long addr)
245a8e53c15SJinbum Park {
246a8e53c15SJinbum Park if (!st->check_wx)
247a8e53c15SJinbum Park return;
248a8e53c15SJinbum Park if ((st->current_prot & pg_level[st->level].ro_bit->mask) ==
249a8e53c15SJinbum Park pg_level[st->level].ro_bit->val)
250a8e53c15SJinbum Park return;
251a8e53c15SJinbum Park if ((st->current_prot & pg_level[st->level].nx_bit->mask) ==
252a8e53c15SJinbum Park pg_level[st->level].nx_bit->val)
253a8e53c15SJinbum Park return;
254a8e53c15SJinbum Park
255a8e53c15SJinbum Park WARN_ONCE(1, "arm/mm: Found insecure W+X mapping at address %pS\n",
256a8e53c15SJinbum Park (void *)st->start_address);
257a8e53c15SJinbum Park
258a8e53c15SJinbum Park st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
259a8e53c15SJinbum Park }
260a8e53c15SJinbum Park
note_page(struct pg_state * st,unsigned long addr,unsigned int level,u64 val,const char * domain)261dd59f974SKees Cook static void note_page(struct pg_state *st, unsigned long addr,
262dd59f974SKees Cook unsigned int level, u64 val, const char *domain)
2631fd15b87SRussell King {
2641fd15b87SRussell King static const char units[] = "KMGTPE";
2651fd15b87SRussell King u64 prot = val & pg_level[level].mask;
2661fd15b87SRussell King
2671fd15b87SRussell King if (!st->level) {
2681fd15b87SRussell King st->level = level;
2691fd15b87SRussell King st->current_prot = prot;
270dd59f974SKees Cook st->current_domain = domain;
271d02ca6d7SJinbum Park pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
2721fd15b87SRussell King } else if (prot != st->current_prot || level != st->level ||
273dd59f974SKees Cook domain != st->current_domain ||
2741fd15b87SRussell King addr >= st->marker[1].start_address) {
2751fd15b87SRussell King const char *unit = units;
2761fd15b87SRussell King unsigned long delta;
2771fd15b87SRussell King
2781fd15b87SRussell King if (st->current_prot) {
279a8e53c15SJinbum Park note_prot_wx(st, addr);
280d02ca6d7SJinbum Park pt_dump_seq_printf(st->seq, "0x%08lx-0x%08lx ",
2811fd15b87SRussell King st->start_address, addr);
2821fd15b87SRussell King
2831fd15b87SRussell King delta = (addr - st->start_address) >> 10;
2841fd15b87SRussell King while (!(delta & 1023) && unit[1]) {
2851fd15b87SRussell King delta >>= 10;
2861fd15b87SRussell King unit++;
2871fd15b87SRussell King }
288e66372ecSWang Kefeng pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
289e66372ecSWang Kefeng pg_level[st->level].name);
290dd59f974SKees Cook if (st->current_domain)
291d02ca6d7SJinbum Park pt_dump_seq_printf(st->seq, " %s",
292d02ca6d7SJinbum Park st->current_domain);
2931fd15b87SRussell King if (pg_level[st->level].bits)
2941fd15b87SRussell King dump_prot(st, pg_level[st->level].bits, pg_level[st->level].num);
295d02ca6d7SJinbum Park pt_dump_seq_printf(st->seq, "\n");
2961fd15b87SRussell King }
2971fd15b87SRussell King
2981fd15b87SRussell King if (addr >= st->marker[1].start_address) {
2991fd15b87SRussell King st->marker++;
300d02ca6d7SJinbum Park pt_dump_seq_printf(st->seq, "---[ %s ]---\n",
301d02ca6d7SJinbum Park st->marker->name);
3021fd15b87SRussell King }
3031fd15b87SRussell King st->start_address = addr;
3041fd15b87SRussell King st->current_prot = prot;
305dd59f974SKees Cook st->current_domain = domain;
3061fd15b87SRussell King st->level = level;
3071fd15b87SRussell King }
3081fd15b87SRussell King }
3091fd15b87SRussell King
walk_pte(struct pg_state * st,pmd_t * pmd,unsigned long start,const char * domain)310dd59f974SKees Cook static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start,
311dd59f974SKees Cook const char *domain)
3121fd15b87SRussell King {
3131fd15b87SRussell King pte_t *pte = pte_offset_kernel(pmd, 0);
3141fd15b87SRussell King unsigned long addr;
3151fd15b87SRussell King unsigned i;
3161fd15b87SRussell King
3171fd15b87SRussell King for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
3181fd15b87SRussell King addr = start + i * PAGE_SIZE;
31984e6ffb2SMike Rapoport note_page(st, addr, 5, pte_val(*pte), domain);
3201fd15b87SRussell King }
3211fd15b87SRussell King }
3221fd15b87SRussell King
get_domain_name(pmd_t * pmd)323dd59f974SKees Cook static const char *get_domain_name(pmd_t *pmd)
324dd59f974SKees Cook {
325dd59f974SKees Cook #ifndef CONFIG_ARM_LPAE
326dd59f974SKees Cook switch (pmd_val(*pmd) & PMD_DOMAIN_MASK) {
327dd59f974SKees Cook case PMD_DOMAIN(DOMAIN_KERNEL):
328dd59f974SKees Cook return "KERNEL ";
329dd59f974SKees Cook case PMD_DOMAIN(DOMAIN_USER):
330dd59f974SKees Cook return "USER ";
331dd59f974SKees Cook case PMD_DOMAIN(DOMAIN_IO):
332dd59f974SKees Cook return "IO ";
333dd59f974SKees Cook case PMD_DOMAIN(DOMAIN_VECTORS):
334dd59f974SKees Cook return "VECTORS";
335dd59f974SKees Cook default:
336dd59f974SKees Cook return "unknown";
337dd59f974SKees Cook }
338dd59f974SKees Cook #endif
339dd59f974SKees Cook return NULL;
340dd59f974SKees Cook }
341dd59f974SKees Cook
walk_pmd(struct pg_state * st,pud_t * pud,unsigned long start)3421fd15b87SRussell King static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
3431fd15b87SRussell King {
3441fd15b87SRussell King pmd_t *pmd = pmd_offset(pud, 0);
3451fd15b87SRussell King unsigned long addr;
3461fd15b87SRussell King unsigned i;
347dd59f974SKees Cook const char *domain;
3481fd15b87SRussell King
3491fd15b87SRussell King for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
3501fd15b87SRussell King addr = start + i * PMD_SIZE;
351dd59f974SKees Cook domain = get_domain_name(pmd);
3521fd15b87SRussell King if (pmd_none(*pmd) || pmd_large(*pmd) || !pmd_present(*pmd))
3532ccd19b3SWang Kefeng note_page(st, addr, 4, pmd_val(*pmd), domain);
3541fd15b87SRussell King else
355dd59f974SKees Cook walk_pte(st, pmd, addr, domain);
356cd91b2feSKees Cook
357dd59f974SKees Cook if (SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])) {
358dd59f974SKees Cook addr += SECTION_SIZE;
359dd59f974SKees Cook pmd++;
360dd59f974SKees Cook domain = get_domain_name(pmd);
36184e6ffb2SMike Rapoport note_page(st, addr, 4, pmd_val(*pmd), domain);
362dd59f974SKees Cook }
3631fd15b87SRussell King }
3641fd15b87SRussell King }
3651fd15b87SRussell King
walk_pud(struct pg_state * st,p4d_t * p4d,unsigned long start)36684e6ffb2SMike Rapoport static void walk_pud(struct pg_state *st, p4d_t *p4d, unsigned long start)
3671fd15b87SRussell King {
36884e6ffb2SMike Rapoport pud_t *pud = pud_offset(p4d, 0);
3691fd15b87SRussell King unsigned long addr;
3701fd15b87SRussell King unsigned i;
3711fd15b87SRussell King
3721fd15b87SRussell King for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
3731fd15b87SRussell King addr = start + i * PUD_SIZE;
3741fd15b87SRussell King if (!pud_none(*pud)) {
3751fd15b87SRussell King walk_pmd(st, pud, addr);
3761fd15b87SRussell King } else {
37784e6ffb2SMike Rapoport note_page(st, addr, 3, pud_val(*pud), NULL);
37884e6ffb2SMike Rapoport }
37984e6ffb2SMike Rapoport }
38084e6ffb2SMike Rapoport }
38184e6ffb2SMike Rapoport
walk_p4d(struct pg_state * st,pgd_t * pgd,unsigned long start)38284e6ffb2SMike Rapoport static void walk_p4d(struct pg_state *st, pgd_t *pgd, unsigned long start)
38384e6ffb2SMike Rapoport {
38484e6ffb2SMike Rapoport p4d_t *p4d = p4d_offset(pgd, 0);
38584e6ffb2SMike Rapoport unsigned long addr;
38684e6ffb2SMike Rapoport unsigned i;
38784e6ffb2SMike Rapoport
38884e6ffb2SMike Rapoport for (i = 0; i < PTRS_PER_P4D; i++, p4d++) {
38984e6ffb2SMike Rapoport addr = start + i * P4D_SIZE;
39084e6ffb2SMike Rapoport if (!p4d_none(*p4d)) {
39184e6ffb2SMike Rapoport walk_pud(st, p4d, addr);
39284e6ffb2SMike Rapoport } else {
39384e6ffb2SMike Rapoport note_page(st, addr, 2, p4d_val(*p4d), NULL);
3941fd15b87SRussell King }
3951fd15b87SRussell King }
3961fd15b87SRussell King }
3971fd15b87SRussell King
walk_pgd(struct pg_state * st,struct mm_struct * mm,unsigned long start)3984fb69cc4SJinbum Park static void walk_pgd(struct pg_state *st, struct mm_struct *mm,
3994fb69cc4SJinbum Park unsigned long start)
4001fd15b87SRussell King {
4014fb69cc4SJinbum Park pgd_t *pgd = pgd_offset(mm, 0UL);
402cca547e9SMark Rutland unsigned i;
4034fb69cc4SJinbum Park unsigned long addr;
4041fd15b87SRussell King
405cca547e9SMark Rutland for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
4064fb69cc4SJinbum Park addr = start + i * PGDIR_SIZE;
4071fd15b87SRussell King if (!pgd_none(*pgd)) {
40884e6ffb2SMike Rapoport walk_p4d(st, pgd, addr);
4091fd15b87SRussell King } else {
4104fb69cc4SJinbum Park note_page(st, addr, 1, pgd_val(*pgd), NULL);
4114fb69cc4SJinbum Park }
4121fd15b87SRussell King }
4131fd15b87SRussell King }
4141fd15b87SRussell King
ptdump_walk_pgd(struct seq_file * m,struct ptdump_info * info)4154fb69cc4SJinbum Park void ptdump_walk_pgd(struct seq_file *m, struct ptdump_info *info)
4164fb69cc4SJinbum Park {
4174fb69cc4SJinbum Park struct pg_state st = {
4184fb69cc4SJinbum Park .seq = m,
4194fb69cc4SJinbum Park .marker = info->markers,
420a8e53c15SJinbum Park .check_wx = false,
4214fb69cc4SJinbum Park };
4224fb69cc4SJinbum Park
4234fb69cc4SJinbum Park walk_pgd(&st, info->mm, info->base_addr);
424dd59f974SKees Cook note_page(&st, 0, 0, 0, NULL);
4251fd15b87SRussell King }
4261fd15b87SRussell King
ptdump_initialize(void)427a5e8acd9SJisheng Zhang (syna) static void __init ptdump_initialize(void)
4281fd15b87SRussell King {
4291fd15b87SRussell King unsigned i, j;
4301fd15b87SRussell King
4311fd15b87SRussell King for (i = 0; i < ARRAY_SIZE(pg_level); i++)
4321fd15b87SRussell King if (pg_level[i].bits)
433a8e53c15SJinbum Park for (j = 0; j < pg_level[i].num; j++) {
4341fd15b87SRussell King pg_level[i].mask |= pg_level[i].bits[j].mask;
435a8e53c15SJinbum Park if (pg_level[i].bits[j].ro_bit)
436a8e53c15SJinbum Park pg_level[i].ro_bit = &pg_level[i].bits[j];
437a8e53c15SJinbum Park if (pg_level[i].bits[j].nx_bit)
438a8e53c15SJinbum Park pg_level[i].nx_bit = &pg_level[i].bits[j];
439a8e53c15SJinbum Park }
440b53a9edcSHailong Liu #ifdef CONFIG_KASAN
441b53a9edcSHailong Liu address_markers[4].start_address = VMALLOC_START;
442b53a9edcSHailong Liu #else
4431fd15b87SRussell King address_markers[2].start_address = VMALLOC_START;
444b53a9edcSHailong Liu #endif
4454fb69cc4SJinbum Park }
4461fd15b87SRussell King
4474fb69cc4SJinbum Park static struct ptdump_info kernel_ptdump_info = {
4484fb69cc4SJinbum Park .mm = &init_mm,
4494fb69cc4SJinbum Park .markers = address_markers,
4504fb69cc4SJinbum Park .base_addr = 0,
4514fb69cc4SJinbum Park };
4524fb69cc4SJinbum Park
ptdump_check_wx(void)453a8e53c15SJinbum Park void ptdump_check_wx(void)
454a8e53c15SJinbum Park {
455a8e53c15SJinbum Park struct pg_state st = {
456a8e53c15SJinbum Park .seq = NULL,
457a8e53c15SJinbum Park .marker = (struct addr_marker[]) {
458a8e53c15SJinbum Park { 0, NULL},
459a8e53c15SJinbum Park { -1, NULL},
460a8e53c15SJinbum Park },
461a8e53c15SJinbum Park .check_wx = true,
462a8e53c15SJinbum Park };
463a8e53c15SJinbum Park
464a8e53c15SJinbum Park walk_pgd(&st, &init_mm, 0);
465a8e53c15SJinbum Park note_page(&st, 0, 0, 0, NULL);
466a8e53c15SJinbum Park if (st.wx_pages)
467a8e53c15SJinbum Park pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found\n",
468a8e53c15SJinbum Park st.wx_pages);
469a8e53c15SJinbum Park else
470a8e53c15SJinbum Park pr_info("Checked W+X mappings: passed, no W+X pages found\n");
471a8e53c15SJinbum Park }
472a8e53c15SJinbum Park
ptdump_init(void)473a5e8acd9SJisheng Zhang (syna) static int __init ptdump_init(void)
4744fb69cc4SJinbum Park {
4754fb69cc4SJinbum Park ptdump_initialize();
476db0487abSGreg Kroah-Hartman ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
477db0487abSGreg Kroah-Hartman return 0;
4781fd15b87SRussell King }
4791fd15b87SRussell King __initcall(ptdump_init);
480