Linux Audio

Check our new training course

Loading...
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2019 SiFive
  4 */
  5
 
  6#include <linux/init.h>
  7#include <linux/debugfs.h>
  8#include <linux/seq_file.h>
  9#include <linux/ptdump.h>
 10
 11#include <asm/ptdump.h>
 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_printf(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
 52static struct addr_marker address_markers[] = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 53#ifdef CONFIG_KASAN
 54	{KASAN_SHADOW_START,	"Kasan shadow start"},
 55	{KASAN_SHADOW_END,	"Kasan shadow end"},
 
 
 
 
 56#endif
 57	{FIXADDR_START,		"Fixmap start"},
 58	{FIXADDR_TOP,		"Fixmap end"},
 59	{PCI_IO_START,		"PCI I/O start"},
 60	{PCI_IO_END,		"PCI I/O end"},
 
 
 
 
 61#ifdef CONFIG_SPARSEMEM_VMEMMAP
 62	{VMEMMAP_START,		"vmemmap start"},
 63	{VMEMMAP_END,		"vmemmap end"},
 
 
 
 
 
 
 
 
 
 
 
 64#endif
 65	{VMALLOC_START,		"vmalloc() area"},
 66	{VMALLOC_END,		"vmalloc() end"},
 67	{PAGE_OFFSET,		"Linear mapping"},
 68	{-1, NULL},
 69};
 70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 71/* Page Table Entry */
 72struct prot_bits {
 73	u64 mask;
 74	u64 val;
 75	const char *set;
 76	const char *clear;
 77};
 78
 79static const struct prot_bits pte_bits[] = {
 80	{
 
 
 
 
 
 
 
 
 
 
 81		.mask = _PAGE_SOFT,
 82		.val = _PAGE_SOFT,
 83		.set = "RSW",
 84		.clear = "   ",
 85	}, {
 86		.mask = _PAGE_DIRTY,
 87		.val = _PAGE_DIRTY,
 88		.set = "D",
 89		.clear = ".",
 90	}, {
 91		.mask = _PAGE_ACCESSED,
 92		.val = _PAGE_ACCESSED,
 93		.set = "A",
 94		.clear = ".",
 95	}, {
 96		.mask = _PAGE_GLOBAL,
 97		.val = _PAGE_GLOBAL,
 98		.set = "G",
 99		.clear = ".",
100	}, {
101		.mask = _PAGE_USER,
102		.val = _PAGE_USER,
103		.set = "U",
104		.clear = ".",
105	}, {
106		.mask = _PAGE_EXEC,
107		.val = _PAGE_EXEC,
108		.set = "X",
109		.clear = ".",
110	}, {
111		.mask = _PAGE_WRITE,
112		.val = _PAGE_WRITE,
113		.set = "W",
114		.clear = ".",
115	}, {
116		.mask = _PAGE_READ,
117		.val = _PAGE_READ,
118		.set = "R",
119		.clear = ".",
120	}, {
121		.mask = _PAGE_PRESENT,
122		.val = _PAGE_PRESENT,
123		.set = "V",
124		.clear = ".",
125	}
126};
127
128/* Page Level */
129struct pg_level {
130	const char *name;
131	u64 mask;
132};
133
134static struct pg_level pg_level[] = {
135	{ /* pgd */
136		.name = "PGD",
137	}, { /* p4d */
138		.name = (CONFIG_PGTABLE_LEVELS > 4) ? "P4D" : "PGD",
139	}, { /* pud */
140		.name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
141	}, { /* pmd */
142		.name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
143	}, { /* pte */
144		.name = "PTE",
145	},
146};
147
148static void dump_prot(struct pg_state *st)
149{
150	unsigned int i;
151
152	for (i = 0; i < ARRAY_SIZE(pte_bits); i++) {
153		const char *s;
 
154
155		if ((st->current_prot & pte_bits[i].mask) == pte_bits[i].val)
156			s = pte_bits[i].set;
157		else
158			s = pte_bits[i].clear;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
160		if (s)
161			pt_dump_seq_printf(st->seq, " %s", s);
162	}
163}
164
165#ifdef CONFIG_64BIT
166#define ADDR_FORMAT	"0x%016lx"
167#else
168#define ADDR_FORMAT	"0x%08lx"
169#endif
170static void dump_addr(struct pg_state *st, unsigned long addr)
171{
172	static const char units[] = "KMGTPE";
173	const char *unit = units;
174	unsigned long delta;
175
176	pt_dump_seq_printf(st->seq, ADDR_FORMAT "-" ADDR_FORMAT "   ",
177			   st->start_address, addr);
178
179	pt_dump_seq_printf(st->seq, " " ADDR_FORMAT " ", st->start_pa);
180	delta = (addr - st->start_address) >> 10;
181
182	while (!(delta & 1023) && unit[1]) {
183		delta >>= 10;
184		unit++;
185	}
186
187	pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
188			   pg_level[st->level].name);
189}
190
191static void note_prot_wx(struct pg_state *st, unsigned long addr)
192{
193	if (!st->check_wx)
194		return;
195
196	if ((st->current_prot & (_PAGE_WRITE | _PAGE_EXEC)) !=
197	    (_PAGE_WRITE | _PAGE_EXEC))
198		return;
199
200	WARN_ONCE(1, "riscv/mm: Found insecure W+X mapping at address %p/%pS\n",
201		  (void *)st->start_address, (void *)st->start_address);
202
203	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
204}
205
206static void note_page(struct ptdump_state *pt_st, unsigned long addr,
207		      int level, u64 val)
208{
209	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
210	u64 pa = PFN_PHYS(pte_pfn(__pte(val)));
211	u64 prot = 0;
212
213	if (level >= 0)
214		prot = val & pg_level[level].mask;
215
216	if (st->level == -1) {
217		st->level = level;
218		st->current_prot = prot;
219		st->start_address = addr;
220		st->start_pa = pa;
221		st->last_pa = pa;
222		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
223	} else if (prot != st->current_prot ||
224		   level != st->level || addr >= st->marker[1].start_address) {
225		if (st->current_prot) {
226			note_prot_wx(st, addr);
227			dump_addr(st, addr);
228			dump_prot(st);
229			pt_dump_seq_puts(st->seq, "\n");
230		}
231
232		while (addr >= st->marker[1].start_address) {
233			st->marker++;
234			pt_dump_seq_printf(st->seq, "---[ %s ]---\n",
235					   st->marker->name);
236		}
237
238		st->start_address = addr;
239		st->start_pa = pa;
240		st->last_pa = pa;
241		st->current_prot = prot;
242		st->level = level;
243	} else {
244		st->last_pa = pa;
245	}
246}
247
248static void ptdump_walk(struct seq_file *s)
249{
250	struct pg_state st = {
251		.seq = s,
252		.marker = address_markers,
253		.level = -1,
254		.ptdump = {
255			.note_page = note_page,
256			.range = (struct ptdump_range[]) {
257				{KERN_VIRT_START, ULONG_MAX},
258				{0, 0}
259			}
260		}
261	};
262
263	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
264}
265
266void ptdump_check_wx(void)
267{
268	struct pg_state st = {
269		.seq = NULL,
270		.marker = (struct addr_marker[]) {
271			{0, NULL},
272			{-1, NULL},
273		},
274		.level = -1,
275		.check_wx = true,
276		.ptdump = {
277			.note_page = note_page,
278			.range = (struct ptdump_range[]) {
279				{KERN_VIRT_START, ULONG_MAX},
280				{0, 0}
281			}
282		}
283	};
284
285	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
286
287	if (st.wx_pages)
288		pr_warn("Checked W+X mappings: failed, %lu W+X pages found\n",
289			st.wx_pages);
290	else
 
 
291		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
 
 
 
292}
293
294static int ptdump_show(struct seq_file *m, void *v)
295{
296	ptdump_walk(m);
297
298	return 0;
299}
300
301DEFINE_SHOW_ATTRIBUTE(ptdump);
302
303static int ptdump_init(void)
304{
305	unsigned int i, j;
306
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
308		for (j = 0; j < ARRAY_SIZE(pte_bits); j++)
309			pg_level[i].mask |= pte_bits[j].mask;
310
311	debugfs_create_file("kernel_page_tables", 0400, NULL, NULL,
312			    &ptdump_fops);
 
 
 
 
 
313
314	return 0;
315}
316
317device_initcall(ptdump_init);
v6.9.4
  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_printf(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 ptdump_walk(struct seq_file *s, struct ptd_mm_info *pinfo)
321{
322	struct pg_state st = {
323		.seq = s,
324		.marker = pinfo->markers,
325		.level = -1,
326		.ptdump = {
327			.note_page = note_page,
328			.range = (struct ptdump_range[]) {
329				{pinfo->base_addr, pinfo->end},
330				{0, 0}
331			}
332		}
333	};
334
335	ptdump_walk_pgd(&st.ptdump, pinfo->mm, NULL);
336}
337
338bool ptdump_check_wx(void)
339{
340	struct pg_state st = {
341		.seq = NULL,
342		.marker = (struct addr_marker[]) {
343			{0, NULL},
344			{-1, NULL},
345		},
346		.level = -1,
347		.check_wx = true,
348		.ptdump = {
349			.note_page = note_page,
350			.range = (struct ptdump_range[]) {
351				{KERN_VIRT_START, ULONG_MAX},
352				{0, 0}
353			}
354		}
355	};
356
357	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
358
359	if (st.wx_pages) {
360		pr_warn("Checked W+X mappings: failed, %lu W+X pages found\n",
361			st.wx_pages);
362
363		return false;
364	} else {
365		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
366
367		return true;
368	}
369}
370
371static int ptdump_show(struct seq_file *m, void *v)
372{
373	ptdump_walk(m, m->private);
374
375	return 0;
376}
377
378DEFINE_SHOW_ATTRIBUTE(ptdump);
379
380static int __init ptdump_init(void)
381{
382	unsigned int i, j;
383
384	address_markers[FIXMAP_START_NR].start_address = FIXADDR_START;
385	address_markers[FIXMAP_END_NR].start_address = FIXADDR_TOP;
386	address_markers[PCI_IO_START_NR].start_address = PCI_IO_START;
387	address_markers[PCI_IO_END_NR].start_address = PCI_IO_END;
388#ifdef CONFIG_SPARSEMEM_VMEMMAP
389	address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START;
390	address_markers[VMEMMAP_END_NR].start_address = VMEMMAP_END;
391#endif
392	address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
393	address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
394	address_markers[PAGE_OFFSET_NR].start_address = PAGE_OFFSET;
395#ifdef CONFIG_KASAN
396	address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START;
397	address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END;
398#endif
399#ifdef CONFIG_64BIT
400	address_markers[MODULES_MAPPING_NR].start_address = MODULES_VADDR;
401	address_markers[KERNEL_MAPPING_NR].start_address = kernel_map.virt_addr;
402#endif
403
404	kernel_ptd_info.base_addr = KERN_VIRT_START;
405
406	pg_level[1].name = pgtable_l5_enabled ? "P4D" : "PGD";
407	pg_level[2].name = pgtable_l4_enabled ? "PUD" : "PGD";
408
409	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
410		for (j = 0; j < ARRAY_SIZE(pte_bits); j++)
411			pg_level[i].mask |= pte_bits[j].mask;
412
413	debugfs_create_file("kernel_page_tables", 0400, NULL, &kernel_ptd_info,
414			    &ptdump_fops);
415#ifdef CONFIG_EFI
416	if (efi_enabled(EFI_RUNTIME_SERVICES))
417		debugfs_create_file("efi_page_tables", 0400, NULL, &efi_ptd_info,
418				    &ptdump_fops);
419#endif
420
421	return 0;
422}
423
424device_initcall(ptdump_init);