xref: /linux/arch/x86/mm/dump_pagetables.c (revision 926e5392ba8a388ae32ca0d2714cc2c73945c609)
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/module.h>
16 #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  */
26 struct pg_state {
27 	int level;
28 	pgprot_t current_prot;
29 	unsigned long start_address;
30 	unsigned long current_address;
31 	int printed_vmalloc;
32 	int printed_modules;
33 	int printed_vmemmap;
34 	int printed_highmap;
35 };
36 
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)
42 
43 
44 /*
45  * Print a readable form of a pgprot_t to the seq_file
46  */
47 static void printk_prot(struct seq_file *m, pgprot_t prot, int level)
48 {
49 	unsigned long pr = pgprot_val(prot);
50 
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 ");
72 		else
73 			seq_printf(m, "    ");
74 	} else {
75 		if (pr & _PAGE_PAT)
76 			seq_printf(m, "pat ");
77 		else
78 			seq_printf(m, "    ");
79 	}
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  ");
88 }
89 
90 /*
91  * Sign-extend the 48 bit address to 64 bit
92  */
93 static unsigned long sign_extend(unsigned long u)
94 {
95 	if (u>>47)
96 		u = u | (0xffffUL << 48);
97 	return u;
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  */
105 static void note_page(struct seq_file *m, struct pg_state *st,
106 					pgprot_t new_prot, int level)
107 {
108 	unsigned long prot, cur;
109 
110 	/*
111 	 * 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.
113 	 */
114 	prot = pgprot_val(new_prot) & ~(PTE_MASK);
115 	cur = pgprot_val(st->current_prot) & ~(PTE_MASK);
116 
117 	if ((prot != cur || level != st->level) &&
118 				st->current_address != st->start_address) {
119 		char unit = 'K';
120 		unsigned long delta;
121 
122 		/*
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.
126 		 */
127 		if (!st->printed_vmalloc &&
128 				st->start_address >= VMALLOC_START) {
129 			seq_printf(m, "---[ VMALLOC SPACE ]---\n");
130 			st->printed_vmalloc = 1;
131 		}
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 		}
152 
153 		/*
154 		 * Now print the actual finished series
155 		 */
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';
163 		}
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 		}
174 		st->start_address = st->current_address;
175 		st->current_prot = new_prot;
176 		st->level = level;
177 	};
178 }
179 
180 static void walk_level_4(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 
190 		st->current_address = sign_extend(P + i * LEVEL_4_MULT);
191 		note_page(m, st, prot, 4);
192 		start++;
193 	}
194 }
195 
196 
197 static void walk_level_3(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++) {
205 		st->current_address = sign_extend(P + i * LEVEL_3_MULT);
206 		if (!pmd_none(*start)) {
207 			unsigned long prot;
208 
209 			prot = pmd_val(*start) & ~(PTE_MASK);
210 			/* Deal with 2Mb pages */
211 			if (pmd_large(*start))
212 				note_page(m, st, __pgprot(prot), 3);
213 			else
214 				walk_level_4(m, st, *start,
215 							P + i * LEVEL_3_MULT);
216 		} else
217 			note_page(m, st, __pgprot(0), 3);
218 		start++;
219 	}
220 }
221 
222 
223 static void walk_level_2(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++) {
232 		if (!pud_none(*start)) {
233 			unsigned long prot;
234 
235 			prot = pud_val(*start) & ~(PTE_MASK);
236 			/* Deal with 1Gb pages */
237 			if (pud_large(*start))
238 				note_page(m, st, __pgprot(prot), 2);
239 			else
240 				walk_level_3(m, st, *start,
241 					P + i * LEVEL_2_MULT);
242 		} else
243 			note_page(m, st, __pgprot(0), 2);
244 
245 		start++;
246 	}
247 }
248 
249 static void walk_level_1(struct seq_file *m)
250 {
251 	pgd_t *start = (pgd_t *) &init_level4_pgt;
252 	int i;
253 	struct pg_state st;
254 
255 	memset(&st, 0, sizeof(st));
256 	st.level = 1;
257 
258 	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
262 			note_page(m, &st, __pgprot(0), 1);
263 		start++;
264 	}
265 }
266 
267 static int ptdump_show(struct seq_file *m, void *v)
268 {
269 	seq_puts(m, "Kernel pagetable dump\n");
270 	walk_level_1(m);
271 	return 0;
272 }
273 
274 static int ptdump_open(struct inode *inode, struct file *filp)
275 {
276 	return single_open(filp, ptdump_show, NULL);
277 }
278 
279 static const struct file_operations ptdump_fops = {
280 	.open		= ptdump_open,
281 	.read		= seq_read,
282 	.llseek		= seq_lseek,
283 	.release	= single_release,
284 };
285 
286 int pt_dump_init(void)
287 {
288 	struct dentry *pe;
289 
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);
299 MODULE_LICENSE("GPL");
300 MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
301 MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables");
302