dump_pagetables.c (926e5392ba8a388ae32ca0d2714cc2c73945c609) | dump_pagetables.c (fe770bf0310d90b3b033c19044d45b7de5f2041c) |
---|---|
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 | 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 |
15#include <linux/debugfs.h> 16#include <linux/mm.h> |
|
15#include <linux/module.h> 16#include <linux/seq_file.h> | 17#include <linux/module.h> 18#include <linux/seq_file.h> |
17#include <linux/debugfs.h> | |
18 19#include <asm/pgtable.h> 20 21/* 22 * The dumper groups pagetable entries of the same type into one, and for 23 * that it needs to keep some state when walking, and flush this state 24 * when a "break" in the continuity is found. 25 */ 26struct pg_state { 27 int level; 28 pgprot_t current_prot; 29 unsigned long start_address; 30 unsigned long current_address; | 19 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; |
31 int printed_vmalloc; 32 int printed_modules; 33 int printed_vmemmap; 34 int printed_highmap; | 32 const struct addr_marker *marker; |
35}; 36 | 33}; 34 |
37/* Multipliers for offsets within the PTEs */ 38#define LEVEL_4_MULT (PAGE_SIZE) 39#define LEVEL_3_MULT (512UL * LEVEL_4_MULT) 40#define LEVEL_2_MULT (512UL * LEVEL_3_MULT) 41#define LEVEL_1_MULT (512UL * LEVEL_2_MULT) | 35struct addr_marker { 36 unsigned long start_address; 37 const char *name; 38}; |
42 | 39 |
40/* Address space markers hints */ 41static struct addr_marker address_markers[] = { 42 { 0, "User Space" }, 43#ifdef CONFIG_X86_64 44 { 0x8000000000000000UL, "Kernel Space" }, 45 { 0xffff810000000000UL, "Low Kernel Mapping" }, 46 { VMALLOC_START, "vmalloc() Area" }, 47 { MODULES_VADDR, "Modules" }, 48 { MODULES_END, "End Modules" }, 49 { VMEMMAP_START, "Vmemmap" }, 50 { __START_KERNEL_map, "High Kernel Mapping" }, 51#else 52 { PAGE_OFFSET, "Kernel Mapping" }, 53 { 0/* VMALLOC_START */, "vmalloc() Area" }, 54 { 0/*VMALLOC_END*/, "vmalloc() End" }, 55# ifdef CONFIG_HIGHMEM 56 { 0/*PKMAP_BASE*/, "Persisent kmap() Area" }, 57# endif 58 { 0/*FIXADDR_START*/, "Fixmap Area" }, 59#endif 60 { -1, NULL } /* End of list */ 61}; |
|
43 | 62 |
63/* Multipliers for offsets within the PTEs */ 64#define PTE_LEVEL_MULT (PAGE_SIZE) 65#define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT) 66#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT) 67#define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT) 68 |
|
44/* 45 * Print a readable form of a pgprot_t to the seq_file 46 */ 47static void printk_prot(struct seq_file *m, pgprot_t prot, int level) 48{ | 69/* 70 * Print a readable form of a pgprot_t to the seq_file 71 */ 72static void printk_prot(struct seq_file *m, pgprot_t prot, int level) 73{ |
49 unsigned long pr = pgprot_val(prot); | 74 pgprotval_t pr = pgprot_val(prot); 75 static const char * const level_name[] = 76 { "cr3", "pgd", "pud", "pmd", "pte" }; |
50 | 77 |
51 if (pr & _PAGE_USER) 52 seq_printf(m, "USR "); 53 else 54 seq_printf(m, " "); 55 if (pr & _PAGE_RW) 56 seq_printf(m, "RW "); 57 else 58 seq_printf(m, "ro "); 59 if (pr & _PAGE_PWT) 60 seq_printf(m, "PWT "); 61 else 62 seq_printf(m, " "); 63 if (pr & _PAGE_PCD) 64 seq_printf(m, "PCD "); 65 else 66 seq_printf(m, " "); 67 68 /* Bit 9 has a different meaning on level 3 vs 4 */ 69 if (level <= 3) { 70 if (pr & _PAGE_PSE) 71 seq_printf(m, "PSE "); | 78 if (!pgprot_val(prot)) { 79 /* Not present */ 80 seq_printf(m, " "); 81 } else { 82 if (pr & _PAGE_USER) 83 seq_printf(m, "USR "); |
72 else 73 seq_printf(m, " "); | 84 else 85 seq_printf(m, " "); |
74 } else { 75 if (pr & _PAGE_PAT) 76 seq_printf(m, "pat "); | 86 if (pr & _PAGE_RW) 87 seq_printf(m, "RW "); |
77 else | 88 else |
89 seq_printf(m, "ro "); 90 if (pr & _PAGE_PWT) 91 seq_printf(m, "PWT "); 92 else |
|
78 seq_printf(m, " "); | 93 seq_printf(m, " "); |
94 if (pr & _PAGE_PCD) 95 seq_printf(m, "PCD "); 96 else 97 seq_printf(m, " "); 98 99 /* Bit 9 has a different meaning on level 3 vs 4 */ 100 if (level <= 3) { 101 if (pr & _PAGE_PSE) 102 seq_printf(m, "PSE "); 103 else 104 seq_printf(m, " "); 105 } else { 106 if (pr & _PAGE_PAT) 107 seq_printf(m, "pat "); 108 else 109 seq_printf(m, " "); 110 } 111 if (pr & _PAGE_GLOBAL) 112 seq_printf(m, "GLB "); 113 else 114 seq_printf(m, " "); 115 if (pr & _PAGE_NX) 116 seq_printf(m, "NX "); 117 else 118 seq_printf(m, "x "); |
|
79 } | 119 } |
80 if (pr & _PAGE_GLOBAL) 81 seq_printf(m, "GLB "); 82 else 83 seq_printf(m, " "); 84 if (pr & _PAGE_NX) 85 seq_printf(m, "NX "); 86 else 87 seq_printf(m, "x "); | 120 seq_printf(m, "%s\n", level_name[level]); |
88} 89 90/* | 121} 122 123/* |
91 * Sign-extend the 48 bit address to 64 bit | 124 * On 64 bits, sign-extend the 48 bit address to 64 bit |
92 */ | 125 */ |
93static unsigned long sign_extend(unsigned long u) | 126static unsigned long normalize_addr(unsigned long u) |
94{ | 127{ |
95 if (u>>47) 96 u = u | (0xffffUL << 48); | 128#ifdef CONFIG_X86_64 129 return (signed long)(u << 16) >> 16; 130#else |
97 return u; | 131 return u; |
132#endif |
|
98} 99 100/* 101 * This function gets called on a break in a continuous series 102 * of PTE entries; the next one is different so we need to 103 * print what we collected so far. 104 */ 105static void note_page(struct seq_file *m, struct pg_state *st, | 133} 134 135/* 136 * This function gets called on a break in a continuous series 137 * of PTE entries; the next one is different so we need to 138 * print what we collected so far. 139 */ 140static void note_page(struct seq_file *m, struct pg_state *st, |
106 pgprot_t new_prot, int level) | 141 pgprot_t new_prot, int level) |
107{ | 142{ |
108 unsigned long prot, cur; | 143 pgprotval_t prot, cur; 144 static const char units[] = "KMGTPE"; |
109 110 /* 111 * If we have a "break" in the series, we need to flush the state that | 145 146 /* 147 * If we have a "break" in the series, we need to flush the state that |
112 * we have now. "break" is either changing perms or a different level. | 148 * we have now. "break" is either changing perms, levels or 149 * address space marker. |
113 */ 114 prot = pgprot_val(new_prot) & ~(PTE_MASK); 115 cur = pgprot_val(st->current_prot) & ~(PTE_MASK); 116 | 150 */ 151 prot = pgprot_val(new_prot) & ~(PTE_MASK); 152 cur = pgprot_val(st->current_prot) & ~(PTE_MASK); 153 |
117 if ((prot != cur || level != st->level) && 118 st->current_address != st->start_address) { 119 char unit = 'K'; | 154 if (!st->level) { 155 /* First entry */ 156 st->current_prot = new_prot; 157 st->level = level; 158 st->marker = address_markers; 159 seq_printf(m, "---[ %s ]---\n", st->marker->name); 160 } else if (prot != cur || level != st->level || 161 st->current_address >= st->marker[1].start_address) { 162 const char *unit = units; |
120 unsigned long delta; 121 122 /* | 163 unsigned long delta; 164 165 /* |
123 * We print markers for special areas of address space, 124 * such as the start of vmalloc space etc. 125 * This helps in the interpretation. | 166 * Now print the actual finished series |
126 */ | 167 */ |
127 if (!st->printed_vmalloc && 128 st->start_address >= VMALLOC_START) { 129 seq_printf(m, "---[ VMALLOC SPACE ]---\n"); 130 st->printed_vmalloc = 1; | 168 seq_printf(m, "0x%p-0x%p ", 169 (void *)st->start_address, 170 (void *)st->current_address); 171 172 delta = (st->current_address - st->start_address) >> 10; 173 while (!(delta & 1023) && unit[1]) { 174 delta >>= 10; 175 unit++; |
131 } | 176 } |
132 if (!st->printed_modules && 133 st->start_address >= MODULES_VADDR) { 134 seq_printf(m, "---[ MODULES SPACE ]---\n"); 135 st->printed_modules = 1; 136 } 137 if (st->printed_modules < 2 && 138 st->start_address >= MODULES_END) { 139 seq_printf(m, "---[ END MODULES SPACE ]---\n"); 140 st->printed_modules = 2; 141 } 142 if (!st->printed_vmemmap && 143 st->start_address >= VMEMMAP_START) { 144 seq_printf(m, "---[ VMMEMMAP SPACE ]---\n"); 145 st->printed_vmemmap = 1; 146 } 147 if (!st->printed_highmap && 148 st->start_address >= __START_KERNEL_map) { 149 seq_printf(m, "---[ HIGH KERNEL MAPPING ]---\n"); 150 st->printed_highmap = 1; 151 } | 177 seq_printf(m, "%9lu%c ", delta, *unit); 178 printk_prot(m, st->current_prot, st->level); |
152 153 /* | 179 180 /* |
154 * Now print the actual finished series | 181 * We print markers for special areas of address space, 182 * such as the start of vmalloc space etc. 183 * This helps in the interpretation. |
155 */ | 184 */ |
156 seq_printf(m, "[ %016lx - %016lx ", 157 st->start_address, st->current_address); 158 159 delta = (st->current_address - st->start_address) >> 10; 160 if ((delta & 1023) == 0) { 161 delta = delta >> 10; 162 unit = 'M'; | 185 if (st->current_address >= st->marker[1].start_address) { 186 st->marker++; 187 seq_printf(m, "---[ %s ]---\n", st->marker->name); |
163 } | 188 } |
164 if (pgprot_val(st->current_prot)) { 165 seq_printf(m, "Size %9lu%cb ", delta, unit); 166 printk_prot(m, st->current_prot, st->level); 167 seq_printf(m, "L%i]\n", st->level); 168 } else { 169 /* don't print protections on non-present memory */ 170 seq_printf(m, "%14lu%cb", delta, unit); 171 seq_printf(m, " L%i]\n", 172 st->level); 173 } | 189 |
174 st->start_address = st->current_address; 175 st->current_prot = new_prot; 176 st->level = level; | 190 st->start_address = st->current_address; 191 st->current_prot = new_prot; 192 st->level = level; |
177 }; | 193 } |
178} 179 | 194} 195 |
180static void walk_level_4(struct seq_file *m, struct pg_state *st, pmd_t addr, | 196static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr, |
181 unsigned long P) 182{ 183 int i; 184 pte_t *start; 185 186 start = (pte_t *) pmd_page_vaddr(addr); 187 for (i = 0; i < PTRS_PER_PTE; i++) { 188 pgprot_t prot = pte_pgprot(*start); 189 | 197 unsigned long P) 198{ 199 int i; 200 pte_t *start; 201 202 start = (pte_t *) pmd_page_vaddr(addr); 203 for (i = 0; i < PTRS_PER_PTE; i++) { 204 pgprot_t prot = pte_pgprot(*start); 205 |
190 st->current_address = sign_extend(P + i * LEVEL_4_MULT); | 206 st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT); |
191 note_page(m, st, prot, 4); 192 start++; 193 } 194} 195 | 207 note_page(m, st, prot, 4); 208 start++; 209 } 210} 211 |
212#if PTRS_PER_PMD > 1 |
|
196 | 213 |
197static void walk_level_3(struct seq_file *m, struct pg_state *st, pud_t addr, | 214static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr, |
198 unsigned long P) 199{ 200 int i; 201 pmd_t *start; 202 203 start = (pmd_t *) pud_page_vaddr(addr); 204 for (i = 0; i < PTRS_PER_PMD; i++) { | 215 unsigned long P) 216{ 217 int i; 218 pmd_t *start; 219 220 start = (pmd_t *) pud_page_vaddr(addr); 221 for (i = 0; i < PTRS_PER_PMD; i++) { |
205 st->current_address = sign_extend(P + i * LEVEL_3_MULT); | 222 st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT); |
206 if (!pmd_none(*start)) { | 223 if (!pmd_none(*start)) { |
207 unsigned long prot; | 224 pgprotval_t prot = pmd_val(*start) & ~PTE_MASK; |
208 | 225 |
209 prot = pmd_val(*start) & ~(PTE_MASK); 210 /* Deal with 2Mb pages */ 211 if (pmd_large(*start)) | 226 if (pmd_large(*start) || !pmd_present(*start)) |
212 note_page(m, st, __pgprot(prot), 3); 213 else | 227 note_page(m, st, __pgprot(prot), 3); 228 else |
214 walk_level_4(m, st, *start, 215 P + i * LEVEL_3_MULT); | 229 walk_pte_level(m, st, *start, 230 P + i * PMD_LEVEL_MULT); |
216 } else 217 note_page(m, st, __pgprot(0), 3); 218 start++; 219 } 220} 221 | 231 } else 232 note_page(m, st, __pgprot(0), 3); 233 start++; 234 } 235} 236 |
237#else 238#define walk_pmd_level(m,s,a,p) walk_pte_level(m,s,__pmd(pud_val(a)),p) 239#define pud_large(a) pmd_large(__pmd(pud_val(a))) 240#define pud_none(a) pmd_none(__pmd(pud_val(a))) 241#endif |
|
222 | 242 |
223static void walk_level_2(struct seq_file *m, struct pg_state *st, pgd_t addr, | 243#if PTRS_PER_PUD > 1 244 245static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr, |
224 unsigned long P) 225{ 226 int i; 227 pud_t *start; 228 229 start = (pud_t *) pgd_page_vaddr(addr); 230 231 for (i = 0; i < PTRS_PER_PUD; i++) { | 246 unsigned long P) 247{ 248 int i; 249 pud_t *start; 250 251 start = (pud_t *) pgd_page_vaddr(addr); 252 253 for (i = 0; i < PTRS_PER_PUD; i++) { |
254 st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT); |
|
232 if (!pud_none(*start)) { | 255 if (!pud_none(*start)) { |
233 unsigned long prot; | 256 pgprotval_t prot = pud_val(*start) & ~PTE_MASK; |
234 | 257 |
235 prot = pud_val(*start) & ~(PTE_MASK); 236 /* Deal with 1Gb pages */ 237 if (pud_large(*start)) | 258 if (pud_large(*start) || !pud_present(*start)) |
238 note_page(m, st, __pgprot(prot), 2); 239 else | 259 note_page(m, st, __pgprot(prot), 2); 260 else |
240 walk_level_3(m, st, *start, 241 P + i * LEVEL_2_MULT); | 261 walk_pmd_level(m, st, *start, 262 P + i * PUD_LEVEL_MULT); |
242 } else 243 note_page(m, st, __pgprot(0), 2); 244 245 start++; 246 } 247} 248 | 263 } else 264 note_page(m, st, __pgprot(0), 2); 265 266 start++; 267 } 268} 269 |
249static void walk_level_1(struct seq_file *m) | 270#else 271#define walk_pud_level(m,s,a,p) walk_pmd_level(m,s,__pud(pgd_val(a)),p) 272#define pgd_large(a) pud_large(__pud(pgd_val(a))) 273#define pgd_none(a) pud_none(__pud(pgd_val(a))) 274#endif 275 276static void walk_pgd_level(struct seq_file *m) |
250{ | 277{ |
278#ifdef CONFIG_X86_64 |
|
251 pgd_t *start = (pgd_t *) &init_level4_pgt; | 279 pgd_t *start = (pgd_t *) &init_level4_pgt; |
280#else 281 pgd_t *start = swapper_pg_dir; 282#endif |
|
252 int i; 253 struct pg_state st; 254 255 memset(&st, 0, sizeof(st)); | 283 int i; 284 struct pg_state st; 285 286 memset(&st, 0, sizeof(st)); |
256 st.level = 1; | |
257 258 for (i = 0; i < PTRS_PER_PGD; i++) { | 287 288 for (i = 0; i < PTRS_PER_PGD; i++) { |
259 if (!pgd_none(*start)) 260 walk_level_2(m, &st, *start, i * LEVEL_1_MULT); 261 else | 289 st.current_address = normalize_addr(i * PGD_LEVEL_MULT); 290 if (!pgd_none(*start)) { 291 pgprotval_t prot = pgd_val(*start) & ~PTE_MASK; 292 293 if (pgd_large(*start) || !pgd_present(*start)) 294 note_page(m, &st, __pgprot(prot), 1); 295 else 296 walk_pud_level(m, &st, *start, 297 i * PGD_LEVEL_MULT); 298 } else |
262 note_page(m, &st, __pgprot(0), 1); | 299 note_page(m, &st, __pgprot(0), 1); |
300 |
|
263 start++; 264 } | 301 start++; 302 } |
303 304 /* Flush out the last page */ 305 st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT); 306 note_page(m, &st, __pgprot(0), 0); |
|
265} 266 267static int ptdump_show(struct seq_file *m, void *v) 268{ | 307} 308 309static int ptdump_show(struct seq_file *m, void *v) 310{ |
269 seq_puts(m, "Kernel pagetable dump\n"); 270 walk_level_1(m); | 311 walk_pgd_level(m); |
271 return 0; 272} 273 274static int ptdump_open(struct inode *inode, struct file *filp) 275{ 276 return single_open(filp, ptdump_show, NULL); 277} 278 279static const struct file_operations ptdump_fops = { 280 .open = ptdump_open, 281 .read = seq_read, 282 .llseek = seq_lseek, 283 .release = single_release, 284}; 285 286int pt_dump_init(void) 287{ 288 struct dentry *pe; 289 | 312 return 0; 313} 314 315static int ptdump_open(struct inode *inode, struct file *filp) 316{ 317 return single_open(filp, ptdump_show, NULL); 318} 319 320static const struct file_operations ptdump_fops = { 321 .open = ptdump_open, 322 .read = seq_read, 323 .llseek = seq_lseek, 324 .release = single_release, 325}; 326 327int pt_dump_init(void) 328{ 329 struct dentry *pe; 330 |
331#ifdef CONFIG_X86_32 332 /* Not a compile-time constant on x86-32 */ 333 address_markers[2].start_address = VMALLOC_START; 334 address_markers[3].start_address = VMALLOC_END; 335# ifdef CONFIG_HIGHMEM 336 address_markers[4].start_address = PKMAP_BASE; 337 address_markers[5].start_address = FIXADDR_START; 338# else 339 address_markers[4].start_address = FIXADDR_START; 340# endif 341#endif 342 |
|
290 pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL, 291 &ptdump_fops); 292 if (!pe) 293 return -ENOMEM; 294 295 return 0; 296} 297 298__initcall(pt_dump_init); 299MODULE_LICENSE("GPL"); 300MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>"); 301MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables"); | 343 pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL, 344 &ptdump_fops); 345 if (!pe) 346 return -ENOMEM; 347 348 return 0; 349} 350 351__initcall(pt_dump_init); 352MODULE_LICENSE("GPL"); 353MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>"); 354MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables"); |