18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2016, Rashmica Gupta, IBM Corp.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This traverses the kernel pagetables and dumps the
68c2ecf20Sopenharmony_ci * information about the used sections of memory to
78c2ecf20Sopenharmony_ci * /sys/kernel/debug/kernel_pagetables.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Derived from the arm64 implementation:
108c2ecf20Sopenharmony_ci * Copyright (c) 2014, The Linux Foundation, Laura Abbott.
118c2ecf20Sopenharmony_ci * (C) Copyright 2008 Intel Corporation, Arjan van de Ven.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
148c2ecf20Sopenharmony_ci#include <linux/fs.h>
158c2ecf20Sopenharmony_ci#include <linux/hugetlb.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci#include <linux/highmem.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
218c2ecf20Sopenharmony_ci#include <asm/fixmap.h>
228c2ecf20Sopenharmony_ci#include <linux/const.h>
238c2ecf20Sopenharmony_ci#include <asm/page.h>
248c2ecf20Sopenharmony_ci#include <asm/hugetlb.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <mm/mmu_decl.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "ptdump.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * To visualise what is happening,
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci *  - PTRS_PER_P** = how many entries there are in the corresponding P**
348c2ecf20Sopenharmony_ci *  - P**_SHIFT = how many bits of the address we use to index into the
358c2ecf20Sopenharmony_ci * corresponding P**
368c2ecf20Sopenharmony_ci *  - P**_SIZE is how much memory we can access through the table - not the
378c2ecf20Sopenharmony_ci * size of the table itself.
388c2ecf20Sopenharmony_ci * P**={PGD, PUD, PMD, PTE}
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * Each entry of the PGD points to a PUD. Each entry of a PUD points to a
428c2ecf20Sopenharmony_ci * PMD. Each entry of a PMD points to a PTE. And every PTE entry points to
438c2ecf20Sopenharmony_ci * a page.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * In the case where there are only 3 levels, the PUD is folded into the
468c2ecf20Sopenharmony_ci * PGD: every PUD has only one entry which points to the PMD.
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci * The page dumper groups page table entries of the same type into a single
498c2ecf20Sopenharmony_ci * description. It uses pg_state to track the range information while
508c2ecf20Sopenharmony_ci * iterating over the PTE entries. When the continuity is broken it then
518c2ecf20Sopenharmony_ci * dumps out a description of the range - ie PTEs that are virtually contiguous
528c2ecf20Sopenharmony_ci * with the same PTE flags are chunked together. This is to make it clear how
538c2ecf20Sopenharmony_ci * different areas of the kernel virtual memory are used.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_cistruct pg_state {
578c2ecf20Sopenharmony_ci	struct seq_file *seq;
588c2ecf20Sopenharmony_ci	const struct addr_marker *marker;
598c2ecf20Sopenharmony_ci	unsigned long start_address;
608c2ecf20Sopenharmony_ci	unsigned long start_pa;
618c2ecf20Sopenharmony_ci	unsigned long last_pa;
628c2ecf20Sopenharmony_ci	unsigned long page_size;
638c2ecf20Sopenharmony_ci	unsigned int level;
648c2ecf20Sopenharmony_ci	u64 current_flags;
658c2ecf20Sopenharmony_ci	bool check_wx;
668c2ecf20Sopenharmony_ci	unsigned long wx_pages;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct addr_marker {
708c2ecf20Sopenharmony_ci	unsigned long start_address;
718c2ecf20Sopenharmony_ci	const char *name;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic struct addr_marker address_markers[] = {
758c2ecf20Sopenharmony_ci	{ 0,	"Start of kernel VM" },
768c2ecf20Sopenharmony_ci#ifdef MODULES_VADDR
778c2ecf20Sopenharmony_ci	{ 0,	"modules start" },
788c2ecf20Sopenharmony_ci	{ 0,	"modules end" },
798c2ecf20Sopenharmony_ci#endif
808c2ecf20Sopenharmony_ci	{ 0,	"vmalloc() Area" },
818c2ecf20Sopenharmony_ci	{ 0,	"vmalloc() End" },
828c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
838c2ecf20Sopenharmony_ci	{ 0,	"isa I/O start" },
848c2ecf20Sopenharmony_ci	{ 0,	"isa I/O end" },
858c2ecf20Sopenharmony_ci	{ 0,	"phb I/O start" },
868c2ecf20Sopenharmony_ci	{ 0,	"phb I/O end" },
878c2ecf20Sopenharmony_ci	{ 0,	"I/O remap start" },
888c2ecf20Sopenharmony_ci	{ 0,	"I/O remap end" },
898c2ecf20Sopenharmony_ci	{ 0,	"vmemmap start" },
908c2ecf20Sopenharmony_ci#else
918c2ecf20Sopenharmony_ci	{ 0,	"Early I/O remap start" },
928c2ecf20Sopenharmony_ci	{ 0,	"Early I/O remap end" },
938c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM
948c2ecf20Sopenharmony_ci	{ 0,	"Highmem PTEs start" },
958c2ecf20Sopenharmony_ci	{ 0,	"Highmem PTEs end" },
968c2ecf20Sopenharmony_ci#endif
978c2ecf20Sopenharmony_ci	{ 0,	"Fixmap start" },
988c2ecf20Sopenharmony_ci	{ 0,	"Fixmap end" },
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci#ifdef CONFIG_KASAN
1018c2ecf20Sopenharmony_ci	{ 0,	"kasan shadow mem start" },
1028c2ecf20Sopenharmony_ci	{ 0,	"kasan shadow mem end" },
1038c2ecf20Sopenharmony_ci#endif
1048c2ecf20Sopenharmony_ci	{ -1,	NULL },
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define pt_dump_seq_printf(m, fmt, args...)	\
1088c2ecf20Sopenharmony_ci({						\
1098c2ecf20Sopenharmony_ci	if (m)					\
1108c2ecf20Sopenharmony_ci		seq_printf(m, fmt, ##args);	\
1118c2ecf20Sopenharmony_ci})
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define pt_dump_seq_putc(m, c)		\
1148c2ecf20Sopenharmony_ci({					\
1158c2ecf20Sopenharmony_ci	if (m)				\
1168c2ecf20Sopenharmony_ci		seq_putc(m, c);		\
1178c2ecf20Sopenharmony_ci})
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_civoid pt_dump_size(struct seq_file *m, unsigned long size)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	static const char units[] = "KMGTPE";
1228c2ecf20Sopenharmony_ci	const char *unit = units;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Work out what appropriate unit to use */
1258c2ecf20Sopenharmony_ci	while (!(size & 1023) && unit[1]) {
1268c2ecf20Sopenharmony_ci		size >>= 10;
1278c2ecf20Sopenharmony_ci		unit++;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	pt_dump_seq_printf(m, "%9lu%c ", size, *unit);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void dump_flag_info(struct pg_state *st, const struct flag_info
1338c2ecf20Sopenharmony_ci		*flag, u64 pte, int num)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	unsigned int i;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++, flag++) {
1388c2ecf20Sopenharmony_ci		const char *s = NULL;
1398c2ecf20Sopenharmony_ci		u64 val;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		/* flag not defined so don't check it */
1428c2ecf20Sopenharmony_ci		if (flag->mask == 0)
1438c2ecf20Sopenharmony_ci			continue;
1448c2ecf20Sopenharmony_ci		/* Some 'flags' are actually values */
1458c2ecf20Sopenharmony_ci		if (flag->is_val) {
1468c2ecf20Sopenharmony_ci			val = pte & flag->val;
1478c2ecf20Sopenharmony_ci			if (flag->shift)
1488c2ecf20Sopenharmony_ci				val = val >> flag->shift;
1498c2ecf20Sopenharmony_ci			pt_dump_seq_printf(st->seq, "  %s:%llx", flag->set, val);
1508c2ecf20Sopenharmony_ci		} else {
1518c2ecf20Sopenharmony_ci			if ((pte & flag->mask) == flag->val)
1528c2ecf20Sopenharmony_ci				s = flag->set;
1538c2ecf20Sopenharmony_ci			else
1548c2ecf20Sopenharmony_ci				s = flag->clear;
1558c2ecf20Sopenharmony_ci			if (s)
1568c2ecf20Sopenharmony_ci				pt_dump_seq_printf(st->seq, "  %s", s);
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci		st->current_flags &= ~flag->mask;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	if (st->current_flags != 0)
1618c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "  unknown flags:%llx", st->current_flags);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void dump_addr(struct pg_state *st, unsigned long addr)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	unsigned long delta;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
1698c2ecf20Sopenharmony_ci#define REG		"0x%016lx"
1708c2ecf20Sopenharmony_ci#else
1718c2ecf20Sopenharmony_ci#define REG		"0x%08lx"
1728c2ecf20Sopenharmony_ci#endif
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	pt_dump_seq_printf(st->seq, REG "-" REG " ", st->start_address, addr - 1);
1758c2ecf20Sopenharmony_ci	if (st->start_pa == st->last_pa && st->start_address + st->page_size != addr) {
1768c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "[" REG "]", st->start_pa);
1778c2ecf20Sopenharmony_ci		delta = st->page_size >> 10;
1788c2ecf20Sopenharmony_ci	} else {
1798c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, " " REG " ", st->start_pa);
1808c2ecf20Sopenharmony_ci		delta = (addr - st->start_address) >> 10;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	pt_dump_size(st->seq, delta);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic void note_prot_wx(struct pg_state *st, unsigned long addr)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	pte_t pte = __pte(st->current_flags);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_PPC_DEBUG_WX) || !st->check_wx)
1908c2ecf20Sopenharmony_ci		return;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!pte_write(pte) || !pte_exec(pte))
1938c2ecf20Sopenharmony_ci		return;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	WARN_ONCE(1, "powerpc/mm: Found insecure W+X mapping at address %p/%pS\n",
1968c2ecf20Sopenharmony_ci		  (void *)st->start_address, (void *)st->start_address);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void note_page_update_state(struct pg_state *st, unsigned long addr,
2028c2ecf20Sopenharmony_ci				   unsigned int level, u64 val, unsigned long page_size)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u64 flag = val & pg_level[level].mask;
2058c2ecf20Sopenharmony_ci	u64 pa = val & PTE_RPN_MASK;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	st->level = level;
2088c2ecf20Sopenharmony_ci	st->current_flags = flag;
2098c2ecf20Sopenharmony_ci	st->start_address = addr;
2108c2ecf20Sopenharmony_ci	st->start_pa = pa;
2118c2ecf20Sopenharmony_ci	st->page_size = page_size;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	while (addr >= st->marker[1].start_address) {
2148c2ecf20Sopenharmony_ci		st->marker++;
2158c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void note_page(struct pg_state *st, unsigned long addr,
2208c2ecf20Sopenharmony_ci	       unsigned int level, u64 val, unsigned long page_size)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	u64 flag = val & pg_level[level].mask;
2238c2ecf20Sopenharmony_ci	u64 pa = val & PTE_RPN_MASK;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* At first no level is set */
2268c2ecf20Sopenharmony_ci	if (!st->level) {
2278c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
2288c2ecf20Sopenharmony_ci		note_page_update_state(st, addr, level, val, page_size);
2298c2ecf20Sopenharmony_ci	/*
2308c2ecf20Sopenharmony_ci	 * Dump the section of virtual memory when:
2318c2ecf20Sopenharmony_ci	 *   - the PTE flags from one entry to the next differs.
2328c2ecf20Sopenharmony_ci	 *   - we change levels in the tree.
2338c2ecf20Sopenharmony_ci	 *   - the address is in a different section of memory and is thus
2348c2ecf20Sopenharmony_ci	 *   used for a different purpose, regardless of the flags.
2358c2ecf20Sopenharmony_ci	 *   - the pa of this page is not adjacent to the last inspected page
2368c2ecf20Sopenharmony_ci	 */
2378c2ecf20Sopenharmony_ci	} else if (flag != st->current_flags || level != st->level ||
2388c2ecf20Sopenharmony_ci		   addr >= st->marker[1].start_address ||
2398c2ecf20Sopenharmony_ci		   (pa != st->last_pa + st->page_size &&
2408c2ecf20Sopenharmony_ci		    (pa != st->start_pa || st->start_pa != st->last_pa))) {
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		/* Check the PTE flags */
2438c2ecf20Sopenharmony_ci		if (st->current_flags) {
2448c2ecf20Sopenharmony_ci			note_prot_wx(st, addr);
2458c2ecf20Sopenharmony_ci			dump_addr(st, addr);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci			/* Dump all the flags */
2488c2ecf20Sopenharmony_ci			if (pg_level[st->level].flag)
2498c2ecf20Sopenharmony_ci				dump_flag_info(st, pg_level[st->level].flag,
2508c2ecf20Sopenharmony_ci					  st->current_flags,
2518c2ecf20Sopenharmony_ci					  pg_level[st->level].num);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci			pt_dump_seq_putc(st->seq, '\n');
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		/*
2578c2ecf20Sopenharmony_ci		 * Address indicates we have passed the end of the
2588c2ecf20Sopenharmony_ci		 * current section of virtual memory
2598c2ecf20Sopenharmony_ci		 */
2608c2ecf20Sopenharmony_ci		note_page_update_state(st, addr, level, val, page_size);
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci	st->last_pa = pa;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	pte_t *pte = pte_offset_kernel(pmd, 0);
2688c2ecf20Sopenharmony_ci	unsigned long addr;
2698c2ecf20Sopenharmony_ci	unsigned int i;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
2728c2ecf20Sopenharmony_ci		addr = start + i * PAGE_SIZE;
2738c2ecf20Sopenharmony_ci		note_page(st, addr, 4, pte_val(*pte), PAGE_SIZE);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void walk_hugepd(struct pg_state *st, hugepd_t *phpd, unsigned long start,
2798c2ecf20Sopenharmony_ci			int pdshift, int level)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_HUGEPD
2828c2ecf20Sopenharmony_ci	unsigned int i;
2838c2ecf20Sopenharmony_ci	int shift = hugepd_shift(*phpd);
2848c2ecf20Sopenharmony_ci	int ptrs_per_hpd = pdshift - shift > 0 ? 1 << (pdshift - shift) : 1;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (start & ((1 << shift) - 1))
2878c2ecf20Sopenharmony_ci		return;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	for (i = 0; i < ptrs_per_hpd; i++) {
2908c2ecf20Sopenharmony_ci		unsigned long addr = start + (i << shift);
2918c2ecf20Sopenharmony_ci		pte_t *pte = hugepte_offset(*phpd, addr, pdshift);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		note_page(st, addr, level + 1, pte_val(*pte), 1 << shift);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci#endif
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	pmd_t *pmd = pmd_offset(pud, 0);
3018c2ecf20Sopenharmony_ci	unsigned long addr;
3028c2ecf20Sopenharmony_ci	unsigned int i;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
3058c2ecf20Sopenharmony_ci		addr = start + i * PMD_SIZE;
3068c2ecf20Sopenharmony_ci		if (!pmd_none(*pmd) && !pmd_is_leaf(*pmd))
3078c2ecf20Sopenharmony_ci			/* pmd exists */
3088c2ecf20Sopenharmony_ci			walk_pte(st, pmd, addr);
3098c2ecf20Sopenharmony_ci		else
3108c2ecf20Sopenharmony_ci			note_page(st, addr, 3, pmd_val(*pmd), PMD_SIZE);
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic void walk_pud(struct pg_state *st, p4d_t *p4d, unsigned long start)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	pud_t *pud = pud_offset(p4d, 0);
3178c2ecf20Sopenharmony_ci	unsigned long addr;
3188c2ecf20Sopenharmony_ci	unsigned int i;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
3218c2ecf20Sopenharmony_ci		addr = start + i * PUD_SIZE;
3228c2ecf20Sopenharmony_ci		if (!pud_none(*pud) && !pud_is_leaf(*pud))
3238c2ecf20Sopenharmony_ci			/* pud exists */
3248c2ecf20Sopenharmony_ci			walk_pmd(st, pud, addr);
3258c2ecf20Sopenharmony_ci		else
3268c2ecf20Sopenharmony_ci			note_page(st, addr, 2, pud_val(*pud), PUD_SIZE);
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void walk_pagetables(struct pg_state *st)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	unsigned int i;
3338c2ecf20Sopenharmony_ci	unsigned long addr = st->start_address & PGDIR_MASK;
3348c2ecf20Sopenharmony_ci	pgd_t *pgd = pgd_offset_k(addr);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * Traverse the linux pagetable structure and dump pages that are in
3388c2ecf20Sopenharmony_ci	 * the hash pagetable.
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	for (i = pgd_index(addr); i < PTRS_PER_PGD; i++, pgd++, addr += PGDIR_SIZE) {
3418c2ecf20Sopenharmony_ci		p4d_t *p4d = p4d_offset(pgd, 0);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		if (p4d_none(*p4d) || p4d_is_leaf(*p4d))
3448c2ecf20Sopenharmony_ci			note_page(st, addr, 1, p4d_val(*p4d), PGDIR_SIZE);
3458c2ecf20Sopenharmony_ci		else if (is_hugepd(__hugepd(p4d_val(*p4d))))
3468c2ecf20Sopenharmony_ci			walk_hugepd(st, (hugepd_t *)p4d, addr, PGDIR_SHIFT, 1);
3478c2ecf20Sopenharmony_ci		else
3488c2ecf20Sopenharmony_ci			/* p4d exists */
3498c2ecf20Sopenharmony_ci			walk_pud(st, p4d, addr);
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic void populate_markers(void)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int i = 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
3588c2ecf20Sopenharmony_ci	address_markers[i++].start_address = PAGE_OFFSET;
3598c2ecf20Sopenharmony_ci#else
3608c2ecf20Sopenharmony_ci	address_markers[i++].start_address = TASK_SIZE;
3618c2ecf20Sopenharmony_ci#endif
3628c2ecf20Sopenharmony_ci#ifdef MODULES_VADDR
3638c2ecf20Sopenharmony_ci	address_markers[i++].start_address = MODULES_VADDR;
3648c2ecf20Sopenharmony_ci	address_markers[i++].start_address = MODULES_END;
3658c2ecf20Sopenharmony_ci#endif
3668c2ecf20Sopenharmony_ci	address_markers[i++].start_address = VMALLOC_START;
3678c2ecf20Sopenharmony_ci	address_markers[i++].start_address = VMALLOC_END;
3688c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
3698c2ecf20Sopenharmony_ci	address_markers[i++].start_address = ISA_IO_BASE;
3708c2ecf20Sopenharmony_ci	address_markers[i++].start_address = ISA_IO_END;
3718c2ecf20Sopenharmony_ci	address_markers[i++].start_address = PHB_IO_BASE;
3728c2ecf20Sopenharmony_ci	address_markers[i++].start_address = PHB_IO_END;
3738c2ecf20Sopenharmony_ci	address_markers[i++].start_address = IOREMAP_BASE;
3748c2ecf20Sopenharmony_ci	address_markers[i++].start_address = IOREMAP_END;
3758c2ecf20Sopenharmony_ci	/* What is the ifdef about? */
3768c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64
3778c2ecf20Sopenharmony_ci	address_markers[i++].start_address =  H_VMEMMAP_START;
3788c2ecf20Sopenharmony_ci#else
3798c2ecf20Sopenharmony_ci	address_markers[i++].start_address =  VMEMMAP_BASE;
3808c2ecf20Sopenharmony_ci#endif
3818c2ecf20Sopenharmony_ci#else /* !CONFIG_PPC64 */
3828c2ecf20Sopenharmony_ci	address_markers[i++].start_address = ioremap_bot;
3838c2ecf20Sopenharmony_ci	address_markers[i++].start_address = IOREMAP_TOP;
3848c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM
3858c2ecf20Sopenharmony_ci	address_markers[i++].start_address = PKMAP_BASE;
3868c2ecf20Sopenharmony_ci	address_markers[i++].start_address = PKMAP_ADDR(LAST_PKMAP);
3878c2ecf20Sopenharmony_ci#endif
3888c2ecf20Sopenharmony_ci	address_markers[i++].start_address = FIXADDR_START;
3898c2ecf20Sopenharmony_ci	address_markers[i++].start_address = FIXADDR_TOP;
3908c2ecf20Sopenharmony_ci#ifdef CONFIG_KASAN
3918c2ecf20Sopenharmony_ci	address_markers[i++].start_address = KASAN_SHADOW_START;
3928c2ecf20Sopenharmony_ci	address_markers[i++].start_address = KASAN_SHADOW_END;
3938c2ecf20Sopenharmony_ci#endif
3948c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC64 */
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int ptdump_show(struct seq_file *m, void *v)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct pg_state st = {
4008c2ecf20Sopenharmony_ci		.seq = m,
4018c2ecf20Sopenharmony_ci		.marker = address_markers,
4028c2ecf20Sopenharmony_ci		.start_address = IS_ENABLED(CONFIG_PPC64) ? PAGE_OFFSET : TASK_SIZE,
4038c2ecf20Sopenharmony_ci	};
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
4068c2ecf20Sopenharmony_ci	if (!radix_enabled())
4078c2ecf20Sopenharmony_ci		st.start_address = KERN_VIRT_START;
4088c2ecf20Sopenharmony_ci#endif
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Traverse kernel page tables */
4118c2ecf20Sopenharmony_ci	walk_pagetables(&st);
4128c2ecf20Sopenharmony_ci	note_page(&st, 0, 0, 0, 0);
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int ptdump_open(struct inode *inode, struct file *file)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	return single_open(file, ptdump_show, NULL);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic const struct file_operations ptdump_fops = {
4238c2ecf20Sopenharmony_ci	.open		= ptdump_open,
4248c2ecf20Sopenharmony_ci	.read		= seq_read,
4258c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
4268c2ecf20Sopenharmony_ci	.release	= single_release,
4278c2ecf20Sopenharmony_ci};
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void build_pgtable_complete_mask(void)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	unsigned int i, j;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
4348c2ecf20Sopenharmony_ci		if (pg_level[i].flag)
4358c2ecf20Sopenharmony_ci			for (j = 0; j < pg_level[i].num; j++)
4368c2ecf20Sopenharmony_ci				pg_level[i].mask |= pg_level[i].flag[j].mask;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DEBUG_WX
4408c2ecf20Sopenharmony_civoid ptdump_check_wx(void)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct pg_state st = {
4438c2ecf20Sopenharmony_ci		.seq = NULL,
4448c2ecf20Sopenharmony_ci		.marker = address_markers,
4458c2ecf20Sopenharmony_ci		.check_wx = true,
4468c2ecf20Sopenharmony_ci		.start_address = IS_ENABLED(CONFIG_PPC64) ? PAGE_OFFSET : TASK_SIZE,
4478c2ecf20Sopenharmony_ci	};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
4508c2ecf20Sopenharmony_ci	if (!radix_enabled())
4518c2ecf20Sopenharmony_ci		st.start_address = KERN_VIRT_START;
4528c2ecf20Sopenharmony_ci#endif
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	walk_pagetables(&st);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (st.wx_pages)
4578c2ecf20Sopenharmony_ci		pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found\n",
4588c2ecf20Sopenharmony_ci			st.wx_pages);
4598c2ecf20Sopenharmony_ci	else
4608c2ecf20Sopenharmony_ci		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci#endif
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int ptdump_init(void)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	populate_markers();
4678c2ecf20Sopenharmony_ci	build_pgtable_complete_mask();
4688c2ecf20Sopenharmony_ci	debugfs_create_file("kernel_page_tables", 0400, NULL, NULL,
4698c2ecf20Sopenharmony_ci			    &ptdump_fops);
4708c2ecf20Sopenharmony_ci	return 0;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_cidevice_initcall(ptdump_init);
473