blob: 7b71ac15b23593ed8f3fbddc8301b00a83744f2e [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>
Andrey Ryabinin04b67022017-07-24 18:25:58 +030016#include <linux/kasan.h>
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020017#include <linux/mm.h>
Paul Gortmaker84e629b2016-07-13 20:18:54 -040018#include <linux/init.h>
Andrey Ryabinin146fbb762017-02-10 12:54:05 +030019#include <linux/sched.h>
Arjan van de Ven926e5392008-04-17 17:40:45 +020020#include <linux/seq_file.h>
Joerg Roedeld6ef1f12018-04-17 15:27:16 +020021#include <linux/highmem.h>
Thomas Gleixnerc200dac2018-10-08 21:53:48 +020022#include <linux/pci.h>
Arjan van de Ven926e5392008-04-17 17:40:45 +020023
Thomas Gleixnerc200dac2018-10-08 21:53:48 +020024#include <asm/e820/types.h>
Arjan van de Ven926e5392008-04-17 17:40:45 +020025#include <asm/pgtable.h>
26
27/*
28 * The dumper groups pagetable entries of the same type into one, and for
29 * that it needs to keep some state when walking, and flush this state
30 * when a "break" in the continuity is found.
31 */
32struct pg_state {
33 int level;
34 pgprot_t current_prot;
Jan Beulich672c0ae2018-02-23 01:27:37 -070035 pgprotval_t effective_prot;
Arjan van de Ven926e5392008-04-17 17:40:45 +020036 unsigned long start_address;
37 unsigned long current_address;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020038 const struct addr_marker *marker;
H. Peter Anvin3891a042014-04-29 16:46:09 -070039 unsigned long lines;
Borislav Petkovef6bea62014-01-18 12:48:14 +010040 bool to_dmesg;
Stephen Smalleye1a58322015-10-05 12:55:20 -040041 bool check_wx;
42 unsigned long wx_pages;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020043};
44
45struct addr_marker {
46 unsigned long start_address;
47 const char *name;
H. Peter Anvin3891a042014-04-29 16:46:09 -070048 unsigned long max_lines;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +020049};
50
Thomas Gleixner146122e2017-12-20 18:07:42 +010051/* Address space markers hints */
52
53#ifdef CONFIG_X86_64
54
Andres Salomon92851e22010-07-20 15:19:46 -070055enum address_markers_idx {
56 USER_SPACE_NR = 0,
Andres Salomon92851e22010-07-20 15:19:46 -070057 KERNEL_SPACE_NR,
Kirill A. Shutemov254eb552018-11-30 23:23:28 +030058#ifdef CONFIG_MODIFY_LDT_SYSCALL
Andy Lutomirskif55f0502017-12-12 07:56:45 -080059 LDT_NR,
60#endif
Kirill A. Shutemov254eb552018-11-30 23:23:28 +030061 LOW_KERNEL_NR,
Andres Salomon92851e22010-07-20 15:19:46 -070062 VMALLOC_START_NR,
63 VMEMMAP_START_NR,
Andrey Ryabinin025205f2017-02-14 13:08:39 +030064#ifdef CONFIG_KASAN
65 KASAN_SHADOW_START_NR,
66 KASAN_SHADOW_END_NR,
67#endif
Thomas Gleixnerf2078902018-01-04 13:01:40 +010068 CPU_ENTRY_AREA_NR,
Thomas Gleixner146122e2017-12-20 18:07:42 +010069#ifdef CONFIG_X86_ESPFIX64
H. Peter Anvin3891a042014-04-29 16:46:09 -070070 ESPFIX_START_NR,
Thomas Gleixner146122e2017-12-20 18:07:42 +010071#endif
72#ifdef CONFIG_EFI
73 EFI_END_NR,
74#endif
Andres Salomon92851e22010-07-20 15:19:46 -070075 HIGH_KERNEL_NR,
76 MODULES_VADDR_NR,
77 MODULES_END_NR,
Thomas Gleixner146122e2017-12-20 18:07:42 +010078 FIXADDR_START_NR,
79 END_OF_SPACE_NR,
80};
81
82static struct addr_marker address_markers[] = {
83 [USER_SPACE_NR] = { 0, "User Space" },
84 [KERNEL_SPACE_NR] = { (1UL << 63), "Kernel Space" },
85 [LOW_KERNEL_NR] = { 0UL, "Low Kernel Mapping" },
86 [VMALLOC_START_NR] = { 0UL, "vmalloc() Area" },
87 [VMEMMAP_START_NR] = { 0UL, "Vmemmap" },
88#ifdef CONFIG_KASAN
Kirill A. Shutemov09e61a72018-02-14 14:16:55 +030089 /*
90 * These fields get initialized with the (dynamic)
91 * KASAN_SHADOW_{START,END} values in pt_dump_init().
92 */
93 [KASAN_SHADOW_START_NR] = { 0UL, "KASAN shadow" },
94 [KASAN_SHADOW_END_NR] = { 0UL, "KASAN shadow end" },
Thomas Gleixner146122e2017-12-20 18:07:42 +010095#endif
Andy Lutomirskif55f0502017-12-12 07:56:45 -080096#ifdef CONFIG_MODIFY_LDT_SYSCALL
Kirill A. Shutemov5c7919b2018-02-14 14:16:52 +030097 [LDT_NR] = { 0UL, "LDT remap" },
Andy Lutomirskif55f0502017-12-12 07:56:45 -080098#endif
Thomas Gleixner92a0f812017-12-20 18:51:31 +010099 [CPU_ENTRY_AREA_NR] = { CPU_ENTRY_AREA_BASE,"CPU entry Area" },
Thomas Gleixner146122e2017-12-20 18:07:42 +0100100#ifdef CONFIG_X86_ESPFIX64
101 [ESPFIX_START_NR] = { ESPFIX_BASE_ADDR, "ESPfix Area", 16 },
102#endif
103#ifdef CONFIG_EFI
104 [EFI_END_NR] = { EFI_VA_END, "EFI Runtime Services" },
105#endif
106 [HIGH_KERNEL_NR] = { __START_KERNEL_map, "High Kernel Mapping" },
107 [MODULES_VADDR_NR] = { MODULES_VADDR, "Modules" },
108 [MODULES_END_NR] = { MODULES_END, "End Modules" },
109 [FIXADDR_START_NR] = { FIXADDR_START, "Fixmap Area" },
110 [END_OF_SPACE_NR] = { -1, NULL }
111};
112
Joerg Roedel4e8537e2018-07-18 11:41:08 +0200113#define INIT_PGD ((pgd_t *) &init_top_pgt)
114
Thomas Gleixner146122e2017-12-20 18:07:42 +0100115#else /* CONFIG_X86_64 */
116
117enum address_markers_idx {
118 USER_SPACE_NR = 0,
Andres Salomon92851e22010-07-20 15:19:46 -0700119 KERNEL_SPACE_NR,
120 VMALLOC_START_NR,
121 VMALLOC_END_NR,
Thomas Gleixner146122e2017-12-20 18:07:42 +0100122#ifdef CONFIG_HIGHMEM
Andres Salomon92851e22010-07-20 15:19:46 -0700123 PKMAP_BASE_NR,
Andres Salomon92851e22010-07-20 15:19:46 -0700124#endif
Joerg Roedelf3e48e52018-07-18 11:41:10 +0200125#ifdef CONFIG_MODIFY_LDT_SYSCALL
126 LDT_NR,
127#endif
Thomas Gleixner92a0f812017-12-20 18:51:31 +0100128 CPU_ENTRY_AREA_NR,
Thomas Gleixner146122e2017-12-20 18:07:42 +0100129 FIXADDR_START_NR,
130 END_OF_SPACE_NR,
Andres Salomon92851e22010-07-20 15:19:46 -0700131};
132
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200133static struct addr_marker address_markers[] = {
Thomas Gleixner146122e2017-12-20 18:07:42 +0100134 [USER_SPACE_NR] = { 0, "User Space" },
135 [KERNEL_SPACE_NR] = { PAGE_OFFSET, "Kernel Mapping" },
136 [VMALLOC_START_NR] = { 0UL, "vmalloc() Area" },
137 [VMALLOC_END_NR] = { 0UL, "vmalloc() End" },
138#ifdef CONFIG_HIGHMEM
139 [PKMAP_BASE_NR] = { 0UL, "Persistent kmap() Area" },
Andrey Ryabinin025205f2017-02-14 13:08:39 +0300140#endif
Joerg Roedelf3e48e52018-07-18 11:41:10 +0200141#ifdef CONFIG_MODIFY_LDT_SYSCALL
142 [LDT_NR] = { 0UL, "LDT remap" },
143#endif
Thomas Gleixner92a0f812017-12-20 18:51:31 +0100144 [CPU_ENTRY_AREA_NR] = { 0UL, "CPU entry area" },
Thomas Gleixner146122e2017-12-20 18:07:42 +0100145 [FIXADDR_START_NR] = { 0UL, "Fixmap area" },
146 [END_OF_SPACE_NR] = { -1, NULL }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200147};
148
Joerg Roedel4e8537e2018-07-18 11:41:08 +0200149#define INIT_PGD (swapper_pg_dir)
150
Thomas Gleixner146122e2017-12-20 18:07:42 +0100151#endif /* !CONFIG_X86_64 */
152
Arjan van de Ven926e5392008-04-17 17:40:45 +0200153/* Multipliers for offsets within the PTEs */
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200154#define PTE_LEVEL_MULT (PAGE_SIZE)
155#define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT)
156#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT)
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300157#define P4D_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT)
Juergen Gross84bbabc2017-04-12 16:36:34 +0200158#define PGD_LEVEL_MULT (PTRS_PER_P4D * P4D_LEVEL_MULT)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200159
Borislav Petkovef6bea62014-01-18 12:48:14 +0100160#define pt_dump_seq_printf(m, to_dmesg, fmt, args...) \
161({ \
162 if (to_dmesg) \
163 printk(KERN_INFO fmt, ##args); \
164 else \
165 if (m) \
166 seq_printf(m, fmt, ##args); \
167})
168
169#define pt_dump_cont_printf(m, to_dmesg, fmt, args...) \
170({ \
171 if (to_dmesg) \
172 printk(KERN_CONT fmt, ##args); \
173 else \
174 if (m) \
175 seq_printf(m, fmt, ##args); \
176})
177
Arjan van de Ven926e5392008-04-17 17:40:45 +0200178/*
179 * Print a readable form of a pgprot_t to the seq_file
180 */
Borislav Petkovef6bea62014-01-18 12:48:14 +0100181static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200182{
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200183 pgprotval_t pr = pgprot_val(prot);
184 static const char * const level_name[] =
Kirill A. Shutemov45dcd202017-07-17 01:59:48 +0300185 { "cr3", "pgd", "p4d", "pud", "pmd", "pte" };
Arjan van de Ven926e5392008-04-17 17:40:45 +0200186
Thomas Gleixnerc0534492017-12-16 01:14:39 +0100187 if (!(pr & _PAGE_PRESENT)) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200188 /* Not present */
Juergen Grossf439c429c32014-11-03 14:02:01 +0100189 pt_dump_cont_printf(m, dmsg, " ");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200190 } else {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200191 if (pr & _PAGE_USER)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100192 pt_dump_cont_printf(m, dmsg, "USR ");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200193 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100194 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200195 if (pr & _PAGE_RW)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100196 pt_dump_cont_printf(m, dmsg, "RW ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200197 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100198 pt_dump_cont_printf(m, dmsg, "ro ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200199 if (pr & _PAGE_PWT)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100200 pt_dump_cont_printf(m, dmsg, "PWT ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200201 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100202 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200203 if (pr & _PAGE_PCD)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100204 pt_dump_cont_printf(m, dmsg, "PCD ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200205 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100206 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200207
Juergen Grossf439c429c32014-11-03 14:02:01 +0100208 /* Bit 7 has a different meaning on level 3 vs 4 */
Kirill A. Shutemov45dcd202017-07-17 01:59:48 +0300209 if (level <= 4 && pr & _PAGE_PSE)
Juergen Grossf439c429c32014-11-03 14:02:01 +0100210 pt_dump_cont_printf(m, dmsg, "PSE ");
211 else
212 pt_dump_cont_printf(m, dmsg, " ");
Kirill A. Shutemov45dcd202017-07-17 01:59:48 +0300213 if ((level == 5 && pr & _PAGE_PAT) ||
214 ((level == 4 || level == 3) && pr & _PAGE_PAT_LARGE))
Toshi Kanida25e622015-09-17 12:24:19 -0600215 pt_dump_cont_printf(m, dmsg, "PAT ");
Juergen Grossf439c429c32014-11-03 14:02:01 +0100216 else
217 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200218 if (pr & _PAGE_GLOBAL)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100219 pt_dump_cont_printf(m, dmsg, "GLB ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200220 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100221 pt_dump_cont_printf(m, dmsg, " ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200222 if (pr & _PAGE_NX)
Borislav Petkovef6bea62014-01-18 12:48:14 +0100223 pt_dump_cont_printf(m, dmsg, "NX ");
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200224 else
Borislav Petkovef6bea62014-01-18 12:48:14 +0100225 pt_dump_cont_printf(m, dmsg, "x ");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200226 }
Borislav Petkovef6bea62014-01-18 12:48:14 +0100227 pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200228}
229
230/*
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200231 * On 64 bits, sign-extend the 48 bit address to 64 bit
Arjan van de Ven926e5392008-04-17 17:40:45 +0200232 */
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200233static unsigned long normalize_addr(unsigned long u)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200234{
Kirill A. Shutemov3a366f72017-07-17 01:59:47 +0300235 int shift;
236 if (!IS_ENABLED(CONFIG_X86_64))
237 return u;
238
239 shift = 64 - (__VIRTUAL_MASK_SHIFT + 1);
240 return (signed long)(u << shift) >> shift;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200241}
242
Thomas Gleixnerc200dac2018-10-08 21:53:48 +0200243static void note_wx(struct pg_state *st)
244{
245 unsigned long npages;
246
247 npages = (st->current_address - st->start_address) / PAGE_SIZE;
248
249#ifdef CONFIG_PCI_BIOS
250 /*
251 * If PCI BIOS is enabled, the PCI BIOS area is forced to WX.
252 * Inform about it, but avoid the warning.
253 */
254 if (pcibios_enabled && st->start_address >= PAGE_OFFSET + BIOS_BEGIN &&
255 st->current_address <= PAGE_OFFSET + BIOS_END) {
256 pr_warn_once("x86/mm: PCI BIOS W+X mapping %lu pages\n", npages);
257 return;
258 }
259#endif
260 /* Account the WX pages */
261 st->wx_pages += npages;
262 WARN_ONCE(1, "x86/mm: Found insecure W+X mapping at address %pS\n",
263 (void *)st->start_address);
264}
265
Arjan van de Ven926e5392008-04-17 17:40:45 +0200266/*
267 * This function gets called on a break in a continuous series
268 * of PTE entries; the next one is different so we need to
269 * print what we collected so far.
270 */
271static void note_page(struct seq_file *m, struct pg_state *st,
Jan Beulich672c0ae2018-02-23 01:27:37 -0700272 pgprot_t new_prot, pgprotval_t new_eff, int level)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200273{
Jan Beulich672c0ae2018-02-23 01:27:37 -0700274 pgprotval_t prot, cur, eff;
H. Peter Anvin3891a042014-04-29 16:46:09 -0700275 static const char units[] = "BKMGTPE";
Arjan van de Ven926e5392008-04-17 17:40:45 +0200276
277 /*
278 * If we have a "break" in the series, we need to flush the state that
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200279 * we have now. "break" is either changing perms, levels or
280 * address space marker.
Arjan van de Ven926e5392008-04-17 17:40:45 +0200281 */
Toshi Kanida25e622015-09-17 12:24:19 -0600282 prot = pgprot_val(new_prot);
283 cur = pgprot_val(st->current_prot);
Jan Beulich672c0ae2018-02-23 01:27:37 -0700284 eff = st->effective_prot;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200285
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200286 if (!st->level) {
287 /* First entry */
288 st->current_prot = new_prot;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700289 st->effective_prot = new_eff;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200290 st->level = level;
291 st->marker = address_markers;
H. Peter Anvin3891a042014-04-29 16:46:09 -0700292 st->lines = 0;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100293 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
294 st->marker->name);
Jan Beulich672c0ae2018-02-23 01:27:37 -0700295 } else if (prot != cur || new_eff != eff || level != st->level ||
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200296 st->current_address >= st->marker[1].start_address) {
297 const char *unit = units;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200298 unsigned long delta;
Yinghai Lu6424fb32009-04-13 23:51:46 -0700299 int width = sizeof(unsigned long) * 2;
Stephen Smalleye1a58322015-10-05 12:55:20 -0400300
Thomas Gleixnerc200dac2018-10-08 21:53:48 +0200301 if (st->check_wx && (eff & _PAGE_RW) && !(eff & _PAGE_NX))
302 note_wx(st);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200303
304 /*
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200305 * Now print the actual finished series
306 */
H. Peter Anvin3891a042014-04-29 16:46:09 -0700307 if (!st->marker->max_lines ||
308 st->lines < st->marker->max_lines) {
309 pt_dump_seq_printf(m, st->to_dmesg,
310 "0x%0*lx-0x%0*lx ",
311 width, st->start_address,
312 width, st->current_address);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200313
H. Peter Anvin3891a042014-04-29 16:46:09 -0700314 delta = st->current_address - st->start_address;
315 while (!(delta & 1023) && unit[1]) {
316 delta >>= 10;
317 unit++;
318 }
319 pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ",
320 delta, *unit);
321 printk_prot(m, st->current_prot, st->level,
322 st->to_dmesg);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200323 }
H. Peter Anvin3891a042014-04-29 16:46:09 -0700324 st->lines++;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200325
326 /*
Arjan van de Ven926e5392008-04-17 17:40:45 +0200327 * We print markers for special areas of address space,
328 * such as the start of vmalloc space etc.
329 * This helps in the interpretation.
330 */
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200331 if (st->current_address >= st->marker[1].start_address) {
H. Peter Anvin3891a042014-04-29 16:46:09 -0700332 if (st->marker->max_lines &&
333 st->lines > st->marker->max_lines) {
334 unsigned long nskip =
335 st->lines - st->marker->max_lines;
336 pt_dump_seq_printf(m, st->to_dmesg,
337 "... %lu entr%s skipped ... \n",
338 nskip,
339 nskip == 1 ? "y" : "ies");
340 }
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200341 st->marker++;
H. Peter Anvin3891a042014-04-29 16:46:09 -0700342 st->lines = 0;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100343 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
344 st->marker->name);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200345 }
346
Arjan van de Ven926e5392008-04-17 17:40:45 +0200347 st->start_address = st->current_address;
348 st->current_prot = new_prot;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700349 st->effective_prot = new_eff;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200350 st->level = level;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200351 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200352}
353
Jan Beulich672c0ae2018-02-23 01:27:37 -0700354static inline pgprotval_t effective_prot(pgprotval_t prot1, pgprotval_t prot2)
355{
356 return (prot1 & prot2 & (_PAGE_USER | _PAGE_RW)) |
357 ((prot1 | prot2) & _PAGE_NX);
358}
359
360static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
361 pgprotval_t eff_in, unsigned long P)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200362{
363 int i;
Joerg Roedeld6ef1f12018-04-17 15:27:16 +0200364 pte_t *pte;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700365 pgprotval_t prot, eff;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200366
Arjan van de Ven926e5392008-04-17 17:40:45 +0200367 for (i = 0; i < PTRS_PER_PTE; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200368 st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT);
Joerg Roedeld6ef1f12018-04-17 15:27:16 +0200369 pte = pte_offset_map(&addr, st->current_address);
370 prot = pte_flags(*pte);
371 eff = effective_prot(eff_in, prot);
Jan Beulich672c0ae2018-02-23 01:27:37 -0700372 note_page(m, st, __pgprot(prot), eff, 5);
Joerg Roedeld6ef1f12018-04-17 15:27:16 +0200373 pte_unmap(pte);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200374 }
375}
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300376#ifdef CONFIG_KASAN
377
378/*
379 * This is an optimization for KASAN=y case. Since all kasan page tables
Andrey Konovalov9577dd72018-12-28 00:30:01 -0800380 * eventually point to the kasan_early_shadow_page we could call note_page()
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300381 * right away without walking through lower level page tables. This saves
382 * us dozens of seconds (minutes for 5-level config) while checking for
383 * W+X mapping or reading kernel_page_tables debugfs file.
384 */
385static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
386 void *pt)
387{
Andrey Konovalov9577dd72018-12-28 00:30:01 -0800388 if (__pa(pt) == __pa(kasan_early_shadow_pmd) ||
389 (pgtable_l5_enabled() &&
390 __pa(pt) == __pa(kasan_early_shadow_p4d)) ||
391 __pa(pt) == __pa(kasan_early_shadow_pud)) {
392 pgprotval_t prot = pte_flags(kasan_early_shadow_pte[0]);
Jan Beulich672c0ae2018-02-23 01:27:37 -0700393 note_page(m, st, __pgprot(prot), 0, 5);
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300394 return true;
395 }
396 return false;
397}
398#else
399static inline bool kasan_page_table(struct seq_file *m, struct pg_state *st,
400 void *pt)
401{
402 return false;
403}
404#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200405
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200406#if PTRS_PER_PMD > 1
Arjan van de Ven926e5392008-04-17 17:40:45 +0200407
Jan Beulich672c0ae2018-02-23 01:27:37 -0700408static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
409 pgprotval_t eff_in, unsigned long P)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200410{
411 int i;
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300412 pmd_t *start, *pmd_start;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700413 pgprotval_t prot, eff;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200414
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300415 pmd_start = start = (pmd_t *)pud_page_vaddr(addr);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200416 for (i = 0; i < PTRS_PER_PMD; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200417 st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200418 if (!pmd_none(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700419 prot = pmd_flags(*start);
420 eff = effective_prot(eff_in, prot);
Toshi Kanida25e622015-09-17 12:24:19 -0600421 if (pmd_large(*start) || !pmd_present(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700422 note_page(m, st, __pgprot(prot), eff, 4);
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300423 } else if (!kasan_page_table(m, st, pmd_start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700424 walk_pte_level(m, st, *start, eff,
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200425 P + i * PMD_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600426 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200427 } else
Jan Beulich672c0ae2018-02-23 01:27:37 -0700428 note_page(m, st, __pgprot(0), 0, 4);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200429 start++;
430 }
431}
432
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200433#else
Jan Beulich672c0ae2018-02-23 01:27:37 -0700434#define walk_pmd_level(m,s,a,e,p) walk_pte_level(m,s,__pmd(pud_val(a)),e,p)
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200435#define pud_large(a) pmd_large(__pmd(pud_val(a)))
436#define pud_none(a) pmd_none(__pmd(pud_val(a)))
437#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200438
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200439#if PTRS_PER_PUD > 1
440
Jan Beulich672c0ae2018-02-23 01:27:37 -0700441static void walk_pud_level(struct seq_file *m, struct pg_state *st, p4d_t addr,
442 pgprotval_t eff_in, unsigned long P)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200443{
444 int i;
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300445 pud_t *start, *pud_start;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700446 pgprotval_t prot, eff;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200447
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300448 pud_start = start = (pud_t *)p4d_page_vaddr(addr);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200449
450 for (i = 0; i < PTRS_PER_PUD; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200451 st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300452 if (!pud_none(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700453 prot = pud_flags(*start);
454 eff = effective_prot(eff_in, prot);
Toshi Kanida25e622015-09-17 12:24:19 -0600455 if (pud_large(*start) || !pud_present(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700456 note_page(m, st, __pgprot(prot), eff, 3);
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300457 } else if (!kasan_page_table(m, st, pud_start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700458 walk_pmd_level(m, st, *start, eff,
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200459 P + i * PUD_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600460 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200461 } else
Jan Beulich672c0ae2018-02-23 01:27:37 -0700462 note_page(m, st, __pgprot(0), 0, 3);
Arjan van de Ven926e5392008-04-17 17:40:45 +0200463
464 start++;
465 }
466}
467
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200468#else
Jan Beulich672c0ae2018-02-23 01:27:37 -0700469#define walk_pud_level(m,s,a,e,p) walk_pmd_level(m,s,__pud(p4d_val(a)),e,p)
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300470#define p4d_large(a) pud_large(__pud(p4d_val(a)))
471#define p4d_none(a) pud_none(__pud(p4d_val(a)))
472#endif
473
Jan Beulich672c0ae2018-02-23 01:27:37 -0700474static void walk_p4d_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
475 pgprotval_t eff_in, unsigned long P)
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300476{
477 int i;
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300478 p4d_t *start, *p4d_start;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700479 pgprotval_t prot, eff;
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300480
Kirill A. Shutemovc65e7742018-02-14 14:16:53 +0300481 if (PTRS_PER_P4D == 1)
Jan Beulich672c0ae2018-02-23 01:27:37 -0700482 return walk_pud_level(m, st, __p4d(pgd_val(addr)), eff_in, P);
Kirill A. Shutemovc65e7742018-02-14 14:16:53 +0300483
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300484 p4d_start = start = (p4d_t *)pgd_page_vaddr(addr);
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300485
486 for (i = 0; i < PTRS_PER_P4D; i++) {
487 st->current_address = normalize_addr(P + i * P4D_LEVEL_MULT);
488 if (!p4d_none(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700489 prot = p4d_flags(*start);
490 eff = effective_prot(eff_in, prot);
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300491 if (p4d_large(*start) || !p4d_present(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700492 note_page(m, st, __pgprot(prot), eff, 2);
Andrey Ryabinin04b67022017-07-24 18:25:58 +0300493 } else if (!kasan_page_table(m, st, p4d_start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700494 walk_pud_level(m, st, *start, eff,
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300495 P + i * P4D_LEVEL_MULT);
496 }
497 } else
Jan Beulich672c0ae2018-02-23 01:27:37 -0700498 note_page(m, st, __pgprot(0), 0, 2);
Kirill A. Shutemovfdd3d8c2017-03-28 13:48:06 +0300499
500 start++;
501 }
502}
503
Kirill A. Shutemoved7588d2018-05-18 13:35:24 +0300504#define pgd_large(a) (pgtable_l5_enabled() ? pgd_large(a) : p4d_large(__p4d(pgd_val(a))))
505#define pgd_none(a) (pgtable_l5_enabled() ? pgd_none(a) : p4d_none(__p4d(pgd_val(a))))
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200506
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500507static inline bool is_hypervisor_range(int idx)
508{
Borislav Petkovb1768622016-02-18 21:00:41 +0100509#ifdef CONFIG_X86_64
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500510 /*
Kirill A. Shutemov16877a52018-11-30 23:23:27 +0300511 * A hole in the beginning of kernel address space reserved
512 * for a hypervisor.
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500513 */
Kirill A. Shutemov16877a52018-11-30 23:23:27 +0300514 return (idx >= pgd_index(GUARD_HOLE_BASE_ADDR)) &&
515 (idx < pgd_index(GUARD_HOLE_END_ADDR));
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500516#else
Borislav Petkovb1768622016-02-18 21:00:41 +0100517 return false;
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500518#endif
Borislav Petkovb1768622016-02-18 21:00:41 +0100519}
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500520
Stephen Smalleye1a58322015-10-05 12:55:20 -0400521static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100522 bool checkwx, bool dmesg)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200523{
Joerg Roedel4e8537e2018-07-18 11:41:08 +0200524 pgd_t *start = INIT_PGD;
Jan Beulich672c0ae2018-02-23 01:27:37 -0700525 pgprotval_t prot, eff;
Arjan van de Ven926e5392008-04-17 17:40:45 +0200526 int i;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100527 struct pg_state st = {};
Arjan van de Ven926e5392008-04-17 17:40:45 +0200528
Borislav Petkovef6bea62014-01-18 12:48:14 +0100529 if (pgd) {
530 start = pgd;
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100531 st.to_dmesg = dmesg;
Borislav Petkovef6bea62014-01-18 12:48:14 +0100532 }
Arjan van de Ven926e5392008-04-17 17:40:45 +0200533
Stephen Smalleye1a58322015-10-05 12:55:20 -0400534 st.check_wx = checkwx;
535 if (checkwx)
536 st.wx_pages = 0;
537
Arjan van de Ven926e5392008-04-17 17:40:45 +0200538 for (i = 0; i < PTRS_PER_PGD; i++) {
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200539 st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
Boris Ostrovskyf4e342c2015-11-05 13:56:35 -0500540 if (!pgd_none(*start) && !is_hypervisor_range(i)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700541 prot = pgd_flags(*start);
542#ifdef CONFIG_X86_PAE
543 eff = _PAGE_USER | _PAGE_RW;
544#else
545 eff = prot;
546#endif
Toshi Kanida25e622015-09-17 12:24:19 -0600547 if (pgd_large(*start) || !pgd_present(*start)) {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700548 note_page(m, &st, __pgprot(prot), eff, 1);
Toshi Kanida25e622015-09-17 12:24:19 -0600549 } else {
Jan Beulich672c0ae2018-02-23 01:27:37 -0700550 walk_p4d_level(m, &st, *start, eff,
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200551 i * PGD_LEVEL_MULT);
Toshi Kanida25e622015-09-17 12:24:19 -0600552 }
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200553 } else
Jan Beulich672c0ae2018-02-23 01:27:37 -0700554 note_page(m, &st, __pgprot(0), 0, 1);
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200555
Andrey Ryabinin146fbb762017-02-10 12:54:05 +0300556 cond_resched();
Arjan van de Ven926e5392008-04-17 17:40:45 +0200557 start++;
558 }
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200559
560 /* Flush out the last page */
561 st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT);
Jan Beulich672c0ae2018-02-23 01:27:37 -0700562 note_page(m, &st, __pgprot(0), 0, 0);
Stephen Smalleye1a58322015-10-05 12:55:20 -0400563 if (!checkwx)
564 return;
565 if (st.wx_pages)
566 pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n",
567 st.wx_pages);
568 else
569 pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n");
Arjan van de Ven926e5392008-04-17 17:40:45 +0200570}
571
Stephen Smalleye1a58322015-10-05 12:55:20 -0400572void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
573{
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100574 ptdump_walk_pgd_level_core(m, pgd, false, true);
Stephen Smalleye1a58322015-10-05 12:55:20 -0400575}
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100576
Thomas Gleixnera4b51ef2017-12-04 15:08:06 +0100577void ptdump_walk_pgd_level_debugfs(struct seq_file *m, pgd_t *pgd, bool user)
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100578{
Thomas Gleixnera4b51ef2017-12-04 15:08:06 +0100579#ifdef CONFIG_PAGE_TABLE_ISOLATION
Borislav Petkov28e3ace2019-03-29 20:00:38 +0100580 if (user && boot_cpu_has(X86_FEATURE_PTI))
Thomas Gleixnera4b51ef2017-12-04 15:08:06 +0100581 pgd = kernel_to_user_pgdp(pgd);
582#endif
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100583 ptdump_walk_pgd_level_core(m, pgd, false, false);
584}
585EXPORT_SYMBOL_GPL(ptdump_walk_pgd_level_debugfs);
586
Joerg Roedeld878efc2018-08-08 13:16:40 +0200587void ptdump_walk_user_pgd_level_checkwx(void)
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100588{
589#ifdef CONFIG_PAGE_TABLE_ISOLATION
Joerg Roedel4e8537e2018-07-18 11:41:08 +0200590 pgd_t *pgd = INIT_PGD;
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100591
Joerg Roedeld878efc2018-08-08 13:16:40 +0200592 if (!(__supported_pte_mask & _PAGE_NX) ||
Borislav Petkov28e3ace2019-03-29 20:00:38 +0100593 !boot_cpu_has(X86_FEATURE_PTI))
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100594 return;
595
596 pr_info("x86/mm: Checking user space page tables\n");
597 pgd = kernel_to_user_pgdp(pgd);
598 ptdump_walk_pgd_level_core(NULL, pgd, true, false);
599#endif
600}
Stephen Smalleye1a58322015-10-05 12:55:20 -0400601
602void ptdump_walk_pgd_level_checkwx(void)
603{
Thomas Gleixnerb4bf4f92017-12-04 15:08:05 +0100604 ptdump_walk_pgd_level_core(NULL, NULL, true, false);
Stephen Smalleye1a58322015-10-05 12:55:20 -0400605}
606
Kees Cook8609d1b2015-11-19 17:07:55 -0800607static int __init pt_dump_init(void)
Arjan van de Ven926e5392008-04-17 17:40:45 +0200608{
Thomas Garnier0483e1f2016-06-21 17:47:02 -0700609 /*
610 * Various markers are not compile-time constants, so assign them
611 * here.
612 */
613#ifdef CONFIG_X86_64
614 address_markers[LOW_KERNEL_NR].start_address = PAGE_OFFSET;
615 address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
616 address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START;
Kirill A. Shutemov5c7919b2018-02-14 14:16:52 +0300617#ifdef CONFIG_MODIFY_LDT_SYSCALL
618 address_markers[LDT_NR].start_address = LDT_BASE_ADDR;
619#endif
Kirill A. Shutemov09e61a72018-02-14 14:16:55 +0300620#ifdef CONFIG_KASAN
621 address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START;
622 address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END;
623#endif
Thomas Garnier0483e1f2016-06-21 17:47:02 -0700624#endif
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200625#ifdef CONFIG_X86_32
Andres Salomon92851e22010-07-20 15:19:46 -0700626 address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
627 address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200628# ifdef CONFIG_HIGHMEM
Andres Salomon92851e22010-07-20 15:19:46 -0700629 address_markers[PKMAP_BASE_NR].start_address = PKMAP_BASE;
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200630# endif
Andres Salomon92851e22010-07-20 15:19:46 -0700631 address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
Thomas Gleixner92a0f812017-12-20 18:51:31 +0100632 address_markers[CPU_ENTRY_AREA_NR].start_address = CPU_ENTRY_AREA_BASE;
Joerg Roedelf3e48e52018-07-18 11:41:10 +0200633# ifdef CONFIG_MODIFY_LDT_SYSCALL
634 address_markers[LDT_NR].start_address = LDT_BASE_ADDR;
635# endif
H. Peter Anvinfe770bf02008-04-17 17:40:45 +0200636#endif
Arjan van de Ven926e5392008-04-17 17:40:45 +0200637 return 0;
638}
Arjan van de Ven926e5392008-04-17 17:40:45 +0200639__initcall(pt_dump_init);