ptdump.c (8650B)
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 */ 34struct 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 */ 48struct addr_marker { 49 unsigned long start_address; 50 const char *name; 51}; 52 53/* Private information for debugfs */ 54struct 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 61enum 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 84static 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 107static 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 115static struct addr_marker efi_addr_markers[] = { 116 { 0, "UEFI runtime start" }, 117 { SZ_1G, "UEFI runtime end" }, 118 { -1, NULL } 119}; 120 121static 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 */ 130struct prot_bits { 131 u64 mask; 132 u64 val; 133 const char *set; 134 const char *clear; 135}; 136 137static 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 */ 187struct pg_level { 188 const char *name; 189 u64 mask; 190}; 191 192static 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 206static 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 228static 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 249static 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 264static 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 306static 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 324void 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 352static int ptdump_show(struct seq_file *m, void *v) 353{ 354 ptdump_walk(m, m->private); 355 356 return 0; 357} 358 359DEFINE_SHOW_ATTRIBUTE(ptdump); 360 361static 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 402device_initcall(ptdump_init);