xref: /kernel/linux/linux-5.10/arch/arm64/mm/ptdump.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci * Debug helper to dump the current kernel pagetables of the system
58c2ecf20Sopenharmony_ci * so that we can see what the various memory ranges are set to.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Derived from x86 and arm implementation:
88c2ecf20Sopenharmony_ci * (C) Copyright 2008 Intel Corporation
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Author: Arjan van de Ven <arjan@linux.intel.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/fs.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci#include <linux/ptdump.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/fixmap.h>
238c2ecf20Sopenharmony_ci#include <asm/kasan.h>
248c2ecf20Sopenharmony_ci#include <asm/memory.h>
258c2ecf20Sopenharmony_ci#include <asm/pgtable-hwdef.h>
268c2ecf20Sopenharmony_ci#include <asm/ptdump.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cienum address_markers_idx {
308c2ecf20Sopenharmony_ci	PAGE_OFFSET_NR = 0,
318c2ecf20Sopenharmony_ci	PAGE_END_NR,
328c2ecf20Sopenharmony_ci#ifdef CONFIG_KASAN
338c2ecf20Sopenharmony_ci	KASAN_START_NR,
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct addr_marker address_markers[] = {
388c2ecf20Sopenharmony_ci	{ PAGE_OFFSET,			"Linear Mapping start" },
398c2ecf20Sopenharmony_ci	{ 0 /* PAGE_END */,		"Linear Mapping end" },
408c2ecf20Sopenharmony_ci#ifdef CONFIG_KASAN
418c2ecf20Sopenharmony_ci	{ 0 /* KASAN_SHADOW_START */,	"Kasan shadow start" },
428c2ecf20Sopenharmony_ci	{ KASAN_SHADOW_END,		"Kasan shadow end" },
438c2ecf20Sopenharmony_ci#endif
448c2ecf20Sopenharmony_ci	{ BPF_JIT_REGION_START,		"BPF start" },
458c2ecf20Sopenharmony_ci	{ BPF_JIT_REGION_END,		"BPF end" },
468c2ecf20Sopenharmony_ci	{ MODULES_VADDR,		"Modules start" },
478c2ecf20Sopenharmony_ci	{ MODULES_END,			"Modules end" },
488c2ecf20Sopenharmony_ci	{ VMALLOC_START,		"vmalloc() area" },
498c2ecf20Sopenharmony_ci	{ VMALLOC_END,			"vmalloc() end" },
508c2ecf20Sopenharmony_ci	{ FIXADDR_START,		"Fixmap start" },
518c2ecf20Sopenharmony_ci	{ FIXADDR_TOP,			"Fixmap end" },
528c2ecf20Sopenharmony_ci	{ PCI_IO_START,			"PCI I/O start" },
538c2ecf20Sopenharmony_ci	{ PCI_IO_END,			"PCI I/O end" },
548c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP
558c2ecf20Sopenharmony_ci	{ VMEMMAP_START,		"vmemmap start" },
568c2ecf20Sopenharmony_ci	{ VMEMMAP_START + VMEMMAP_SIZE,	"vmemmap end" },
578c2ecf20Sopenharmony_ci#endif
588c2ecf20Sopenharmony_ci	{ -1,				NULL },
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define pt_dump_seq_printf(m, fmt, args...)	\
628c2ecf20Sopenharmony_ci({						\
638c2ecf20Sopenharmony_ci	if (m)					\
648c2ecf20Sopenharmony_ci		seq_printf(m, fmt, ##args);	\
658c2ecf20Sopenharmony_ci})
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define pt_dump_seq_puts(m, fmt)	\
688c2ecf20Sopenharmony_ci({					\
698c2ecf20Sopenharmony_ci	if (m)				\
708c2ecf20Sopenharmony_ci		seq_printf(m, fmt);	\
718c2ecf20Sopenharmony_ci})
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/*
748c2ecf20Sopenharmony_ci * The page dumper groups page table entries of the same type into a single
758c2ecf20Sopenharmony_ci * description. It uses pg_state to track the range information while
768c2ecf20Sopenharmony_ci * iterating over the pte entries. When the continuity is broken it then
778c2ecf20Sopenharmony_ci * dumps out a description of the range.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_cistruct pg_state {
808c2ecf20Sopenharmony_ci	struct ptdump_state ptdump;
818c2ecf20Sopenharmony_ci	struct seq_file *seq;
828c2ecf20Sopenharmony_ci	const struct addr_marker *marker;
838c2ecf20Sopenharmony_ci	unsigned long start_address;
848c2ecf20Sopenharmony_ci	int level;
858c2ecf20Sopenharmony_ci	u64 current_prot;
868c2ecf20Sopenharmony_ci	bool check_wx;
878c2ecf20Sopenharmony_ci	unsigned long wx_pages;
888c2ecf20Sopenharmony_ci	unsigned long uxn_pages;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct prot_bits {
928c2ecf20Sopenharmony_ci	u64		mask;
938c2ecf20Sopenharmony_ci	u64		val;
948c2ecf20Sopenharmony_ci	const char	*set;
958c2ecf20Sopenharmony_ci	const char	*clear;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic const struct prot_bits pte_bits[] = {
998c2ecf20Sopenharmony_ci	{
1008c2ecf20Sopenharmony_ci		.mask	= PTE_VALID,
1018c2ecf20Sopenharmony_ci		.val	= PTE_VALID,
1028c2ecf20Sopenharmony_ci		.set	= " ",
1038c2ecf20Sopenharmony_ci		.clear	= "F",
1048c2ecf20Sopenharmony_ci	}, {
1058c2ecf20Sopenharmony_ci		.mask	= PTE_USER,
1068c2ecf20Sopenharmony_ci		.val	= PTE_USER,
1078c2ecf20Sopenharmony_ci		.set	= "USR",
1088c2ecf20Sopenharmony_ci		.clear	= "   ",
1098c2ecf20Sopenharmony_ci	}, {
1108c2ecf20Sopenharmony_ci		.mask	= PTE_RDONLY,
1118c2ecf20Sopenharmony_ci		.val	= PTE_RDONLY,
1128c2ecf20Sopenharmony_ci		.set	= "ro",
1138c2ecf20Sopenharmony_ci		.clear	= "RW",
1148c2ecf20Sopenharmony_ci	}, {
1158c2ecf20Sopenharmony_ci		.mask	= PTE_PXN,
1168c2ecf20Sopenharmony_ci		.val	= PTE_PXN,
1178c2ecf20Sopenharmony_ci		.set	= "NX",
1188c2ecf20Sopenharmony_ci		.clear	= "x ",
1198c2ecf20Sopenharmony_ci	}, {
1208c2ecf20Sopenharmony_ci		.mask	= PTE_SHARED,
1218c2ecf20Sopenharmony_ci		.val	= PTE_SHARED,
1228c2ecf20Sopenharmony_ci		.set	= "SHD",
1238c2ecf20Sopenharmony_ci		.clear	= "   ",
1248c2ecf20Sopenharmony_ci	}, {
1258c2ecf20Sopenharmony_ci		.mask	= PTE_AF,
1268c2ecf20Sopenharmony_ci		.val	= PTE_AF,
1278c2ecf20Sopenharmony_ci		.set	= "AF",
1288c2ecf20Sopenharmony_ci		.clear	= "  ",
1298c2ecf20Sopenharmony_ci	}, {
1308c2ecf20Sopenharmony_ci		.mask	= PTE_NG,
1318c2ecf20Sopenharmony_ci		.val	= PTE_NG,
1328c2ecf20Sopenharmony_ci		.set	= "NG",
1338c2ecf20Sopenharmony_ci		.clear	= "  ",
1348c2ecf20Sopenharmony_ci	}, {
1358c2ecf20Sopenharmony_ci		.mask	= PTE_CONT,
1368c2ecf20Sopenharmony_ci		.val	= PTE_CONT,
1378c2ecf20Sopenharmony_ci		.set	= "CON",
1388c2ecf20Sopenharmony_ci		.clear	= "   ",
1398c2ecf20Sopenharmony_ci	}, {
1408c2ecf20Sopenharmony_ci		.mask	= PTE_TABLE_BIT,
1418c2ecf20Sopenharmony_ci		.val	= PTE_TABLE_BIT,
1428c2ecf20Sopenharmony_ci		.set	= "   ",
1438c2ecf20Sopenharmony_ci		.clear	= "BLK",
1448c2ecf20Sopenharmony_ci	}, {
1458c2ecf20Sopenharmony_ci		.mask	= PTE_UXN,
1468c2ecf20Sopenharmony_ci		.val	= PTE_UXN,
1478c2ecf20Sopenharmony_ci		.set	= "UXN",
1488c2ecf20Sopenharmony_ci		.clear	= "   ",
1498c2ecf20Sopenharmony_ci	}, {
1508c2ecf20Sopenharmony_ci		.mask	= PTE_GP,
1518c2ecf20Sopenharmony_ci		.val	= PTE_GP,
1528c2ecf20Sopenharmony_ci		.set	= "GP",
1538c2ecf20Sopenharmony_ci		.clear	= "  ",
1548c2ecf20Sopenharmony_ci	}, {
1558c2ecf20Sopenharmony_ci		.mask	= PTE_ATTRINDX_MASK,
1568c2ecf20Sopenharmony_ci		.val	= PTE_ATTRINDX(MT_DEVICE_nGnRnE),
1578c2ecf20Sopenharmony_ci		.set	= "DEVICE/nGnRnE",
1588c2ecf20Sopenharmony_ci	}, {
1598c2ecf20Sopenharmony_ci		.mask	= PTE_ATTRINDX_MASK,
1608c2ecf20Sopenharmony_ci		.val	= PTE_ATTRINDX(MT_DEVICE_nGnRE),
1618c2ecf20Sopenharmony_ci		.set	= "DEVICE/nGnRE",
1628c2ecf20Sopenharmony_ci	}, {
1638c2ecf20Sopenharmony_ci		.mask	= PTE_ATTRINDX_MASK,
1648c2ecf20Sopenharmony_ci		.val	= PTE_ATTRINDX(MT_DEVICE_GRE),
1658c2ecf20Sopenharmony_ci		.set	= "DEVICE/GRE",
1668c2ecf20Sopenharmony_ci	}, {
1678c2ecf20Sopenharmony_ci		.mask	= PTE_ATTRINDX_MASK,
1688c2ecf20Sopenharmony_ci		.val	= PTE_ATTRINDX(MT_NORMAL_NC),
1698c2ecf20Sopenharmony_ci		.set	= "MEM/NORMAL-NC",
1708c2ecf20Sopenharmony_ci	}, {
1718c2ecf20Sopenharmony_ci		.mask	= PTE_ATTRINDX_MASK,
1728c2ecf20Sopenharmony_ci		.val	= PTE_ATTRINDX(MT_NORMAL),
1738c2ecf20Sopenharmony_ci		.set	= "MEM/NORMAL",
1748c2ecf20Sopenharmony_ci	}, {
1758c2ecf20Sopenharmony_ci		.mask	= PTE_ATTRINDX_MASK,
1768c2ecf20Sopenharmony_ci		.val	= PTE_ATTRINDX(MT_NORMAL_TAGGED),
1778c2ecf20Sopenharmony_ci		.set	= "MEM/NORMAL-TAGGED",
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct pg_level {
1828c2ecf20Sopenharmony_ci	const struct prot_bits *bits;
1838c2ecf20Sopenharmony_ci	const char *name;
1848c2ecf20Sopenharmony_ci	size_t num;
1858c2ecf20Sopenharmony_ci	u64 mask;
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic struct pg_level pg_level[] = {
1898c2ecf20Sopenharmony_ci	{ /* pgd */
1908c2ecf20Sopenharmony_ci		.name	= "PGD",
1918c2ecf20Sopenharmony_ci		.bits	= pte_bits,
1928c2ecf20Sopenharmony_ci		.num	= ARRAY_SIZE(pte_bits),
1938c2ecf20Sopenharmony_ci	}, { /* p4d */
1948c2ecf20Sopenharmony_ci		.name	= "P4D",
1958c2ecf20Sopenharmony_ci		.bits	= pte_bits,
1968c2ecf20Sopenharmony_ci		.num	= ARRAY_SIZE(pte_bits),
1978c2ecf20Sopenharmony_ci	}, { /* pud */
1988c2ecf20Sopenharmony_ci		.name	= (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
1998c2ecf20Sopenharmony_ci		.bits	= pte_bits,
2008c2ecf20Sopenharmony_ci		.num	= ARRAY_SIZE(pte_bits),
2018c2ecf20Sopenharmony_ci	}, { /* pmd */
2028c2ecf20Sopenharmony_ci		.name	= (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
2038c2ecf20Sopenharmony_ci		.bits	= pte_bits,
2048c2ecf20Sopenharmony_ci		.num	= ARRAY_SIZE(pte_bits),
2058c2ecf20Sopenharmony_ci	}, { /* pte */
2068c2ecf20Sopenharmony_ci		.name	= "PTE",
2078c2ecf20Sopenharmony_ci		.bits	= pte_bits,
2088c2ecf20Sopenharmony_ci		.num	= ARRAY_SIZE(pte_bits),
2098c2ecf20Sopenharmony_ci	},
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic void dump_prot(struct pg_state *st, const struct prot_bits *bits,
2138c2ecf20Sopenharmony_ci			size_t num)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	unsigned i;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++, bits++) {
2188c2ecf20Sopenharmony_ci		const char *s;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		if ((st->current_prot & bits->mask) == bits->val)
2218c2ecf20Sopenharmony_ci			s = bits->set;
2228c2ecf20Sopenharmony_ci		else
2238c2ecf20Sopenharmony_ci			s = bits->clear;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		if (s)
2268c2ecf20Sopenharmony_ci			pt_dump_seq_printf(st->seq, " %s", s);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic void note_prot_uxn(struct pg_state *st, unsigned long addr)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	if (!st->check_wx)
2338c2ecf20Sopenharmony_ci		return;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if ((st->current_prot & PTE_UXN) == PTE_UXN)
2368c2ecf20Sopenharmony_ci		return;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	WARN_ONCE(1, "arm64/mm: Found non-UXN mapping at address %p/%pS\n",
2398c2ecf20Sopenharmony_ci		  (void *)st->start_address, (void *)st->start_address);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	st->uxn_pages += (addr - st->start_address) / PAGE_SIZE;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void note_prot_wx(struct pg_state *st, unsigned long addr)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (!st->check_wx)
2478c2ecf20Sopenharmony_ci		return;
2488c2ecf20Sopenharmony_ci	if ((st->current_prot & PTE_RDONLY) == PTE_RDONLY)
2498c2ecf20Sopenharmony_ci		return;
2508c2ecf20Sopenharmony_ci	if ((st->current_prot & PTE_PXN) == PTE_PXN)
2518c2ecf20Sopenharmony_ci		return;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	WARN_ONCE(1, "arm64/mm: Found insecure W+X mapping at address %p/%pS\n",
2548c2ecf20Sopenharmony_ci		  (void *)st->start_address, (void *)st->start_address);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
2608c2ecf20Sopenharmony_ci		      u64 val)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
2638c2ecf20Sopenharmony_ci	static const char units[] = "KMGTPE";
2648c2ecf20Sopenharmony_ci	u64 prot = 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (level >= 0)
2678c2ecf20Sopenharmony_ci		prot = val & pg_level[level].mask;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (st->level == -1) {
2708c2ecf20Sopenharmony_ci		st->level = level;
2718c2ecf20Sopenharmony_ci		st->current_prot = prot;
2728c2ecf20Sopenharmony_ci		st->start_address = addr;
2738c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
2748c2ecf20Sopenharmony_ci	} else if (prot != st->current_prot || level != st->level ||
2758c2ecf20Sopenharmony_ci		   addr >= st->marker[1].start_address) {
2768c2ecf20Sopenharmony_ci		const char *unit = units;
2778c2ecf20Sopenharmony_ci		unsigned long delta;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		if (st->current_prot) {
2808c2ecf20Sopenharmony_ci			note_prot_uxn(st, addr);
2818c2ecf20Sopenharmony_ci			note_prot_wx(st, addr);
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "0x%016lx-0x%016lx   ",
2858c2ecf20Sopenharmony_ci				   st->start_address, addr);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		delta = (addr - st->start_address) >> 10;
2888c2ecf20Sopenharmony_ci		while (!(delta & 1023) && unit[1]) {
2898c2ecf20Sopenharmony_ci			delta >>= 10;
2908c2ecf20Sopenharmony_ci			unit++;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
2938c2ecf20Sopenharmony_ci				   pg_level[st->level].name);
2948c2ecf20Sopenharmony_ci		if (st->current_prot && pg_level[st->level].bits)
2958c2ecf20Sopenharmony_ci			dump_prot(st, pg_level[st->level].bits,
2968c2ecf20Sopenharmony_ci				  pg_level[st->level].num);
2978c2ecf20Sopenharmony_ci		pt_dump_seq_puts(st->seq, "\n");
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		if (addr >= st->marker[1].start_address) {
3008c2ecf20Sopenharmony_ci			st->marker++;
3018c2ecf20Sopenharmony_ci			pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
3028c2ecf20Sopenharmony_ci		}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		st->start_address = addr;
3058c2ecf20Sopenharmony_ci		st->current_prot = prot;
3068c2ecf20Sopenharmony_ci		st->level = level;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (addr >= st->marker[1].start_address) {
3108c2ecf20Sopenharmony_ci		st->marker++;
3118c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_civoid ptdump_walk(struct seq_file *s, struct ptdump_info *info)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	unsigned long end = ~0UL;
3198c2ecf20Sopenharmony_ci	struct pg_state st;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (info->base_addr < TASK_SIZE_64)
3228c2ecf20Sopenharmony_ci		end = TASK_SIZE_64;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	st = (struct pg_state){
3258c2ecf20Sopenharmony_ci		.seq = s,
3268c2ecf20Sopenharmony_ci		.marker = info->markers,
3278c2ecf20Sopenharmony_ci		.ptdump = {
3288c2ecf20Sopenharmony_ci			.note_page = note_page,
3298c2ecf20Sopenharmony_ci			.range = (struct ptdump_range[]){
3308c2ecf20Sopenharmony_ci				{info->base_addr, end},
3318c2ecf20Sopenharmony_ci				{0, 0}
3328c2ecf20Sopenharmony_ci			}
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci	};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic void ptdump_initialize(void)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	unsigned i, j;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
3448c2ecf20Sopenharmony_ci		if (pg_level[i].bits)
3458c2ecf20Sopenharmony_ci			for (j = 0; j < pg_level[i].num; j++)
3468c2ecf20Sopenharmony_ci				pg_level[i].mask |= pg_level[i].bits[j].mask;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic struct ptdump_info kernel_ptdump_info = {
3508c2ecf20Sopenharmony_ci	.mm		= &init_mm,
3518c2ecf20Sopenharmony_ci	.markers	= address_markers,
3528c2ecf20Sopenharmony_ci	.base_addr	= PAGE_OFFSET,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_civoid ptdump_check_wx(void)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct pg_state st = {
3588c2ecf20Sopenharmony_ci		.seq = NULL,
3598c2ecf20Sopenharmony_ci		.marker = (struct addr_marker[]) {
3608c2ecf20Sopenharmony_ci			{ 0, NULL},
3618c2ecf20Sopenharmony_ci			{ -1, NULL},
3628c2ecf20Sopenharmony_ci		},
3638c2ecf20Sopenharmony_ci		.level = -1,
3648c2ecf20Sopenharmony_ci		.check_wx = true,
3658c2ecf20Sopenharmony_ci		.ptdump = {
3668c2ecf20Sopenharmony_ci			.note_page = note_page,
3678c2ecf20Sopenharmony_ci			.range = (struct ptdump_range[]) {
3688c2ecf20Sopenharmony_ci				{PAGE_OFFSET, ~0UL},
3698c2ecf20Sopenharmony_ci				{0, 0}
3708c2ecf20Sopenharmony_ci			}
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci	};
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (st.wx_pages || st.uxn_pages)
3778c2ecf20Sopenharmony_ci		pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
3788c2ecf20Sopenharmony_ci			st.wx_pages, st.uxn_pages);
3798c2ecf20Sopenharmony_ci	else
3808c2ecf20Sopenharmony_ci		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int ptdump_init(void)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	address_markers[PAGE_END_NR].start_address = PAGE_END;
3868c2ecf20Sopenharmony_ci#ifdef CONFIG_KASAN
3878c2ecf20Sopenharmony_ci	address_markers[KASAN_START_NR].start_address = KASAN_SHADOW_START;
3888c2ecf20Sopenharmony_ci#endif
3898c2ecf20Sopenharmony_ci	ptdump_initialize();
3908c2ecf20Sopenharmony_ci	ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_cidevice_initcall(ptdump_init);
394