Commit | Line | Data |
---|---|---|
926e5392 AV |
1 | /* |
2 | * Debug helper to dump the current kernel pagetables of the system | |
3 | * so that we can see what the various memory ranges are set to. | |
4 | * | |
5 | * (C) Copyright 2008 Intel Corporation | |
6 | * | |
7 | * Author: Arjan van de Ven <arjan@linux.intel.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * as published by the Free Software Foundation; version 2 | |
12 | * of the License. | |
13 | */ | |
14 | ||
fe770bf0 PA |
15 | #include <linux/debugfs.h> |
16 | #include <linux/mm.h> | |
84e629b6 | 17 | #include <linux/init.h> |
146fbb76 | 18 | #include <linux/sched.h> |
926e5392 | 19 | #include <linux/seq_file.h> |
926e5392 | 20 | |
025205f8 | 21 | #include <asm/kasan.h> |
926e5392 AV |
22 | #include <asm/pgtable.h> |
23 | ||
24 | /* | |
25 | * The dumper groups pagetable entries of the same type into one, and for | |
26 | * that it needs to keep some state when walking, and flush this state | |
27 | * when a "break" in the continuity is found. | |
28 | */ | |
29 | struct pg_state { | |
30 | int level; | |
31 | pgprot_t current_prot; | |
32 | unsigned long start_address; | |
33 | unsigned long current_address; | |
fe770bf0 | 34 | const struct addr_marker *marker; |
3891a04a | 35 | unsigned long lines; |
ef6bea6d | 36 | bool to_dmesg; |
e1a58320 SS |
37 | bool check_wx; |
38 | unsigned long wx_pages; | |
926e5392 AV |
39 | }; |
40 | ||
fe770bf0 PA |
41 | struct addr_marker { |
42 | unsigned long start_address; | |
43 | const char *name; | |
3891a04a | 44 | unsigned long max_lines; |
fe770bf0 PA |
45 | }; |
46 | ||
92851e2f AS |
47 | /* indices for address_markers; keep sync'd w/ address_markers below */ |
48 | enum address_markers_idx { | |
49 | USER_SPACE_NR = 0, | |
50 | #ifdef CONFIG_X86_64 | |
51 | KERNEL_SPACE_NR, | |
52 | LOW_KERNEL_NR, | |
53 | VMALLOC_START_NR, | |
54 | VMEMMAP_START_NR, | |
025205f8 AR |
55 | #ifdef CONFIG_KASAN |
56 | KASAN_SHADOW_START_NR, | |
57 | KASAN_SHADOW_END_NR, | |
58 | #endif | |
8a5a5d15 | 59 | # ifdef CONFIG_X86_ESPFIX64 |
3891a04a | 60 | ESPFIX_START_NR, |
8a5a5d15 | 61 | # endif |
92851e2f AS |
62 | HIGH_KERNEL_NR, |
63 | MODULES_VADDR_NR, | |
64 | MODULES_END_NR, | |
65 | #else | |
66 | KERNEL_SPACE_NR, | |
67 | VMALLOC_START_NR, | |
68 | VMALLOC_END_NR, | |
69 | # ifdef CONFIG_HIGHMEM | |
70 | PKMAP_BASE_NR, | |
71 | # endif | |
72 | FIXADDR_START_NR, | |
73 | #endif | |
74 | }; | |
75 | ||
fe770bf0 PA |
76 | /* Address space markers hints */ |
77 | static struct addr_marker address_markers[] = { | |
78 | { 0, "User Space" }, | |
79 | #ifdef CONFIG_X86_64 | |
80 | { 0x8000000000000000UL, "Kernel Space" }, | |
0483e1fa TG |
81 | { 0/* PAGE_OFFSET */, "Low Kernel Mapping" }, |
82 | { 0/* VMALLOC_START */, "vmalloc() Area" }, | |
83 | { 0/* VMEMMAP_START */, "Vmemmap" }, | |
025205f8 AR |
84 | #ifdef CONFIG_KASAN |
85 | { KASAN_SHADOW_START, "KASAN shadow" }, | |
86 | { KASAN_SHADOW_END, "KASAN shadow end" }, | |
87 | #endif | |
8a5a5d15 | 88 | # ifdef CONFIG_X86_ESPFIX64 |
3891a04a | 89 | { ESPFIX_BASE_ADDR, "ESPfix Area", 16 }, |
8266e31e MK |
90 | # endif |
91 | # ifdef CONFIG_EFI | |
92 | { EFI_VA_END, "EFI Runtime Services" }, | |
8a5a5d15 | 93 | # endif |
fe770bf0 | 94 | { __START_KERNEL_map, "High Kernel Mapping" }, |
9a79cf9c YL |
95 | { MODULES_VADDR, "Modules" }, |
96 | { MODULES_END, "End Modules" }, | |
fe770bf0 PA |
97 | #else |
98 | { PAGE_OFFSET, "Kernel Mapping" }, | |
99 | { 0/* VMALLOC_START */, "vmalloc() Area" }, | |
100 | { 0/*VMALLOC_END*/, "vmalloc() End" }, | |
101 | # ifdef CONFIG_HIGHMEM | |
173ae9ba | 102 | { 0/*PKMAP_BASE*/, "Persistent kmap() Area" }, |
fe770bf0 PA |
103 | # endif |
104 | { 0/*FIXADDR_START*/, "Fixmap Area" }, | |
105 | #endif | |
106 | { -1, NULL } /* End of list */ | |
107 | }; | |
926e5392 | 108 | |
fe770bf0 PA |
109 | /* Multipliers for offsets within the PTEs */ |
110 | #define PTE_LEVEL_MULT (PAGE_SIZE) | |
111 | #define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT) | |
112 | #define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT) | |
fdd3d8ce | 113 | #define P4D_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT) |
84bbabc3 | 114 | #define PGD_LEVEL_MULT (PTRS_PER_P4D * P4D_LEVEL_MULT) |
926e5392 | 115 | |
ef6bea6d BP |
116 | #define pt_dump_seq_printf(m, to_dmesg, fmt, args...) \ |
117 | ({ \ | |
118 | if (to_dmesg) \ | |
119 | printk(KERN_INFO fmt, ##args); \ | |
120 | else \ | |
121 | if (m) \ | |
122 | seq_printf(m, fmt, ##args); \ | |
123 | }) | |
124 | ||
125 | #define pt_dump_cont_printf(m, to_dmesg, fmt, args...) \ | |
126 | ({ \ | |
127 | if (to_dmesg) \ | |
128 | printk(KERN_CONT fmt, ##args); \ | |
129 | else \ | |
130 | if (m) \ | |
131 | seq_printf(m, fmt, ##args); \ | |
132 | }) | |
133 | ||
926e5392 AV |
134 | /* |
135 | * Print a readable form of a pgprot_t to the seq_file | |
136 | */ | |
ef6bea6d | 137 | static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg) |
926e5392 | 138 | { |
fe770bf0 PA |
139 | pgprotval_t pr = pgprot_val(prot); |
140 | static const char * const level_name[] = | |
45dcd209 | 141 | { "cr3", "pgd", "p4d", "pud", "pmd", "pte" }; |
fe770bf0 PA |
142 | |
143 | if (!pgprot_val(prot)) { | |
144 | /* Not present */ | |
f439c429 | 145 | pt_dump_cont_printf(m, dmsg, " "); |
fe770bf0 PA |
146 | } else { |
147 | if (pr & _PAGE_USER) | |
ef6bea6d | 148 | pt_dump_cont_printf(m, dmsg, "USR "); |
926e5392 | 149 | else |
ef6bea6d | 150 | pt_dump_cont_printf(m, dmsg, " "); |
fe770bf0 | 151 | if (pr & _PAGE_RW) |
ef6bea6d | 152 | pt_dump_cont_printf(m, dmsg, "RW "); |
fe770bf0 | 153 | else |
ef6bea6d | 154 | pt_dump_cont_printf(m, dmsg, "ro "); |
fe770bf0 | 155 | if (pr & _PAGE_PWT) |
ef6bea6d | 156 | pt_dump_cont_printf(m, dmsg, "PWT "); |
fe770bf0 | 157 | else |
ef6bea6d | 158 | pt_dump_cont_printf(m, dmsg, " "); |
fe770bf0 | 159 | if (pr & _PAGE_PCD) |
ef6bea6d | 160 | pt_dump_cont_printf(m, dmsg, "PCD "); |
926e5392 | 161 | else |
ef6bea6d | 162 | pt_dump_cont_printf(m, dmsg, " "); |
fe770bf0 | 163 | |
f439c429 | 164 | /* Bit 7 has a different meaning on level 3 vs 4 */ |
45dcd209 | 165 | if (level <= 4 && pr & _PAGE_PSE) |
f439c429 JG |
166 | pt_dump_cont_printf(m, dmsg, "PSE "); |
167 | else | |
168 | pt_dump_cont_printf(m, dmsg, " "); | |
45dcd209 KS |
169 | if ((level == 5 && pr & _PAGE_PAT) || |
170 | ((level == 4 || level == 3) && pr & _PAGE_PAT_LARGE)) | |
da25e628 | 171 | pt_dump_cont_printf(m, dmsg, "PAT "); |
f439c429 JG |
172 | else |
173 | pt_dump_cont_printf(m, dmsg, " "); | |
fe770bf0 | 174 | if (pr & _PAGE_GLOBAL) |
ef6bea6d | 175 | pt_dump_cont_printf(m, dmsg, "GLB "); |
fe770bf0 | 176 | else |
ef6bea6d | 177 | pt_dump_cont_printf(m, dmsg, " "); |
fe770bf0 | 178 | if (pr & _PAGE_NX) |
ef6bea6d | 179 | pt_dump_cont_printf(m, dmsg, "NX "); |
fe770bf0 | 180 | else |
ef6bea6d | 181 | pt_dump_cont_printf(m, dmsg, "x "); |
926e5392 | 182 | } |
ef6bea6d | 183 | pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]); |
926e5392 AV |
184 | } |
185 | ||
186 | /* | |
fe770bf0 | 187 | * On 64 bits, sign-extend the 48 bit address to 64 bit |
926e5392 | 188 | */ |
fe770bf0 | 189 | static unsigned long normalize_addr(unsigned long u) |
926e5392 | 190 | { |
3a366f79 KS |
191 | int shift; |
192 | if (!IS_ENABLED(CONFIG_X86_64)) | |
193 | return u; | |
194 | ||
195 | shift = 64 - (__VIRTUAL_MASK_SHIFT + 1); | |
196 | return (signed long)(u << shift) >> shift; | |
926e5392 AV |
197 | } |
198 | ||
199 | /* | |
200 | * This function gets called on a break in a continuous series | |
201 | * of PTE entries; the next one is different so we need to | |
202 | * print what we collected so far. | |
203 | */ | |
204 | static void note_page(struct seq_file *m, struct pg_state *st, | |
fe770bf0 | 205 | pgprot_t new_prot, int level) |
926e5392 | 206 | { |
fe770bf0 | 207 | pgprotval_t prot, cur; |
3891a04a | 208 | static const char units[] = "BKMGTPE"; |
926e5392 AV |
209 | |
210 | /* | |
211 | * If we have a "break" in the series, we need to flush the state that | |
fe770bf0 PA |
212 | * we have now. "break" is either changing perms, levels or |
213 | * address space marker. | |
926e5392 | 214 | */ |
da25e628 TK |
215 | prot = pgprot_val(new_prot); |
216 | cur = pgprot_val(st->current_prot); | |
926e5392 | 217 | |
fe770bf0 PA |
218 | if (!st->level) { |
219 | /* First entry */ | |
220 | st->current_prot = new_prot; | |
221 | st->level = level; | |
222 | st->marker = address_markers; | |
3891a04a | 223 | st->lines = 0; |
ef6bea6d BP |
224 | pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n", |
225 | st->marker->name); | |
fe770bf0 PA |
226 | } else if (prot != cur || level != st->level || |
227 | st->current_address >= st->marker[1].start_address) { | |
228 | const char *unit = units; | |
926e5392 | 229 | unsigned long delta; |
6424fb38 | 230 | int width = sizeof(unsigned long) * 2; |
e1a58320 SS |
231 | pgprotval_t pr = pgprot_val(st->current_prot); |
232 | ||
233 | if (st->check_wx && (pr & _PAGE_RW) && !(pr & _PAGE_NX)) { | |
234 | WARN_ONCE(1, | |
235 | "x86/mm: Found insecure W+X mapping at address %p/%pS\n", | |
236 | (void *)st->start_address, | |
237 | (void *)st->start_address); | |
238 | st->wx_pages += (st->current_address - | |
239 | st->start_address) / PAGE_SIZE; | |
240 | } | |
926e5392 | 241 | |
926e5392 AV |
242 | /* |
243 | * Now print the actual finished series | |
244 | */ | |
3891a04a PA |
245 | if (!st->marker->max_lines || |
246 | st->lines < st->marker->max_lines) { | |
247 | pt_dump_seq_printf(m, st->to_dmesg, | |
248 | "0x%0*lx-0x%0*lx ", | |
249 | width, st->start_address, | |
250 | width, st->current_address); | |
926e5392 | 251 | |
3891a04a PA |
252 | delta = st->current_address - st->start_address; |
253 | while (!(delta & 1023) && unit[1]) { | |
254 | delta >>= 10; | |
255 | unit++; | |
256 | } | |
257 | pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ", | |
258 | delta, *unit); | |
259 | printk_prot(m, st->current_prot, st->level, | |
260 | st->to_dmesg); | |
926e5392 | 261 | } |
3891a04a | 262 | st->lines++; |
fe770bf0 PA |
263 | |
264 | /* | |
265 | * We print markers for special areas of address space, | |
266 | * such as the start of vmalloc space etc. | |
267 | * This helps in the interpretation. | |
268 | */ | |
269 | if (st->current_address >= st->marker[1].start_address) { | |
3891a04a PA |
270 | if (st->marker->max_lines && |
271 | st->lines > st->marker->max_lines) { | |
272 | unsigned long nskip = | |
273 | st->lines - st->marker->max_lines; | |
274 | pt_dump_seq_printf(m, st->to_dmesg, | |
275 | "... %lu entr%s skipped ... \n", | |
276 | nskip, | |
277 | nskip == 1 ? "y" : "ies"); | |
278 | } | |
fe770bf0 | 279 | st->marker++; |
3891a04a | 280 | st->lines = 0; |
ef6bea6d BP |
281 | pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n", |
282 | st->marker->name); | |
926e5392 | 283 | } |
fe770bf0 | 284 | |
926e5392 AV |
285 | st->start_address = st->current_address; |
286 | st->current_prot = new_prot; | |
287 | st->level = level; | |
fe770bf0 | 288 | } |
926e5392 AV |
289 | } |
290 | ||
fdd3d8ce | 291 | static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr, unsigned long P) |
926e5392 AV |
292 | { |
293 | int i; | |
294 | pte_t *start; | |
da25e628 | 295 | pgprotval_t prot; |
926e5392 | 296 | |
fdd3d8ce | 297 | start = (pte_t *)pmd_page_vaddr(addr); |
926e5392 | 298 | for (i = 0; i < PTRS_PER_PTE; i++) { |
da25e628 | 299 | prot = pte_flags(*start); |
fe770bf0 | 300 | st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT); |
45dcd209 | 301 | note_page(m, st, __pgprot(prot), 5); |
926e5392 AV |
302 | start++; |
303 | } | |
304 | } | |
305 | ||
fe770bf0 | 306 | #if PTRS_PER_PMD > 1 |
926e5392 | 307 | |
fdd3d8ce | 308 | static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr, unsigned long P) |
926e5392 AV |
309 | { |
310 | int i; | |
311 | pmd_t *start; | |
da25e628 | 312 | pgprotval_t prot; |
926e5392 | 313 | |
fdd3d8ce | 314 | start = (pmd_t *)pud_page_vaddr(addr); |
926e5392 | 315 | for (i = 0; i < PTRS_PER_PMD; i++) { |
fe770bf0 | 316 | st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT); |
926e5392 | 317 | if (!pmd_none(*start)) { |
da25e628 TK |
318 | if (pmd_large(*start) || !pmd_present(*start)) { |
319 | prot = pmd_flags(*start); | |
45dcd209 | 320 | note_page(m, st, __pgprot(prot), 4); |
da25e628 | 321 | } else { |
fe770bf0 PA |
322 | walk_pte_level(m, st, *start, |
323 | P + i * PMD_LEVEL_MULT); | |
da25e628 | 324 | } |
926e5392 | 325 | } else |
45dcd209 | 326 | note_page(m, st, __pgprot(0), 4); |
926e5392 AV |
327 | start++; |
328 | } | |
329 | } | |
330 | ||
fe770bf0 PA |
331 | #else |
332 | #define walk_pmd_level(m,s,a,p) walk_pte_level(m,s,__pmd(pud_val(a)),p) | |
333 | #define pud_large(a) pmd_large(__pmd(pud_val(a))) | |
334 | #define pud_none(a) pmd_none(__pmd(pud_val(a))) | |
335 | #endif | |
926e5392 | 336 | |
fe770bf0 PA |
337 | #if PTRS_PER_PUD > 1 |
338 | ||
243b72aa AR |
339 | /* |
340 | * This is an optimization for CONFIG_DEBUG_WX=y + CONFIG_KASAN=y | |
341 | * KASAN fills page tables with the same values. Since there is no | |
342 | * point in checking page table more than once we just skip repeated | |
343 | * entries. This saves us dozens of seconds during boot. | |
344 | */ | |
345 | static bool pud_already_checked(pud_t *prev_pud, pud_t *pud, bool checkwx) | |
346 | { | |
347 | return checkwx && prev_pud && (pud_val(*prev_pud) == pud_val(*pud)); | |
348 | } | |
349 | ||
fdd3d8ce | 350 | static void walk_pud_level(struct seq_file *m, struct pg_state *st, p4d_t addr, unsigned long P) |
926e5392 AV |
351 | { |
352 | int i; | |
353 | pud_t *start; | |
da25e628 | 354 | pgprotval_t prot; |
243b72aa | 355 | pud_t *prev_pud = NULL; |
926e5392 | 356 | |
fdd3d8ce | 357 | start = (pud_t *)p4d_page_vaddr(addr); |
926e5392 AV |
358 | |
359 | for (i = 0; i < PTRS_PER_PUD; i++) { | |
fe770bf0 | 360 | st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT); |
243b72aa AR |
361 | if (!pud_none(*start) && |
362 | !pud_already_checked(prev_pud, start, st->check_wx)) { | |
da25e628 TK |
363 | if (pud_large(*start) || !pud_present(*start)) { |
364 | prot = pud_flags(*start); | |
45dcd209 | 365 | note_page(m, st, __pgprot(prot), 3); |
da25e628 | 366 | } else { |
fe770bf0 PA |
367 | walk_pmd_level(m, st, *start, |
368 | P + i * PUD_LEVEL_MULT); | |
da25e628 | 369 | } |
926e5392 | 370 | } else |
45dcd209 | 371 | note_page(m, st, __pgprot(0), 3); |
926e5392 | 372 | |
243b72aa | 373 | prev_pud = start; |
926e5392 AV |
374 | start++; |
375 | } | |
376 | } | |
377 | ||
fe770bf0 | 378 | #else |
fdd3d8ce KS |
379 | #define walk_pud_level(m,s,a,p) walk_pmd_level(m,s,__pud(p4d_val(a)),p) |
380 | #define p4d_large(a) pud_large(__pud(p4d_val(a))) | |
381 | #define p4d_none(a) pud_none(__pud(p4d_val(a))) | |
382 | #endif | |
383 | ||
384 | #if PTRS_PER_P4D > 1 | |
385 | ||
386 | static void walk_p4d_level(struct seq_file *m, struct pg_state *st, pgd_t addr, unsigned long P) | |
387 | { | |
388 | int i; | |
389 | p4d_t *start; | |
390 | pgprotval_t prot; | |
391 | ||
392 | start = (p4d_t *)pgd_page_vaddr(addr); | |
393 | ||
394 | for (i = 0; i < PTRS_PER_P4D; i++) { | |
395 | st->current_address = normalize_addr(P + i * P4D_LEVEL_MULT); | |
396 | if (!p4d_none(*start)) { | |
397 | if (p4d_large(*start) || !p4d_present(*start)) { | |
398 | prot = p4d_flags(*start); | |
399 | note_page(m, st, __pgprot(prot), 2); | |
400 | } else { | |
401 | walk_pud_level(m, st, *start, | |
402 | P + i * P4D_LEVEL_MULT); | |
403 | } | |
404 | } else | |
405 | note_page(m, st, __pgprot(0), 2); | |
406 | ||
407 | start++; | |
408 | } | |
409 | } | |
410 | ||
411 | #else | |
412 | #define walk_p4d_level(m,s,a,p) walk_pud_level(m,s,__p4d(pgd_val(a)),p) | |
413 | #define pgd_large(a) p4d_large(__p4d(pgd_val(a))) | |
414 | #define pgd_none(a) p4d_none(__p4d(pgd_val(a))) | |
fe770bf0 PA |
415 | #endif |
416 | ||
f4e342c8 BO |
417 | static inline bool is_hypervisor_range(int idx) |
418 | { | |
b176862f | 419 | #ifdef CONFIG_X86_64 |
f4e342c8 BO |
420 | /* |
421 | * ffff800000000000 - ffff87ffffffffff is reserved for | |
422 | * the hypervisor. | |
423 | */ | |
b176862f BP |
424 | return (idx >= pgd_index(__PAGE_OFFSET) - 16) && |
425 | (idx < pgd_index(__PAGE_OFFSET)); | |
f4e342c8 | 426 | #else |
b176862f | 427 | return false; |
f4e342c8 | 428 | #endif |
b176862f | 429 | } |
f4e342c8 | 430 | |
e1a58320 SS |
431 | static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd, |
432 | bool checkwx) | |
926e5392 | 433 | { |
fe770bf0 | 434 | #ifdef CONFIG_X86_64 |
65ade2f8 | 435 | pgd_t *start = (pgd_t *) &init_top_pgt; |
fe770bf0 PA |
436 | #else |
437 | pgd_t *start = swapper_pg_dir; | |
438 | #endif | |
da25e628 | 439 | pgprotval_t prot; |
926e5392 | 440 | int i; |
ef6bea6d | 441 | struct pg_state st = {}; |
926e5392 | 442 | |
ef6bea6d BP |
443 | if (pgd) { |
444 | start = pgd; | |
445 | st.to_dmesg = true; | |
446 | } | |
926e5392 | 447 | |
e1a58320 SS |
448 | st.check_wx = checkwx; |
449 | if (checkwx) | |
450 | st.wx_pages = 0; | |
451 | ||
926e5392 | 452 | for (i = 0; i < PTRS_PER_PGD; i++) { |
fe770bf0 | 453 | st.current_address = normalize_addr(i * PGD_LEVEL_MULT); |
f4e342c8 | 454 | if (!pgd_none(*start) && !is_hypervisor_range(i)) { |
da25e628 TK |
455 | if (pgd_large(*start) || !pgd_present(*start)) { |
456 | prot = pgd_flags(*start); | |
fe770bf0 | 457 | note_page(m, &st, __pgprot(prot), 1); |
da25e628 | 458 | } else { |
fdd3d8ce | 459 | walk_p4d_level(m, &st, *start, |
fe770bf0 | 460 | i * PGD_LEVEL_MULT); |
da25e628 | 461 | } |
fe770bf0 | 462 | } else |
926e5392 | 463 | note_page(m, &st, __pgprot(0), 1); |
fe770bf0 | 464 | |
146fbb76 | 465 | cond_resched(); |
926e5392 AV |
466 | start++; |
467 | } | |
fe770bf0 PA |
468 | |
469 | /* Flush out the last page */ | |
470 | st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT); | |
471 | note_page(m, &st, __pgprot(0), 0); | |
e1a58320 SS |
472 | if (!checkwx) |
473 | return; | |
474 | if (st.wx_pages) | |
475 | pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n", | |
476 | st.wx_pages); | |
477 | else | |
478 | pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n"); | |
479 | } | |
480 | ||
481 | void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd) | |
482 | { | |
483 | ptdump_walk_pgd_level_core(m, pgd, false); | |
926e5392 | 484 | } |
8609d1b5 | 485 | EXPORT_SYMBOL_GPL(ptdump_walk_pgd_level); |
926e5392 | 486 | |
e1a58320 SS |
487 | void ptdump_walk_pgd_level_checkwx(void) |
488 | { | |
489 | ptdump_walk_pgd_level_core(NULL, NULL, true); | |
490 | } | |
491 | ||
8609d1b5 | 492 | static int __init pt_dump_init(void) |
926e5392 | 493 | { |
0483e1fa TG |
494 | /* |
495 | * Various markers are not compile-time constants, so assign them | |
496 | * here. | |
497 | */ | |
498 | #ifdef CONFIG_X86_64 | |
499 | address_markers[LOW_KERNEL_NR].start_address = PAGE_OFFSET; | |
500 | address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; | |
501 | address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START; | |
502 | #endif | |
fe770bf0 | 503 | #ifdef CONFIG_X86_32 |
92851e2f AS |
504 | address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; |
505 | address_markers[VMALLOC_END_NR].start_address = VMALLOC_END; | |
fe770bf0 | 506 | # ifdef CONFIG_HIGHMEM |
92851e2f | 507 | address_markers[PKMAP_BASE_NR].start_address = PKMAP_BASE; |
fe770bf0 | 508 | # endif |
92851e2f | 509 | address_markers[FIXADDR_START_NR].start_address = FIXADDR_START; |
fe770bf0 PA |
510 | #endif |
511 | ||
926e5392 AV |
512 | return 0; |
513 | } | |
926e5392 | 514 | __initcall(pt_dump_init); |