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 */ 33 struct 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 */ 47 struct addr_marker { 48 unsigned long start_address; 49 const char *name; 50 }; 51 52 static 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 */ 72 struct prot_bits { 73 u64 mask; 74 u64 val; 75 const char *set; 76 const char *clear; 77 }; 78 79 static 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 */ 129 struct pg_level { 130 const char *name; 131 u64 mask; 132 }; 133 134 static 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 148 static 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 170 static 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 191 static 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 206 static 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 248 static 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 266 void 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 294 static int ptdump_show(struct seq_file *m, void *v) 295 { 296 ptdump_walk(m); 297 298 return 0; 299 } 300 301 DEFINE_SHOW_ATTRIBUTE(ptdump); 302 303 static 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 317 device_initcall(ptdump_init); 318