162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Debug helper to dump the current kernel pagetables of the system
462306a36Sopenharmony_ci * so that we can see what the various memory ranges are set to.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) Copyright 2008 Intel Corporation
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Arjan van de Ven <arjan@linux.intel.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/debugfs.h>
1262306a36Sopenharmony_ci#include <linux/kasan.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/sched.h>
1662306a36Sopenharmony_ci#include <linux/seq_file.h>
1762306a36Sopenharmony_ci#include <linux/highmem.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/ptdump.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <asm/e820/types.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * The dumper groups pagetable entries of the same type into one, and for
2562306a36Sopenharmony_ci * that it needs to keep some state when walking, and flush this state
2662306a36Sopenharmony_ci * when a "break" in the continuity is found.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistruct pg_state {
2962306a36Sopenharmony_ci	struct ptdump_state ptdump;
3062306a36Sopenharmony_ci	int level;
3162306a36Sopenharmony_ci	pgprotval_t current_prot;
3262306a36Sopenharmony_ci	pgprotval_t effective_prot;
3362306a36Sopenharmony_ci	pgprotval_t prot_levels[5];
3462306a36Sopenharmony_ci	unsigned long start_address;
3562306a36Sopenharmony_ci	const struct addr_marker *marker;
3662306a36Sopenharmony_ci	unsigned long lines;
3762306a36Sopenharmony_ci	bool to_dmesg;
3862306a36Sopenharmony_ci	bool check_wx;
3962306a36Sopenharmony_ci	unsigned long wx_pages;
4062306a36Sopenharmony_ci	struct seq_file *seq;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct addr_marker {
4462306a36Sopenharmony_ci	unsigned long start_address;
4562306a36Sopenharmony_ci	const char *name;
4662306a36Sopenharmony_ci	unsigned long max_lines;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Address space markers hints */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#ifdef CONFIG_X86_64
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cienum address_markers_idx {
5462306a36Sopenharmony_ci	USER_SPACE_NR = 0,
5562306a36Sopenharmony_ci	KERNEL_SPACE_NR,
5662306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL
5762306a36Sopenharmony_ci	LDT_NR,
5862306a36Sopenharmony_ci#endif
5962306a36Sopenharmony_ci	LOW_KERNEL_NR,
6062306a36Sopenharmony_ci	VMALLOC_START_NR,
6162306a36Sopenharmony_ci	VMEMMAP_START_NR,
6262306a36Sopenharmony_ci#ifdef CONFIG_KASAN
6362306a36Sopenharmony_ci	KASAN_SHADOW_START_NR,
6462306a36Sopenharmony_ci	KASAN_SHADOW_END_NR,
6562306a36Sopenharmony_ci#endif
6662306a36Sopenharmony_ci	CPU_ENTRY_AREA_NR,
6762306a36Sopenharmony_ci#ifdef CONFIG_X86_ESPFIX64
6862306a36Sopenharmony_ci	ESPFIX_START_NR,
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_ci#ifdef CONFIG_EFI
7162306a36Sopenharmony_ci	EFI_END_NR,
7262306a36Sopenharmony_ci#endif
7362306a36Sopenharmony_ci	HIGH_KERNEL_NR,
7462306a36Sopenharmony_ci	MODULES_VADDR_NR,
7562306a36Sopenharmony_ci	MODULES_END_NR,
7662306a36Sopenharmony_ci	FIXADDR_START_NR,
7762306a36Sopenharmony_ci	END_OF_SPACE_NR,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic struct addr_marker address_markers[] = {
8162306a36Sopenharmony_ci	[USER_SPACE_NR]		= { 0,			"User Space" },
8262306a36Sopenharmony_ci	[KERNEL_SPACE_NR]	= { (1UL << 63),	"Kernel Space" },
8362306a36Sopenharmony_ci	[LOW_KERNEL_NR]		= { 0UL,		"Low Kernel Mapping" },
8462306a36Sopenharmony_ci	[VMALLOC_START_NR]	= { 0UL,		"vmalloc() Area" },
8562306a36Sopenharmony_ci	[VMEMMAP_START_NR]	= { 0UL,		"Vmemmap" },
8662306a36Sopenharmony_ci#ifdef CONFIG_KASAN
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * These fields get initialized with the (dynamic)
8962306a36Sopenharmony_ci	 * KASAN_SHADOW_{START,END} values in pt_dump_init().
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	[KASAN_SHADOW_START_NR]	= { 0UL,		"KASAN shadow" },
9262306a36Sopenharmony_ci	[KASAN_SHADOW_END_NR]	= { 0UL,		"KASAN shadow end" },
9362306a36Sopenharmony_ci#endif
9462306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL
9562306a36Sopenharmony_ci	[LDT_NR]		= { 0UL,		"LDT remap" },
9662306a36Sopenharmony_ci#endif
9762306a36Sopenharmony_ci	[CPU_ENTRY_AREA_NR]	= { CPU_ENTRY_AREA_BASE,"CPU entry Area" },
9862306a36Sopenharmony_ci#ifdef CONFIG_X86_ESPFIX64
9962306a36Sopenharmony_ci	[ESPFIX_START_NR]	= { ESPFIX_BASE_ADDR,	"ESPfix Area", 16 },
10062306a36Sopenharmony_ci#endif
10162306a36Sopenharmony_ci#ifdef CONFIG_EFI
10262306a36Sopenharmony_ci	[EFI_END_NR]		= { EFI_VA_END,		"EFI Runtime Services" },
10362306a36Sopenharmony_ci#endif
10462306a36Sopenharmony_ci	[HIGH_KERNEL_NR]	= { __START_KERNEL_map,	"High Kernel Mapping" },
10562306a36Sopenharmony_ci	[MODULES_VADDR_NR]	= { MODULES_VADDR,	"Modules" },
10662306a36Sopenharmony_ci	[MODULES_END_NR]	= { MODULES_END,	"End Modules" },
10762306a36Sopenharmony_ci	[FIXADDR_START_NR]	= { FIXADDR_START,	"Fixmap Area" },
10862306a36Sopenharmony_ci	[END_OF_SPACE_NR]	= { -1,			NULL }
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define INIT_PGD	((pgd_t *) &init_top_pgt)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#else /* CONFIG_X86_64 */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cienum address_markers_idx {
11662306a36Sopenharmony_ci	USER_SPACE_NR = 0,
11762306a36Sopenharmony_ci	KERNEL_SPACE_NR,
11862306a36Sopenharmony_ci	VMALLOC_START_NR,
11962306a36Sopenharmony_ci	VMALLOC_END_NR,
12062306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM
12162306a36Sopenharmony_ci	PKMAP_BASE_NR,
12262306a36Sopenharmony_ci#endif
12362306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL
12462306a36Sopenharmony_ci	LDT_NR,
12562306a36Sopenharmony_ci#endif
12662306a36Sopenharmony_ci	CPU_ENTRY_AREA_NR,
12762306a36Sopenharmony_ci	FIXADDR_START_NR,
12862306a36Sopenharmony_ci	END_OF_SPACE_NR,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic struct addr_marker address_markers[] = {
13262306a36Sopenharmony_ci	[USER_SPACE_NR]		= { 0,			"User Space" },
13362306a36Sopenharmony_ci	[KERNEL_SPACE_NR]	= { PAGE_OFFSET,	"Kernel Mapping" },
13462306a36Sopenharmony_ci	[VMALLOC_START_NR]	= { 0UL,		"vmalloc() Area" },
13562306a36Sopenharmony_ci	[VMALLOC_END_NR]	= { 0UL,		"vmalloc() End" },
13662306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM
13762306a36Sopenharmony_ci	[PKMAP_BASE_NR]		= { 0UL,		"Persistent kmap() Area" },
13862306a36Sopenharmony_ci#endif
13962306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL
14062306a36Sopenharmony_ci	[LDT_NR]		= { 0UL,		"LDT remap" },
14162306a36Sopenharmony_ci#endif
14262306a36Sopenharmony_ci	[CPU_ENTRY_AREA_NR]	= { 0UL,		"CPU entry area" },
14362306a36Sopenharmony_ci	[FIXADDR_START_NR]	= { 0UL,		"Fixmap area" },
14462306a36Sopenharmony_ci	[END_OF_SPACE_NR]	= { -1,			NULL }
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define INIT_PGD	(swapper_pg_dir)
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci#endif /* !CONFIG_X86_64 */
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* Multipliers for offsets within the PTEs */
15262306a36Sopenharmony_ci#define PTE_LEVEL_MULT (PAGE_SIZE)
15362306a36Sopenharmony_ci#define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT)
15462306a36Sopenharmony_ci#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT)
15562306a36Sopenharmony_ci#define P4D_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT)
15662306a36Sopenharmony_ci#define PGD_LEVEL_MULT (PTRS_PER_P4D * P4D_LEVEL_MULT)
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci#define pt_dump_seq_printf(m, to_dmesg, fmt, args...)		\
15962306a36Sopenharmony_ci({								\
16062306a36Sopenharmony_ci	if (to_dmesg)					\
16162306a36Sopenharmony_ci		printk(KERN_INFO fmt, ##args);			\
16262306a36Sopenharmony_ci	else							\
16362306a36Sopenharmony_ci		if (m)						\
16462306a36Sopenharmony_ci			seq_printf(m, fmt, ##args);		\
16562306a36Sopenharmony_ci})
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define pt_dump_cont_printf(m, to_dmesg, fmt, args...)		\
16862306a36Sopenharmony_ci({								\
16962306a36Sopenharmony_ci	if (to_dmesg)					\
17062306a36Sopenharmony_ci		printk(KERN_CONT fmt, ##args);			\
17162306a36Sopenharmony_ci	else							\
17262306a36Sopenharmony_ci		if (m)						\
17362306a36Sopenharmony_ci			seq_printf(m, fmt, ##args);		\
17462306a36Sopenharmony_ci})
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/*
17762306a36Sopenharmony_ci * Print a readable form of a pgprot_t to the seq_file
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic void printk_prot(struct seq_file *m, pgprotval_t pr, int level, bool dmsg)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	static const char * const level_name[] =
18262306a36Sopenharmony_ci		{ "pgd", "p4d", "pud", "pmd", "pte" };
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!(pr & _PAGE_PRESENT)) {
18562306a36Sopenharmony_ci		/* Not present */
18662306a36Sopenharmony_ci		pt_dump_cont_printf(m, dmsg, "                              ");
18762306a36Sopenharmony_ci	} else {
18862306a36Sopenharmony_ci		if (pr & _PAGE_USER)
18962306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "USR ");
19062306a36Sopenharmony_ci		else
19162306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "    ");
19262306a36Sopenharmony_ci		if (pr & _PAGE_RW)
19362306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "RW ");
19462306a36Sopenharmony_ci		else
19562306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "ro ");
19662306a36Sopenharmony_ci		if (pr & _PAGE_PWT)
19762306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "PWT ");
19862306a36Sopenharmony_ci		else
19962306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "    ");
20062306a36Sopenharmony_ci		if (pr & _PAGE_PCD)
20162306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "PCD ");
20262306a36Sopenharmony_ci		else
20362306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "    ");
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		/* Bit 7 has a different meaning on level 3 vs 4 */
20662306a36Sopenharmony_ci		if (level <= 3 && pr & _PAGE_PSE)
20762306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "PSE ");
20862306a36Sopenharmony_ci		else
20962306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "    ");
21062306a36Sopenharmony_ci		if ((level == 4 && pr & _PAGE_PAT) ||
21162306a36Sopenharmony_ci		    ((level == 3 || level == 2) && pr & _PAGE_PAT_LARGE))
21262306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "PAT ");
21362306a36Sopenharmony_ci		else
21462306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "    ");
21562306a36Sopenharmony_ci		if (pr & _PAGE_GLOBAL)
21662306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "GLB ");
21762306a36Sopenharmony_ci		else
21862306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "    ");
21962306a36Sopenharmony_ci		if (pr & _PAGE_NX)
22062306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "NX ");
22162306a36Sopenharmony_ci		else
22262306a36Sopenharmony_ci			pt_dump_cont_printf(m, dmsg, "x  ");
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void note_wx(struct pg_state *st, unsigned long addr)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	unsigned long npages;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	npages = (addr - st->start_address) / PAGE_SIZE;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci#ifdef CONFIG_PCI_BIOS
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * If PCI BIOS is enabled, the PCI BIOS area is forced to WX.
23662306a36Sopenharmony_ci	 * Inform about it, but avoid the warning.
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci	if (pcibios_enabled && st->start_address >= PAGE_OFFSET + BIOS_BEGIN &&
23962306a36Sopenharmony_ci	    addr <= PAGE_OFFSET + BIOS_END) {
24062306a36Sopenharmony_ci		pr_warn_once("x86/mm: PCI BIOS W+X mapping %lu pages\n", npages);
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci#endif
24462306a36Sopenharmony_ci	/* Account the WX pages */
24562306a36Sopenharmony_ci	st->wx_pages += npages;
24662306a36Sopenharmony_ci	WARN_ONCE(__supported_pte_mask & _PAGE_NX,
24762306a36Sopenharmony_ci		  "x86/mm: Found insecure W+X mapping at address %pS\n",
24862306a36Sopenharmony_ci		  (void *)st->start_address);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void effective_prot(struct ptdump_state *pt_st, int level, u64 val)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
25462306a36Sopenharmony_ci	pgprotval_t prot = val & PTE_FLAGS_MASK;
25562306a36Sopenharmony_ci	pgprotval_t effective;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (level > 0) {
25862306a36Sopenharmony_ci		pgprotval_t higher_prot = st->prot_levels[level - 1];
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		effective = (higher_prot & prot & (_PAGE_USER | _PAGE_RW)) |
26162306a36Sopenharmony_ci			    ((higher_prot | prot) & _PAGE_NX);
26262306a36Sopenharmony_ci	} else {
26362306a36Sopenharmony_ci		effective = prot;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	st->prot_levels[level] = effective;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/*
27062306a36Sopenharmony_ci * This function gets called on a break in a continuous series
27162306a36Sopenharmony_ci * of PTE entries; the next one is different so we need to
27262306a36Sopenharmony_ci * print what we collected so far.
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cistatic void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
27562306a36Sopenharmony_ci		      u64 val)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
27862306a36Sopenharmony_ci	pgprotval_t new_prot, new_eff;
27962306a36Sopenharmony_ci	pgprotval_t cur, eff;
28062306a36Sopenharmony_ci	static const char units[] = "BKMGTPE";
28162306a36Sopenharmony_ci	struct seq_file *m = st->seq;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	new_prot = val & PTE_FLAGS_MASK;
28462306a36Sopenharmony_ci	if (!val)
28562306a36Sopenharmony_ci		new_eff = 0;
28662306a36Sopenharmony_ci	else
28762306a36Sopenharmony_ci		new_eff = st->prot_levels[level];
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/*
29062306a36Sopenharmony_ci	 * If we have a "break" in the series, we need to flush the state that
29162306a36Sopenharmony_ci	 * we have now. "break" is either changing perms, levels or
29262306a36Sopenharmony_ci	 * address space marker.
29362306a36Sopenharmony_ci	 */
29462306a36Sopenharmony_ci	cur = st->current_prot;
29562306a36Sopenharmony_ci	eff = st->effective_prot;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (st->level == -1) {
29862306a36Sopenharmony_ci		/* First entry */
29962306a36Sopenharmony_ci		st->current_prot = new_prot;
30062306a36Sopenharmony_ci		st->effective_prot = new_eff;
30162306a36Sopenharmony_ci		st->level = level;
30262306a36Sopenharmony_ci		st->marker = address_markers;
30362306a36Sopenharmony_ci		st->lines = 0;
30462306a36Sopenharmony_ci		pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
30562306a36Sopenharmony_ci				   st->marker->name);
30662306a36Sopenharmony_ci	} else if (new_prot != cur || new_eff != eff || level != st->level ||
30762306a36Sopenharmony_ci		   addr >= st->marker[1].start_address) {
30862306a36Sopenharmony_ci		const char *unit = units;
30962306a36Sopenharmony_ci		unsigned long delta;
31062306a36Sopenharmony_ci		int width = sizeof(unsigned long) * 2;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		if (st->check_wx && (eff & _PAGE_RW) && !(eff & _PAGE_NX))
31362306a36Sopenharmony_ci			note_wx(st, addr);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		/*
31662306a36Sopenharmony_ci		 * Now print the actual finished series
31762306a36Sopenharmony_ci		 */
31862306a36Sopenharmony_ci		if (!st->marker->max_lines ||
31962306a36Sopenharmony_ci		    st->lines < st->marker->max_lines) {
32062306a36Sopenharmony_ci			pt_dump_seq_printf(m, st->to_dmesg,
32162306a36Sopenharmony_ci					   "0x%0*lx-0x%0*lx   ",
32262306a36Sopenharmony_ci					   width, st->start_address,
32362306a36Sopenharmony_ci					   width, addr);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci			delta = addr - st->start_address;
32662306a36Sopenharmony_ci			while (!(delta & 1023) && unit[1]) {
32762306a36Sopenharmony_ci				delta >>= 10;
32862306a36Sopenharmony_ci				unit++;
32962306a36Sopenharmony_ci			}
33062306a36Sopenharmony_ci			pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ",
33162306a36Sopenharmony_ci					    delta, *unit);
33262306a36Sopenharmony_ci			printk_prot(m, st->current_prot, st->level,
33362306a36Sopenharmony_ci				    st->to_dmesg);
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci		st->lines++;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		/*
33862306a36Sopenharmony_ci		 * We print markers for special areas of address space,
33962306a36Sopenharmony_ci		 * such as the start of vmalloc space etc.
34062306a36Sopenharmony_ci		 * This helps in the interpretation.
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci		if (addr >= st->marker[1].start_address) {
34362306a36Sopenharmony_ci			if (st->marker->max_lines &&
34462306a36Sopenharmony_ci			    st->lines > st->marker->max_lines) {
34562306a36Sopenharmony_ci				unsigned long nskip =
34662306a36Sopenharmony_ci					st->lines - st->marker->max_lines;
34762306a36Sopenharmony_ci				pt_dump_seq_printf(m, st->to_dmesg,
34862306a36Sopenharmony_ci						   "... %lu entr%s skipped ... \n",
34962306a36Sopenharmony_ci						   nskip,
35062306a36Sopenharmony_ci						   nskip == 1 ? "y" : "ies");
35162306a36Sopenharmony_ci			}
35262306a36Sopenharmony_ci			st->marker++;
35362306a36Sopenharmony_ci			st->lines = 0;
35462306a36Sopenharmony_ci			pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
35562306a36Sopenharmony_ci					   st->marker->name);
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		st->start_address = addr;
35962306a36Sopenharmony_ci		st->current_prot = new_prot;
36062306a36Sopenharmony_ci		st->effective_prot = new_eff;
36162306a36Sopenharmony_ci		st->level = level;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void ptdump_walk_pgd_level_core(struct seq_file *m,
36662306a36Sopenharmony_ci				       struct mm_struct *mm, pgd_t *pgd,
36762306a36Sopenharmony_ci				       bool checkwx, bool dmesg)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	const struct ptdump_range ptdump_ranges[] = {
37062306a36Sopenharmony_ci#ifdef CONFIG_X86_64
37162306a36Sopenharmony_ci	{0, PTRS_PER_PGD * PGD_LEVEL_MULT / 2},
37262306a36Sopenharmony_ci	{GUARD_HOLE_END_ADDR, ~0UL},
37362306a36Sopenharmony_ci#else
37462306a36Sopenharmony_ci	{0, ~0UL},
37562306a36Sopenharmony_ci#endif
37662306a36Sopenharmony_ci	{0, 0}
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	struct pg_state st = {
38062306a36Sopenharmony_ci		.ptdump = {
38162306a36Sopenharmony_ci			.note_page	= note_page,
38262306a36Sopenharmony_ci			.effective_prot = effective_prot,
38362306a36Sopenharmony_ci			.range		= ptdump_ranges
38462306a36Sopenharmony_ci		},
38562306a36Sopenharmony_ci		.level = -1,
38662306a36Sopenharmony_ci		.to_dmesg	= dmesg,
38762306a36Sopenharmony_ci		.check_wx	= checkwx,
38862306a36Sopenharmony_ci		.seq		= m
38962306a36Sopenharmony_ci	};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	ptdump_walk_pgd(&st.ptdump, mm, pgd);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (!checkwx)
39462306a36Sopenharmony_ci		return;
39562306a36Sopenharmony_ci	if (st.wx_pages)
39662306a36Sopenharmony_ci		pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n",
39762306a36Sopenharmony_ci			st.wx_pages);
39862306a36Sopenharmony_ci	else
39962306a36Sopenharmony_ci		pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n");
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_civoid ptdump_walk_pgd_level(struct seq_file *m, struct mm_struct *mm)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	ptdump_walk_pgd_level_core(m, mm, mm->pgd, false, true);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_civoid ptdump_walk_pgd_level_debugfs(struct seq_file *m, struct mm_struct *mm,
40862306a36Sopenharmony_ci				   bool user)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	pgd_t *pgd = mm->pgd;
41162306a36Sopenharmony_ci#ifdef CONFIG_PAGE_TABLE_ISOLATION
41262306a36Sopenharmony_ci	if (user && boot_cpu_has(X86_FEATURE_PTI))
41362306a36Sopenharmony_ci		pgd = kernel_to_user_pgdp(pgd);
41462306a36Sopenharmony_ci#endif
41562306a36Sopenharmony_ci	ptdump_walk_pgd_level_core(m, mm, pgd, false, false);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptdump_walk_pgd_level_debugfs);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_civoid ptdump_walk_user_pgd_level_checkwx(void)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci#ifdef CONFIG_PAGE_TABLE_ISOLATION
42262306a36Sopenharmony_ci	pgd_t *pgd = INIT_PGD;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (!(__supported_pte_mask & _PAGE_NX) ||
42562306a36Sopenharmony_ci	    !boot_cpu_has(X86_FEATURE_PTI))
42662306a36Sopenharmony_ci		return;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	pr_info("x86/mm: Checking user space page tables\n");
42962306a36Sopenharmony_ci	pgd = kernel_to_user_pgdp(pgd);
43062306a36Sopenharmony_ci	ptdump_walk_pgd_level_core(NULL, &init_mm, pgd, true, false);
43162306a36Sopenharmony_ci#endif
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_civoid ptdump_walk_pgd_level_checkwx(void)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	ptdump_walk_pgd_level_core(NULL, &init_mm, INIT_PGD, true, false);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int __init pt_dump_init(void)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * Various markers are not compile-time constants, so assign them
44362306a36Sopenharmony_ci	 * here.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci#ifdef CONFIG_X86_64
44662306a36Sopenharmony_ci	address_markers[LOW_KERNEL_NR].start_address = PAGE_OFFSET;
44762306a36Sopenharmony_ci	address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
44862306a36Sopenharmony_ci	address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START;
44962306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL
45062306a36Sopenharmony_ci	address_markers[LDT_NR].start_address = LDT_BASE_ADDR;
45162306a36Sopenharmony_ci#endif
45262306a36Sopenharmony_ci#ifdef CONFIG_KASAN
45362306a36Sopenharmony_ci	address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START;
45462306a36Sopenharmony_ci	address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END;
45562306a36Sopenharmony_ci#endif
45662306a36Sopenharmony_ci#endif
45762306a36Sopenharmony_ci#ifdef CONFIG_X86_32
45862306a36Sopenharmony_ci	address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
45962306a36Sopenharmony_ci	address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
46062306a36Sopenharmony_ci# ifdef CONFIG_HIGHMEM
46162306a36Sopenharmony_ci	address_markers[PKMAP_BASE_NR].start_address = PKMAP_BASE;
46262306a36Sopenharmony_ci# endif
46362306a36Sopenharmony_ci	address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
46462306a36Sopenharmony_ci	address_markers[CPU_ENTRY_AREA_NR].start_address = CPU_ENTRY_AREA_BASE;
46562306a36Sopenharmony_ci# ifdef CONFIG_MODIFY_LDT_SYSCALL
46662306a36Sopenharmony_ci	address_markers[LDT_NR].start_address = LDT_BASE_ADDR;
46762306a36Sopenharmony_ci# endif
46862306a36Sopenharmony_ci#endif
46962306a36Sopenharmony_ci	return 0;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci__initcall(pt_dump_init);
472