1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
4 * Debug helper to dump the current kernel pagetables of the system
5 * so that we can see what the various memory ranges are set to.
6 *
7 * Derived from x86 and arm implementation:
8 * (C) Copyright 2008 Intel Corporation
9 *
10 * Author: Arjan van de Ven <arjan@linux.intel.com>
11 */
12#include <linux/debugfs.h>
13#include <linux/errno.h>
14#include <linux/fs.h>
15#include <linux/io.h>
16#include <linux/init.h>
17#include <linux/mm.h>
18#include <linux/ptdump.h>
19#include <linux/sched.h>
20#include <linux/seq_file.h>
21
22#include <asm/fixmap.h>
23#include <asm/kasan.h>
24#include <asm/memory.h>
25#include <asm/pgtable-hwdef.h>
26#include <asm/ptdump.h>
27
28
29#define pt_dump_seq_printf(m, fmt, args...)	\
30({						\
31	if (m)					\
32		seq_printf(m, fmt, ##args);	\
33})
34
35#define pt_dump_seq_puts(m, fmt)	\
36({					\
37	if (m)				\
38		seq_printf(m, fmt);	\
39})
40
41/*
42 * The page dumper groups page table entries of the same type into a single
43 * description. It uses pg_state to track the range information while
44 * iterating over the pte entries. When the continuity is broken it then
45 * dumps out a description of the range.
46 */
47struct pg_state {
48	struct ptdump_state ptdump;
49	struct seq_file *seq;
50	const struct addr_marker *marker;
51	const struct mm_struct *mm;
52	unsigned long start_address;
53	int level;
54	u64 current_prot;
55	bool check_wx;
56	unsigned long wx_pages;
57	unsigned long uxn_pages;
58};
59
60struct prot_bits {
61	u64		mask;
62	u64		val;
63	const char	*set;
64	const char	*clear;
65};
66
67static const struct prot_bits pte_bits[] = {
68	{
69		.mask	= PTE_VALID,
70		.val	= PTE_VALID,
71		.set	= " ",
72		.clear	= "F",
73	}, {
74		.mask	= PTE_USER,
75		.val	= PTE_USER,
76		.set	= "USR",
77		.clear	= "   ",
78	}, {
79		.mask	= PTE_RDONLY,
80		.val	= PTE_RDONLY,
81		.set	= "ro",
82		.clear	= "RW",
83	}, {
84		.mask	= PTE_PXN,
85		.val	= PTE_PXN,
86		.set	= "NX",
87		.clear	= "x ",
88	}, {
89		.mask	= PTE_SHARED,
90		.val	= PTE_SHARED,
91		.set	= "SHD",
92		.clear	= "   ",
93	}, {
94		.mask	= PTE_AF,
95		.val	= PTE_AF,
96		.set	= "AF",
97		.clear	= "  ",
98	}, {
99		.mask	= PTE_NG,
100		.val	= PTE_NG,
101		.set	= "NG",
102		.clear	= "  ",
103	}, {
104		.mask	= PTE_CONT,
105		.val	= PTE_CONT,
106		.set	= "CON",
107		.clear	= "   ",
108	}, {
109		.mask	= PTE_TABLE_BIT,
110		.val	= PTE_TABLE_BIT,
111		.set	= "   ",
112		.clear	= "BLK",
113	}, {
114		.mask	= PTE_UXN,
115		.val	= PTE_UXN,
116		.set	= "UXN",
117		.clear	= "   ",
118	}, {
119		.mask	= PTE_GP,
120		.val	= PTE_GP,
121		.set	= "GP",
122		.clear	= "  ",
123	}, {
124		.mask	= PTE_ATTRINDX_MASK,
125		.val	= PTE_ATTRINDX(MT_DEVICE_nGnRnE),
126		.set	= "DEVICE/nGnRnE",
127	}, {
128		.mask	= PTE_ATTRINDX_MASK,
129		.val	= PTE_ATTRINDX(MT_DEVICE_nGnRE),
130		.set	= "DEVICE/nGnRE",
131	}, {
132		.mask	= PTE_ATTRINDX_MASK,
133		.val	= PTE_ATTRINDX(MT_NORMAL_NC),
134		.set	= "MEM/NORMAL-NC",
135	}, {
136		.mask	= PTE_ATTRINDX_MASK,
137		.val	= PTE_ATTRINDX(MT_NORMAL),
138		.set	= "MEM/NORMAL",
139	}, {
140		.mask	= PTE_ATTRINDX_MASK,
141		.val	= PTE_ATTRINDX(MT_NORMAL_TAGGED),
142		.set	= "MEM/NORMAL-TAGGED",
143	}
144};
145
146struct pg_level {
147	const struct prot_bits *bits;
148	char name[4];
149	int num;
150	u64 mask;
151};
152
153static struct pg_level pg_level[] __ro_after_init = {
154	{ /* pgd */
155		.name	= "PGD",
156		.bits	= pte_bits,
157		.num	= ARRAY_SIZE(pte_bits),
158	}, { /* p4d */
159		.name	= "P4D",
160		.bits	= pte_bits,
161		.num	= ARRAY_SIZE(pte_bits),
162	}, { /* pud */
163		.name	= "PUD",
164		.bits	= pte_bits,
165		.num	= ARRAY_SIZE(pte_bits),
166	}, { /* pmd */
167		.name	= "PMD",
168		.bits	= pte_bits,
169		.num	= ARRAY_SIZE(pte_bits),
170	}, { /* pte */
171		.name	= "PTE",
172		.bits	= pte_bits,
173		.num	= ARRAY_SIZE(pte_bits),
174	},
175};
176
177static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
178			size_t num)
179{
180	unsigned i;
181
182	for (i = 0; i < num; i++, bits++) {
183		const char *s;
184
185		if ((st->current_prot & bits->mask) == bits->val)
186			s = bits->set;
187		else
188			s = bits->clear;
189
190		if (s)
191			pt_dump_seq_printf(st->seq, " %s", s);
192	}
193}
194
195static void note_prot_uxn(struct pg_state *st, unsigned long addr)
196{
197	if (!st->check_wx)
198		return;
199
200	if ((st->current_prot & PTE_UXN) == PTE_UXN)
201		return;
202
203	WARN_ONCE(1, "arm64/mm: Found non-UXN mapping at address %p/%pS\n",
204		  (void *)st->start_address, (void *)st->start_address);
205
206	st->uxn_pages += (addr - st->start_address) / PAGE_SIZE;
207}
208
209static void note_prot_wx(struct pg_state *st, unsigned long addr)
210{
211	if (!st->check_wx)
212		return;
213	if ((st->current_prot & PTE_RDONLY) == PTE_RDONLY)
214		return;
215	if ((st->current_prot & PTE_PXN) == PTE_PXN)
216		return;
217
218	WARN_ONCE(1, "arm64/mm: Found insecure W+X mapping at address %p/%pS\n",
219		  (void *)st->start_address, (void *)st->start_address);
220
221	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
222}
223
224static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
225		      u64 val)
226{
227	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
228	static const char units[] = "KMGTPE";
229	u64 prot = 0;
230
231	/* check if the current level has been folded dynamically */
232	if ((level == 1 && mm_p4d_folded(st->mm)) ||
233	    (level == 2 && mm_pud_folded(st->mm)))
234		level = 0;
235
236	if (level >= 0)
237		prot = val & pg_level[level].mask;
238
239	if (st->level == -1) {
240		st->level = level;
241		st->current_prot = prot;
242		st->start_address = addr;
243		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
244	} else if (prot != st->current_prot || level != st->level ||
245		   addr >= st->marker[1].start_address) {
246		const char *unit = units;
247		unsigned long delta;
248
249		if (st->current_prot) {
250			note_prot_uxn(st, addr);
251			note_prot_wx(st, addr);
252		}
253
254		pt_dump_seq_printf(st->seq, "0x%016lx-0x%016lx   ",
255				   st->start_address, addr);
256
257		delta = (addr - st->start_address) >> 10;
258		while (!(delta & 1023) && unit[1]) {
259			delta >>= 10;
260			unit++;
261		}
262		pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
263				   pg_level[st->level].name);
264		if (st->current_prot && pg_level[st->level].bits)
265			dump_prot(st, pg_level[st->level].bits,
266				  pg_level[st->level].num);
267		pt_dump_seq_puts(st->seq, "\n");
268
269		if (addr >= st->marker[1].start_address) {
270			st->marker++;
271			pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
272		}
273
274		st->start_address = addr;
275		st->current_prot = prot;
276		st->level = level;
277	}
278
279	if (addr >= st->marker[1].start_address) {
280		st->marker++;
281		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
282	}
283
284}
285
286void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
287{
288	unsigned long end = ~0UL;
289	struct pg_state st;
290
291	if (info->base_addr < TASK_SIZE_64)
292		end = TASK_SIZE_64;
293
294	st = (struct pg_state){
295		.seq = s,
296		.marker = info->markers,
297		.mm = info->mm,
298		.level = -1,
299		.ptdump = {
300			.note_page = note_page,
301			.range = (struct ptdump_range[]){
302				{info->base_addr, end},
303				{0, 0}
304			}
305		}
306	};
307
308	ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
309}
310
311static void __init ptdump_initialize(void)
312{
313	unsigned i, j;
314
315	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
316		if (pg_level[i].bits)
317			for (j = 0; j < pg_level[i].num; j++)
318				pg_level[i].mask |= pg_level[i].bits[j].mask;
319}
320
321static struct ptdump_info kernel_ptdump_info __ro_after_init = {
322	.mm		= &init_mm,
323};
324
325bool ptdump_check_wx(void)
326{
327	struct pg_state st = {
328		.seq = NULL,
329		.marker = (struct addr_marker[]) {
330			{ 0, NULL},
331			{ -1, NULL},
332		},
333		.level = -1,
334		.check_wx = true,
335		.ptdump = {
336			.note_page = note_page,
337			.range = (struct ptdump_range[]) {
338				{_PAGE_OFFSET(vabits_actual), ~0UL},
339				{0, 0}
340			}
341		}
342	};
343
344	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
345
346	if (st.wx_pages || st.uxn_pages) {
347		pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
348			st.wx_pages, st.uxn_pages);
349
350		return false;
351	} else {
352		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
353
354		return true;
355	}
356}
357
358static int __init ptdump_init(void)
359{
360	u64 page_offset = _PAGE_OFFSET(vabits_actual);
361	u64 vmemmap_start = (u64)virt_to_page((void *)page_offset);
362	struct addr_marker m[] = {
363		{ PAGE_OFFSET,		"Linear Mapping start" },
364		{ PAGE_END,		"Linear Mapping end" },
365#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
366		{ KASAN_SHADOW_START,   "Kasan shadow start" },
367		{ KASAN_SHADOW_END,     "Kasan shadow end" },
368#endif
369		{ MODULES_VADDR,	"Modules start" },
370		{ MODULES_END,		"Modules end" },
371		{ VMALLOC_START,	"vmalloc() area" },
372		{ VMALLOC_END,		"vmalloc() end" },
373		{ vmemmap_start,	"vmemmap start" },
374		{ VMEMMAP_END,		"vmemmap end" },
375		{ PCI_IO_START,		"PCI I/O start" },
376		{ PCI_IO_END,		"PCI I/O end" },
377		{ FIXADDR_TOT_START,    "Fixmap start" },
378		{ FIXADDR_TOP,	        "Fixmap end" },
379		{ -1,			NULL },
380	};
381	static struct addr_marker address_markers[ARRAY_SIZE(m)] __ro_after_init;
382
383	kernel_ptdump_info.markers = memcpy(address_markers, m, sizeof(m));
384	kernel_ptdump_info.base_addr = page_offset;
385
386	ptdump_initialize();
387	ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
388	return 0;
389}
390device_initcall(ptdump_init);
391