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