blob: 0f1c6fc3ddd88948646963772ac0fa12c492c212 [file] [log] [blame]
Arjan van de Ven926e5392008-04-17 17:40:45 +02001/*
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
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020015#include <linux/debugfs.h>
16#include <linux/mm.h>
Arjan van de Ven926e5392008-04-17 17:40:45 +020017#include <linux/module.h>
18#include <linux/seq_file.h>
Arjan van de Ven926e5392008-04-17 17:40:45 +020019
20#include <asm/pgtable.h>
21
22/*
23 * The dumper groups pagetable entries of the same type into one, and for
24 * that it needs to keep some state when walking, and flush this state
25 * when a "break" in the continuity is found.
26 */
27struct pg_state {
28 int level;
29 pgprot_t current_prot;
30 unsigned long start_address;
31 unsigned long current_address;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020032 const struct addr_marker *marker;
H. Peter Anvin3891a042014-04-29 16:46:09 -070033 unsigned long lines;
Borislav Petkovef6bea62014-01-18 12:48:14 +010034 bool to_dmesg;
Stephen Smalleye1a58322015-10-05 12:55:20 -040035 bool check_wx;
36 unsigned long wx_pages;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020037};
38
39struct addr_marker {
40 unsigned long start_address;
41 const char *name;
H. Peter Anvin3891a042014-04-29 16:46:09 -070042 unsigned long max_lines;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020043};
44
Andres Salomon92851e22010-07-20 15:19:46 -070045/* indices for address_markers; keep sync'd w/ address_markers below */
46enum address_markers_idx {
47 USER_SPACE_NR = 0,
48#ifdef CONFIG_X86_64
49 KERNEL_SPACE_NR,
50 LOW_KERNEL_NR,
51 VMALLOC_START_NR,
52 VMEMMAP_START_NR,
Mathias Krause8a5a5d12014-09-07 20:30:29 +020053# ifdef CONFIG_X86_ESPFIX64
H. Peter Anvin3891a042014-04-29 16:46:09 -070054 ESPFIX_START_NR,
Mathias Krause8a5a5d12014-09-07 20:30:29 +020055# endif
Andres Salomon92851e22010-07-20 15:19:46 -070056 HIGH_KERNEL_NR,
57 MODULES_VADDR_NR,
58 MODULES_END_NR,
59#else
60 KERNEL_SPACE_NR,
61 VMALLOC_START_NR,
62 VMALLOC_END_NR,
63# ifdef CONFIG_HIGHMEM
64 PKMAP_BASE_NR,
65# endif
66 FIXADDR_START_NR,
67#endif
68};
69
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020070/* Address space markers hints */
71static struct addr_marker address_markers[] = {
72 { 0, "User Space" },
73#ifdef CONFIG_X86_64
74 { 0x8000000000000000UL, "Kernel Space" },
Jiri Slaby684eb012008-05-12 15:43:37 +020075 { PAGE_OFFSET, "Low Kernel Mapping" },
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020076 { VMALLOC_START, "vmalloc() Area" },
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020077 { VMEMMAP_START, "Vmemmap" },
Mathias Krause8a5a5d12014-09-07 20:30:29 +020078# ifdef CONFIG_X86_ESPFIX64
H. Peter Anvin3891a042014-04-29 16:46:09 -070079 { ESPFIX_BASE_ADDR, "ESPfix Area", 16 },
Mathias Krause8a5a5d12014-09-07 20:30:29 +020080# endif
Mathias Krause8266e312014-09-21 17:26:54 +020081# ifdef CONFIG_EFI
82 { EFI_VA_END, "EFI Runtime Services" },
83# endif
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020084 { __START_KERNEL_map, "High Kernel Mapping" },
Yinghai Lu9a79cf92008-03-07 19:17:55 -080085 { MODULES_VADDR, "Modules" },
86 { MODULES_END, "End Modules" },
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020087#else
88 { PAGE_OFFSET, "Kernel Mapping" },
89 { 0/* VMALLOC_START */, "vmalloc() Area" },
90 { 0/*VMALLOC_END*/, "vmalloc() End" },
91# ifdef CONFIG_HIGHMEM
Linus Torvalds173ae9b2015-12-15 10:15:57 -080092 { 0/*PKMAP_BASE*/, "Persistent kmap() Area" },
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020093# endif
94 { 0/*FIXADDR_START*/, "Fixmap Area" },
95#endif
96 { -1, NULL } /* End of list */
Arjan van de Ven926e5392008-04-17 17:40:45 +020097};
98
99/* Multipliers for offsets within the PTEs */
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200100#define PTE_LEVEL_MULT (PAGE_SIZE)
101#define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT)
102#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT)
103#define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200104
Borislav Petkovef6bea62014-01-18 12:48:14 +0100105#define pt_dump_seq_printf(m, to_dmesg, fmt, args...) \
106({ \
107 if (to_dmesg) \
108 printk(KERN_INFO fmt, ##args); \
109 else \
110 if (m) \
111 seq_printf(m, fmt, ##args); \
112})
113
114#define pt_dump_cont_printf(m, to_dmesg, fmt, args...) \
115({ \
116 if (to_dmesg) \
117 printk(KERN_CONT fmt, ##args); \
118 else \
119 if (m) \
120 seq_printf(m, fmt, ##args); \
121})
122
Arjan van de Ven926e5392008-04-17 17:40:45 +0200123/*
124 * Print a readable form of a pgprot_t to the seq_file
125 */
Borislav Petkovef6bea62014-01-18 12:48:14 +0100126static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200127{
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200128 pgprotval_t pr = pgprot_val(prot);
129 static const char * const level_name[] =
130 { "cr3", "pgd", "pud", "pmd", "pte" };
Arjan van de Ven926e5392008-04-17 17:40:45 +0200131
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200132 if (!pgprot_val(prot)) {
133 /* Not present */
Juergen Grossf439c429c32014-11-03 14:02:01 +0100134 pt_dump_cont_printf(m, dmsg, " ");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200135 } else {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200136 if (pr & _PAGE_USER)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100137 pt_dump_cont_printf(m, dmsg, "USR ");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200138 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100139 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200140 if (pr & _PAGE_RW)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100141 pt_dump_cont_printf(m, dmsg, "RW ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200142 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100143 pt_dump_cont_printf(m, dmsg, "ro ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200144 if (pr & _PAGE_PWT)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100145 pt_dump_cont_printf(m, dmsg, "PWT ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200146 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100147 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200148 if (pr & _PAGE_PCD)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100149 pt_dump_cont_printf(m, dmsg, "PCD ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200150 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100151 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200152
Juergen Grossf439c429c32014-11-03 14:02:01 +0100153 /* Bit 7 has a different meaning on level 3 vs 4 */
154 if (level <= 3 && pr & _PAGE_PSE)
155 pt_dump_cont_printf(m, dmsg, "PSE ");
156 else
157 pt_dump_cont_printf(m, dmsg, " ");
158 if ((level == 4 && pr & _PAGE_PAT) ||
159 ((level == 3 || level == 2) && pr & _PAGE_PAT_LARGE))
Toshi Kanida25e622015-09-17 12:24:19 -0600160 pt_dump_cont_printf(m, dmsg, "PAT ");
Juergen Grossf439c429c32014-11-03 14:02:01 +0100161 else
162 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200163 if (pr & _PAGE_GLOBAL)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100164 pt_dump_cont_printf(m, dmsg, "GLB ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200165 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100166 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200167 if (pr & _PAGE_NX)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100168 pt_dump_cont_printf(m, dmsg, "NX ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200169 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100170 pt_dump_cont_printf(m, dmsg, "x ");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200171 }
Borislav Petkovef6bea62014-01-18 12:48:14 +0100172 pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200173}
174
175/*
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200176 * On 64 bits, sign-extend the 48 bit address to 64 bit
Arjan van de Ven926e5392008-04-17 17:40:45 +0200177 */
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200178static unsigned long normalize_addr(unsigned long u)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200179{
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200180#ifdef CONFIG_X86_64
181 return (signed long)(u << 16) >> 16;
182#else
Arjan van de Ven926e5392008-04-17 17:40:45 +0200183 return u;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200184#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200185}
186
187/*
188 * This function gets called on a break in a continuous series
189 * of PTE entries; the next one is different so we need to
190 * print what we collected so far.
191 */
192static void note_page(struct seq_file *m, struct pg_state *st,
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200193 pgprot_t new_prot, int level)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200194{
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200195 pgprotval_t prot, cur;
H. Peter Anvin3891a042014-04-29 16:46:09 -0700196 static const char units[] = "BKMGTPE";
Arjan van de Ven926e5392008-04-17 17:40:45 +0200197
198 /*
199 * If we have a "break" in the series, we need to flush the state that
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200200 * we have now. "break" is either changing perms, levels or
201 * address space marker.
Arjan van de Ven926e5392008-04-17 17:40:45 +0200202 */
Toshi Kanida25e622015-09-17 12:24:19 -0600203 prot = pgprot_val(new_prot);
204 cur = pgprot_val(st->current_prot);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200205
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200206 if (!st->level) {
207 /* First entry */
208 st->current_prot = new_prot;
209 st->level = level;
210 st->marker = address_markers;
H. Peter Anvin3891a042014-04-29 16:46:09 -0700211 st->lines = 0;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100212 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
213 st->marker->name);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200214 } else if (prot != cur || level != st->level ||
215 st->current_address >= st->marker[1].start_address) {
216 const char *unit = units;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200217 unsigned long delta;
Yinghai Lu6424fb32009-04-13 23:51:46 -0700218 int width = sizeof(unsigned long) * 2;
Stephen Smalleye1a58322015-10-05 12:55:20 -0400219 pgprotval_t pr = pgprot_val(st->current_prot);
220
221 if (st->check_wx && (pr & _PAGE_RW) && !(pr & _PAGE_NX)) {
222 WARN_ONCE(1,
223 "x86/mm: Found insecure W+X mapping at address %p/%pS\n",
224 (void *)st->start_address,
225 (void *)st->start_address);
226 st->wx_pages += (st->current_address -
227 st->start_address) / PAGE_SIZE;
228 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200229
230 /*
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200231 * Now print the actual finished series
232 */
H. Peter Anvin3891a042014-04-29 16:46:09 -0700233 if (!st->marker->max_lines ||
234 st->lines < st->marker->max_lines) {
235 pt_dump_seq_printf(m, st->to_dmesg,
236 "0x%0*lx-0x%0*lx ",
237 width, st->start_address,
238 width, st->current_address);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200239
H. Peter Anvin3891a042014-04-29 16:46:09 -0700240 delta = st->current_address - st->start_address;
241 while (!(delta & 1023) && unit[1]) {
242 delta >>= 10;
243 unit++;
244 }
245 pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ",
246 delta, *unit);
247 printk_prot(m, st->current_prot, st->level,
248 st->to_dmesg);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200249 }
H. Peter Anvin3891a042014-04-29 16:46:09 -0700250 st->lines++;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200251
252 /*
Arjan van de Ven926e5392008-04-17 17:40:45 +0200253 * We print markers for special areas of address space,
254 * such as the start of vmalloc space etc.
255 * This helps in the interpretation.
256 */
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200257 if (st->current_address >= st->marker[1].start_address) {
H. Peter Anvin3891a042014-04-29 16:46:09 -0700258 if (st->marker->max_lines &&
259 st->lines > st->marker->max_lines) {
260 unsigned long nskip =
261 st->lines - st->marker->max_lines;
262 pt_dump_seq_printf(m, st->to_dmesg,
263 "... %lu entr%s skipped ... \n",
264 nskip,
265 nskip == 1 ? "y" : "ies");
266 }
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200267 st->marker++;
H. Peter Anvin3891a042014-04-29 16:46:09 -0700268 st->lines = 0;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100269 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
270 st->marker->name);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200271 }
272
Arjan van de Ven926e5392008-04-17 17:40:45 +0200273 st->start_address = st->current_address;
274 st->current_prot = new_prot;
275 st->level = level;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200276 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200277}
278
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200279static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
Arjan van de Ven926e5392008-04-17 17:40:45 +0200280 unsigned long P)
281{
282 int i;
283 pte_t *start;
Toshi Kanida25e622015-09-17 12:24:19 -0600284 pgprotval_t prot;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200285
286 start = (pte_t *) pmd_page_vaddr(addr);
287 for (i = 0; i < PTRS_PER_PTE; i++) {
Toshi Kanida25e622015-09-17 12:24:19 -0600288 prot = pte_flags(*start);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200289 st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600290 note_page(m, st, __pgprot(prot), 4);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200291 start++;
292 }
293}
294
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200295#if PTRS_PER_PMD > 1
Arjan van de Ven926e5392008-04-17 17:40:45 +0200296
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200297static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
Arjan van de Ven926e5392008-04-17 17:40:45 +0200298 unsigned long P)
299{
300 int i;
301 pmd_t *start;
Toshi Kanida25e622015-09-17 12:24:19 -0600302 pgprotval_t prot;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200303
304 start = (pmd_t *) pud_page_vaddr(addr);
305 for (i = 0; i < PTRS_PER_PMD; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200306 st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200307 if (!pmd_none(*start)) {
Toshi Kanida25e622015-09-17 12:24:19 -0600308 if (pmd_large(*start) || !pmd_present(*start)) {
309 prot = pmd_flags(*start);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200310 note_page(m, st, __pgprot(prot), 3);
Toshi Kanida25e622015-09-17 12:24:19 -0600311 } else {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200312 walk_pte_level(m, st, *start,
313 P + i * PMD_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600314 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200315 } else
316 note_page(m, st, __pgprot(0), 3);
317 start++;
318 }
319}
320
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200321#else
322#define walk_pmd_level(m,s,a,p) walk_pte_level(m,s,__pmd(pud_val(a)),p)
323#define pud_large(a) pmd_large(__pmd(pud_val(a)))
324#define pud_none(a) pmd_none(__pmd(pud_val(a)))
325#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200326
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200327#if PTRS_PER_PUD > 1
328
329static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
Arjan van de Ven926e5392008-04-17 17:40:45 +0200330 unsigned long P)
331{
332 int i;
333 pud_t *start;
Toshi Kanida25e622015-09-17 12:24:19 -0600334 pgprotval_t prot;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200335
336 start = (pud_t *) pgd_page_vaddr(addr);
337
338 for (i = 0; i < PTRS_PER_PUD; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200339 st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200340 if (!pud_none(*start)) {
Toshi Kanida25e622015-09-17 12:24:19 -0600341 if (pud_large(*start) || !pud_present(*start)) {
342 prot = pud_flags(*start);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200343 note_page(m, st, __pgprot(prot), 2);
Toshi Kanida25e622015-09-17 12:24:19 -0600344 } else {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200345 walk_pmd_level(m, st, *start,
346 P + i * PUD_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600347 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200348 } else
349 note_page(m, st, __pgprot(0), 2);
350
351 start++;
352 }
353}
354
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200355#else
356#define walk_pud_level(m,s,a,p) walk_pmd_level(m,s,__pud(pgd_val(a)),p)
357#define pgd_large(a) pud_large(__pud(pgd_val(a)))
358#define pgd_none(a) pud_none(__pud(pgd_val(a)))
359#endif
360
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500361#ifdef CONFIG_X86_64
362static inline bool is_hypervisor_range(int idx)
363{
364 /*
365 * ffff800000000000 - ffff87ffffffffff is reserved for
366 * the hypervisor.
367 */
368 return paravirt_enabled() &&
369 (idx >= pgd_index(__PAGE_OFFSET) - 16) &&
370 (idx < pgd_index(__PAGE_OFFSET));
371}
372#else
373static inline bool is_hypervisor_range(int idx) { return false; }
374#endif
375
Stephen Smalleye1a58322015-10-05 12:55:20 -0400376static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
377 bool checkwx)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200378{
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200379#ifdef CONFIG_X86_64
Arjan van de Ven926e5392008-04-17 17:40:45 +0200380 pgd_t *start = (pgd_t *) &init_level4_pgt;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200381#else
382 pgd_t *start = swapper_pg_dir;
383#endif
Toshi Kanida25e622015-09-17 12:24:19 -0600384 pgprotval_t prot;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200385 int i;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100386 struct pg_state st = {};
Arjan van de Ven926e5392008-04-17 17:40:45 +0200387
Borislav Petkovef6bea62014-01-18 12:48:14 +0100388 if (pgd) {
389 start = pgd;
390 st.to_dmesg = true;
391 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200392
Stephen Smalleye1a58322015-10-05 12:55:20 -0400393 st.check_wx = checkwx;
394 if (checkwx)
395 st.wx_pages = 0;
396
Arjan van de Ven926e5392008-04-17 17:40:45 +0200397 for (i = 0; i < PTRS_PER_PGD; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200398 st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500399 if (!pgd_none(*start) && !is_hypervisor_range(i)) {
Toshi Kanida25e622015-09-17 12:24:19 -0600400 if (pgd_large(*start) || !pgd_present(*start)) {
401 prot = pgd_flags(*start);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200402 note_page(m, &st, __pgprot(prot), 1);
Toshi Kanida25e622015-09-17 12:24:19 -0600403 } else {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200404 walk_pud_level(m, &st, *start,
405 i * PGD_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600406 }
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200407 } else
Arjan van de Ven926e5392008-04-17 17:40:45 +0200408 note_page(m, &st, __pgprot(0), 1);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200409
Arjan van de Ven926e5392008-04-17 17:40:45 +0200410 start++;
411 }
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200412
413 /* Flush out the last page */
414 st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT);
415 note_page(m, &st, __pgprot(0), 0);
Stephen Smalleye1a58322015-10-05 12:55:20 -0400416 if (!checkwx)
417 return;
418 if (st.wx_pages)
419 pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n",
420 st.wx_pages);
421 else
422 pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200423}
424
Stephen Smalleye1a58322015-10-05 12:55:20 -0400425void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
426{
427 ptdump_walk_pgd_level_core(m, pgd, false);
428}
429
430void ptdump_walk_pgd_level_checkwx(void)
431{
432 ptdump_walk_pgd_level_core(NULL, NULL, true);
433}
434
435#ifdef CONFIG_X86_PTDUMP
Arjan van de Ven926e5392008-04-17 17:40:45 +0200436static int ptdump_show(struct seq_file *m, void *v)
437{
Borislav Petkovef6bea62014-01-18 12:48:14 +0100438 ptdump_walk_pgd_level(m, NULL);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200439 return 0;
440}
441
442static int ptdump_open(struct inode *inode, struct file *filp)
443{
444 return single_open(filp, ptdump_show, NULL);
445}
446
447static const struct file_operations ptdump_fops = {
448 .open = ptdump_open,
449 .read = seq_read,
450 .llseek = seq_lseek,
451 .release = single_release,
452};
Stephen Smalleye1a58322015-10-05 12:55:20 -0400453#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200454
Ingo Molnara4928cf2008-04-23 13:20:56 +0200455static int pt_dump_init(void)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200456{
Stephen Smalleye1a58322015-10-05 12:55:20 -0400457#ifdef CONFIG_X86_PTDUMP
Arjan van de Ven926e5392008-04-17 17:40:45 +0200458 struct dentry *pe;
Stephen Smalleye1a58322015-10-05 12:55:20 -0400459#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200460
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200461#ifdef CONFIG_X86_32
462 /* Not a compile-time constant on x86-32 */
Andres Salomon92851e22010-07-20 15:19:46 -0700463 address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
464 address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200465# ifdef CONFIG_HIGHMEM
Andres Salomon92851e22010-07-20 15:19:46 -0700466 address_markers[PKMAP_BASE_NR].start_address = PKMAP_BASE;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200467# endif
Andres Salomon92851e22010-07-20 15:19:46 -0700468 address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200469#endif
470
Stephen Smalleye1a58322015-10-05 12:55:20 -0400471#ifdef CONFIG_X86_PTDUMP
Arjan van de Ven926e5392008-04-17 17:40:45 +0200472 pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
473 &ptdump_fops);
474 if (!pe)
475 return -ENOMEM;
Stephen Smalleye1a58322015-10-05 12:55:20 -0400476#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200477
478 return 0;
479}
480
481__initcall(pt_dump_init);
482MODULE_LICENSE("GPL");
483MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
484MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables");