File Index
Files and directories
Sign in
FILE

ptdump.c

File preview with syntax highlighting.

linux-6.19.14/arch/riscv/mm/ptdump.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2019 SiFive
  4 */
  5
  6#include <linux/efi.h>
  7#include <linux/init.h>
  8#include <linux/debugfs.h>
  9#include <linux/seq_file.h>
 10#include <linux/ptdump.h>
 11
 12#include <linux/pgtable.h>
 13#include <asm/kasan.h>
 14
 15#define pt_dump_seq_printf(m, fmt, args...)	\
 16({						\
 17	if (m)					\
 18		seq_printf(m, fmt, ##args);	\
 19})
 20
 21#define pt_dump_seq_puts(m, fmt)	\
 22({					\
 23	if (m)				\
 24		seq_puts(m, fmt);	\
 25})
 26
 27/*
 28 * The page dumper groups page table entries of the same type into a single
 29 * description. It uses pg_state to track the range information while
 30 * iterating over the pte entries. When the continuity is broken it then
 31 * dumps out a description of the range.
 32 */
 33struct pg_state {
 34	struct ptdump_state ptdump;
 35	struct seq_file *seq;
 36	const struct addr_marker *marker;
 37	unsigned long start_address;
 38	unsigned long start_pa;
 39	unsigned long last_pa;
 40	int level;
 41	u64 current_prot;
 42	bool check_wx;
 43	unsigned long wx_pages;
 44};
 45
 46/* Address marker */
 47struct addr_marker {
 48	unsigned long start_address;
 49	const char *name;
 50};
 51
 52/* Private information for debugfs */
 53struct ptd_mm_info {
 54	struct mm_struct		*mm;
 55	const struct addr_marker	*markers;
 56	unsigned long base_addr;
 57	unsigned long end;
 58};
 59
 60enum address_markers_idx {
 61	FIXMAP_START_NR,
 62	FIXMAP_END_NR,
 63	PCI_IO_START_NR,
 64	PCI_IO_END_NR,
 65#ifdef CONFIG_SPARSEMEM_VMEMMAP
 66	VMEMMAP_START_NR,
 67	VMEMMAP_END_NR,
 68#endif
 69	VMALLOC_START_NR,
 70	VMALLOC_END_NR,
 71	PAGE_OFFSET_NR,
 72#ifdef CONFIG_KASAN
 73	KASAN_SHADOW_START_NR,
 74	KASAN_SHADOW_END_NR,
 75#endif
 76#ifdef CONFIG_64BIT
 77	MODULES_MAPPING_NR,
 78	KERNEL_MAPPING_NR,
 79#endif
 80	END_OF_SPACE_NR
 81};
 82
 83static struct addr_marker address_markers[] = {
 84	{0, "Fixmap start"},
 85	{0, "Fixmap end"},
 86	{0, "PCI I/O start"},
 87	{0, "PCI I/O end"},
 88#ifdef CONFIG_SPARSEMEM_VMEMMAP
 89	{0, "vmemmap start"},
 90	{0, "vmemmap end"},
 91#endif
 92	{0, "vmalloc() area"},
 93	{0, "vmalloc() end"},
 94	{0, "Linear mapping"},
 95#ifdef CONFIG_KASAN
 96	{0, "Kasan shadow start"},
 97	{0, "Kasan shadow end"},
 98#endif
 99#ifdef CONFIG_64BIT
100	{0, "Modules/BPF mapping"},
101	{0, "Kernel mapping"},
102#endif
103	{-1, NULL},
104};
105
106static struct ptd_mm_info kernel_ptd_info = {
107	.mm		= &init_mm,
108	.markers	= address_markers,
109	.base_addr	= 0,
110	.end		= ULONG_MAX,
111};
112
113#ifdef CONFIG_EFI
114static struct addr_marker efi_addr_markers[] = {
115		{ 0,		"UEFI runtime start" },
116		{ SZ_1G,	"UEFI runtime end" },
117		{ -1,		NULL }
118};
119
120static struct ptd_mm_info efi_ptd_info = {
121	.mm		= &efi_mm,
122	.markers	= efi_addr_markers,
123	.base_addr	= 0,
124	.end		= SZ_2G,
125};
126#endif
127
128/* Page Table Entry */
129struct prot_bits {
130	u64 mask;
131	const char *set;
132	const char *clear;
133};
134
135static const struct prot_bits pte_bits[] = {
136	{
137#ifdef CONFIG_64BIT
138		.mask = _PAGE_NAPOT,
139		.set = "N",
140		.clear = ".",
141	}, {
142		.mask = _PAGE_MTMASK_SVPBMT,
143		.set = "MT(%s)",
144		.clear = "  ..  ",
145	}, {
146#endif
147		.mask = _PAGE_SOFT,
148		.set = "RSW(%d)",
149		.clear = "  ..  ",
150	}, {
151		.mask = _PAGE_DIRTY,
152		.set = "D",
153		.clear = ".",
154	}, {
155		.mask = _PAGE_ACCESSED,
156		.set = "A",
157		.clear = ".",
158	}, {
159		.mask = _PAGE_GLOBAL,
160		.set = "G",
161		.clear = ".",
162	}, {
163		.mask = _PAGE_USER,
164		.set = "U",
165		.clear = ".",
166	}, {
167		.mask = _PAGE_EXEC,
168		.set = "X",
169		.clear = ".",
170	}, {
171		.mask = _PAGE_WRITE,
172		.set = "W",
173		.clear = ".",
174	}, {
175		.mask = _PAGE_READ,
176		.set = "R",
177		.clear = ".",
178	}, {
179		.mask = _PAGE_PRESENT,
180		.set = "V",
181		.clear = ".",
182	}
183};
184
185/* Page Level */
186struct pg_level {
187	const char *name;
188	u64 mask;
189};
190
191static struct pg_level pg_level[] = {
192	{ /* pgd */
193		.name = "PGD",
194	}, { /* p4d */
195		.name = (CONFIG_PGTABLE_LEVELS > 4) ? "P4D" : "PGD",
196	}, { /* pud */
197		.name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
198	}, { /* pmd */
199		.name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
200	}, { /* pte */
201		.name = "PTE",
202	},
203};
204
205static void dump_prot(struct pg_state *st)
206{
207	unsigned int i;
208
209	for (i = 0; i < ARRAY_SIZE(pte_bits); i++) {
210		char s[7];
211		unsigned long val;
212
213		val = st->current_prot & pte_bits[i].mask;
214		if (val) {
215			if (pte_bits[i].mask == _PAGE_SOFT)
216				sprintf(s, pte_bits[i].set, val >> 8);
217#ifdef CONFIG_64BIT
218			else if (pte_bits[i].mask == _PAGE_MTMASK_SVPBMT) {
219				if (val == _PAGE_NOCACHE_SVPBMT)
220					sprintf(s, pte_bits[i].set, "NC");
221				else if (val == _PAGE_IO_SVPBMT)
222					sprintf(s, pte_bits[i].set, "IO");
223				else
224					sprintf(s, pte_bits[i].set, "??");
225			}
226#endif
227			else
228				sprintf(s, "%s", pte_bits[i].set);
229		} else {
230			sprintf(s, "%s", pte_bits[i].clear);
231		}
232
233		pt_dump_seq_printf(st->seq, " %s", s);
234	}
235}
236
237#ifdef CONFIG_64BIT
238#define ADDR_FORMAT	"0x%016lx"
239#else
240#define ADDR_FORMAT	"0x%08lx"
241#endif
242static void dump_addr(struct pg_state *st, unsigned long addr)
243{
244	static const char units[] = "KMGTPE";
245	const char *unit = units;
246	unsigned long delta;
247
248	pt_dump_seq_printf(st->seq, ADDR_FORMAT "-" ADDR_FORMAT "   ",
249			   st->start_address, addr);
250
251	pt_dump_seq_printf(st->seq, " " ADDR_FORMAT " ", st->start_pa);
252	delta = (addr - st->start_address) >> 10;
253
254	while (!(delta & 1023) && unit[1]) {
255		delta >>= 10;
256		unit++;
257	}
258
259	pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
260			   pg_level[st->level].name);
261}
262
263static void note_prot_wx(struct pg_state *st, unsigned long addr)
264{
265	if (!st->check_wx)
266		return;
267
268	if ((st->current_prot & (_PAGE_WRITE | _PAGE_EXEC)) !=
269	    (_PAGE_WRITE | _PAGE_EXEC))
270		return;
271
272	WARN_ONCE(1, "riscv/mm: Found insecure W+X mapping at address %p/%pS\n",
273		  (void *)st->start_address, (void *)st->start_address);
274
275	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
276}
277
278static void note_page(struct ptdump_state *pt_st, unsigned long addr,
279		      int level, u64 val)
280{
281	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
282	u64 pa = PFN_PHYS(pte_pfn(__pte(val)));
283	u64 prot = 0;
284
285	if (level >= 0)
286		prot = val & pg_level[level].mask;
287
288	if (st->level == -1) {
289		st->level = level;
290		st->current_prot = prot;
291		st->start_address = addr;
292		st->start_pa = pa;
293		st->last_pa = pa;
294		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
295	} else if (prot != st->current_prot ||
296		   level != st->level || addr >= st->marker[1].start_address) {
297		if (st->current_prot) {
298			note_prot_wx(st, addr);
299			dump_addr(st, addr);
300			dump_prot(st);
301			pt_dump_seq_puts(st->seq, "\n");
302		}
303
304		while (addr >= st->marker[1].start_address) {
305			st->marker++;
306			pt_dump_seq_printf(st->seq, "---[ %s ]---\n",
307					   st->marker->name);
308		}
309
310		st->start_address = addr;
311		st->start_pa = pa;
312		st->last_pa = pa;
313		st->current_prot = prot;
314		st->level = level;
315	} else {
316		st->last_pa = pa;
317	}
318}
319
320static void note_page_pte(struct ptdump_state *pt_st, unsigned long addr, pte_t pte)
321{
322	note_page(pt_st, addr, 4, pte_val(pte));
323}
324
325static void note_page_pmd(struct ptdump_state *pt_st, unsigned long addr, pmd_t pmd)
326{
327	note_page(pt_st, addr, 3, pmd_val(pmd));
328}
329
330static void note_page_pud(struct ptdump_state *pt_st, unsigned long addr, pud_t pud)
331{
332	note_page(pt_st, addr, 2, pud_val(pud));
333}
334
335static void note_page_p4d(struct ptdump_state *pt_st, unsigned long addr, p4d_t p4d)
336{
337	note_page(pt_st, addr, 1, p4d_val(p4d));
338}
339
340static void note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
341{
342	note_page(pt_st, addr, 0, pgd_val(pgd));
343}
344
345static void note_page_flush(struct ptdump_state *pt_st)
346{
347	pte_t pte_zero = {0};
348
349	note_page(pt_st, 0, -1, pte_val(pte_zero));
350}
351
352static void ptdump_walk(struct seq_file *s, struct ptd_mm_info *pinfo)
353{
354	struct pg_state st = {
355		.seq = s,
356		.marker = pinfo->markers,
357		.level = -1,
358		.ptdump = {
359			.note_page_pte = note_page_pte,
360			.note_page_pmd = note_page_pmd,
361			.note_page_pud = note_page_pud,
362			.note_page_p4d = note_page_p4d,
363			.note_page_pgd = note_page_pgd,
364			.note_page_flush = note_page_flush,
365			.range = (struct ptdump_range[]) {
366				{pinfo->base_addr, pinfo->end},
367				{0, 0}
368			}
369		}
370	};
371
372	ptdump_walk_pgd(&st.ptdump, pinfo->mm, NULL);
373}
374
375bool ptdump_check_wx(void)
376{
377	struct pg_state st = {
378		.seq = NULL,
379		.marker = (struct addr_marker[]) {
380			{0, NULL},
381			{-1, NULL},
382		},
383		.level = -1,
384		.check_wx = true,
385		.ptdump = {
386			.note_page_pte = note_page_pte,
387			.note_page_pmd = note_page_pmd,
388			.note_page_pud = note_page_pud,
389			.note_page_p4d = note_page_p4d,
390			.note_page_pgd = note_page_pgd,
391			.note_page_flush = note_page_flush,
392			.range = (struct ptdump_range[]) {
393				{KERN_VIRT_START, ULONG_MAX},
394				{0, 0}
395			}
396		}
397	};
398
399	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
400
401	if (st.wx_pages) {
402		pr_warn("Checked W+X mappings: failed, %lu W+X pages found\n",
403			st.wx_pages);
404
405		return false;
406	} else {
407		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
408
409		return true;
410	}
411}
412
413static int ptdump_show(struct seq_file *m, void *v)
414{
415	ptdump_walk(m, m->private);
416
417	return 0;
418}
419
420DEFINE_SHOW_ATTRIBUTE(ptdump);
421
422static int __init ptdump_init(void)
423{
424	unsigned int i, j;
425
426	address_markers[FIXMAP_START_NR].start_address = FIXADDR_START;
427	address_markers[FIXMAP_END_NR].start_address = FIXADDR_TOP;
428	address_markers[PCI_IO_START_NR].start_address = PCI_IO_START;
429	address_markers[PCI_IO_END_NR].start_address = PCI_IO_END;
430#ifdef CONFIG_SPARSEMEM_VMEMMAP
431	address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START;
432	address_markers[VMEMMAP_END_NR].start_address = VMEMMAP_END;
433#endif
434	address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
435	address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
436	address_markers[PAGE_OFFSET_NR].start_address = PAGE_OFFSET;
437#ifdef CONFIG_KASAN
438	address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START;
439	address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END;
440#endif
441#ifdef CONFIG_64BIT
442	address_markers[MODULES_MAPPING_NR].start_address = MODULES_VADDR;
443	address_markers[KERNEL_MAPPING_NR].start_address = kernel_map.virt_addr;
444#endif
445
446	kernel_ptd_info.base_addr = KERN_VIRT_START;
447
448	pg_level[1].name = pgtable_l5_enabled ? "P4D" : "PGD";
449	pg_level[2].name = pgtable_l4_enabled ? "PUD" : "PGD";
450
451	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
452		for (j = 0; j < ARRAY_SIZE(pte_bits); j++)
453			pg_level[i].mask |= pte_bits[j].mask;
454
455	debugfs_create_file("kernel_page_tables", 0400, NULL, &kernel_ptd_info,
456			    &ptdump_fops);
457#ifdef CONFIG_EFI
458	if (efi_enabled(EFI_RUNTIME_SERVICES))
459		debugfs_create_file("efi_page_tables", 0400, NULL, &efi_ptd_info,
460				    &ptdump_fops);
461#endif
462
463	return 0;
464}
465
466device_initcall(ptdump_init);
~/ Read-only file preview.