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