xref: /linux/arch/arm/mm/dump.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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>
18a9ff6961SLinus 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);
352*2f709f7bSPeter Xu 		if (pmd_none(*pmd) || pmd_leaf(*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 
357*2f709f7bSPeter Xu 		if (SECTION_SIZE < PMD_SIZE && pmd_leaf(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