Merge tag 'regmap-v3.15-nodev' of git://git.kernel.org/pub/scm/linux/kernel/git/broon...
[firefly-linux-kernel-4.4.55.git] / arch / x86 / mm / dump_pagetables.c
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>
17 #include <linux/module.h>
18 #include <linux/seq_file.h>
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  */
27 struct pg_state {
28         int level;
29         pgprot_t current_prot;
30         unsigned long start_address;
31         unsigned long current_address;
32         const struct addr_marker *marker;
33         bool to_dmesg;
34 };
35
36 struct addr_marker {
37         unsigned long start_address;
38         const char *name;
39 };
40
41 /* indices for address_markers; keep sync'd w/ address_markers below */
42 enum address_markers_idx {
43         USER_SPACE_NR = 0,
44 #ifdef CONFIG_X86_64
45         KERNEL_SPACE_NR,
46         LOW_KERNEL_NR,
47         VMALLOC_START_NR,
48         VMEMMAP_START_NR,
49         HIGH_KERNEL_NR,
50         MODULES_VADDR_NR,
51         MODULES_END_NR,
52 #else
53         KERNEL_SPACE_NR,
54         VMALLOC_START_NR,
55         VMALLOC_END_NR,
56 # ifdef CONFIG_HIGHMEM
57         PKMAP_BASE_NR,
58 # endif
59         FIXADDR_START_NR,
60 #endif
61 };
62
63 /* Address space markers hints */
64 static struct addr_marker address_markers[] = {
65         { 0, "User Space" },
66 #ifdef CONFIG_X86_64
67         { 0x8000000000000000UL, "Kernel Space" },
68         { PAGE_OFFSET,          "Low Kernel Mapping" },
69         { VMALLOC_START,        "vmalloc() Area" },
70         { VMEMMAP_START,        "Vmemmap" },
71         { __START_KERNEL_map,   "High Kernel Mapping" },
72         { MODULES_VADDR,        "Modules" },
73         { MODULES_END,          "End Modules" },
74 #else
75         { PAGE_OFFSET,          "Kernel Mapping" },
76         { 0/* VMALLOC_START */, "vmalloc() Area" },
77         { 0/*VMALLOC_END*/,     "vmalloc() End" },
78 # ifdef CONFIG_HIGHMEM
79         { 0/*PKMAP_BASE*/,      "Persisent kmap() Area" },
80 # endif
81         { 0/*FIXADDR_START*/,   "Fixmap Area" },
82 #endif
83         { -1, NULL }            /* End of list */
84 };
85
86 /* Multipliers for offsets within the PTEs */
87 #define PTE_LEVEL_MULT (PAGE_SIZE)
88 #define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT)
89 #define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT)
90 #define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT)
91
92 #define pt_dump_seq_printf(m, to_dmesg, fmt, args...)           \
93 ({                                                              \
94         if (to_dmesg)                                   \
95                 printk(KERN_INFO fmt, ##args);                  \
96         else                                                    \
97                 if (m)                                          \
98                         seq_printf(m, fmt, ##args);             \
99 })
100
101 #define pt_dump_cont_printf(m, to_dmesg, fmt, args...)          \
102 ({                                                              \
103         if (to_dmesg)                                   \
104                 printk(KERN_CONT fmt, ##args);                  \
105         else                                                    \
106                 if (m)                                          \
107                         seq_printf(m, fmt, ##args);             \
108 })
109
110 /*
111  * Print a readable form of a pgprot_t to the seq_file
112  */
113 static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg)
114 {
115         pgprotval_t pr = pgprot_val(prot);
116         static const char * const level_name[] =
117                 { "cr3", "pgd", "pud", "pmd", "pte" };
118
119         if (!pgprot_val(prot)) {
120                 /* Not present */
121                 pt_dump_cont_printf(m, dmsg, "                          ");
122         } else {
123                 if (pr & _PAGE_USER)
124                         pt_dump_cont_printf(m, dmsg, "USR ");
125                 else
126                         pt_dump_cont_printf(m, dmsg, "    ");
127                 if (pr & _PAGE_RW)
128                         pt_dump_cont_printf(m, dmsg, "RW ");
129                 else
130                         pt_dump_cont_printf(m, dmsg, "ro ");
131                 if (pr & _PAGE_PWT)
132                         pt_dump_cont_printf(m, dmsg, "PWT ");
133                 else
134                         pt_dump_cont_printf(m, dmsg, "    ");
135                 if (pr & _PAGE_PCD)
136                         pt_dump_cont_printf(m, dmsg, "PCD ");
137                 else
138                         pt_dump_cont_printf(m, dmsg, "    ");
139
140                 /* Bit 9 has a different meaning on level 3 vs 4 */
141                 if (level <= 3) {
142                         if (pr & _PAGE_PSE)
143                                 pt_dump_cont_printf(m, dmsg, "PSE ");
144                         else
145                                 pt_dump_cont_printf(m, dmsg, "    ");
146                 } else {
147                         if (pr & _PAGE_PAT)
148                                 pt_dump_cont_printf(m, dmsg, "pat ");
149                         else
150                                 pt_dump_cont_printf(m, dmsg, "    ");
151                 }
152                 if (pr & _PAGE_GLOBAL)
153                         pt_dump_cont_printf(m, dmsg, "GLB ");
154                 else
155                         pt_dump_cont_printf(m, dmsg, "    ");
156                 if (pr & _PAGE_NX)
157                         pt_dump_cont_printf(m, dmsg, "NX ");
158                 else
159                         pt_dump_cont_printf(m, dmsg, "x  ");
160         }
161         pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]);
162 }
163
164 /*
165  * On 64 bits, sign-extend the 48 bit address to 64 bit
166  */
167 static unsigned long normalize_addr(unsigned long u)
168 {
169 #ifdef CONFIG_X86_64
170         return (signed long)(u << 16) >> 16;
171 #else
172         return u;
173 #endif
174 }
175
176 /*
177  * This function gets called on a break in a continuous series
178  * of PTE entries; the next one is different so we need to
179  * print what we collected so far.
180  */
181 static void note_page(struct seq_file *m, struct pg_state *st,
182                       pgprot_t new_prot, int level)
183 {
184         pgprotval_t prot, cur;
185         static const char units[] = "KMGTPE";
186
187         /*
188          * If we have a "break" in the series, we need to flush the state that
189          * we have now. "break" is either changing perms, levels or
190          * address space marker.
191          */
192         prot = pgprot_val(new_prot) & PTE_FLAGS_MASK;
193         cur = pgprot_val(st->current_prot) & PTE_FLAGS_MASK;
194
195         if (!st->level) {
196                 /* First entry */
197                 st->current_prot = new_prot;
198                 st->level = level;
199                 st->marker = address_markers;
200                 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
201                                    st->marker->name);
202         } else if (prot != cur || level != st->level ||
203                    st->current_address >= st->marker[1].start_address) {
204                 const char *unit = units;
205                 unsigned long delta;
206                 int width = sizeof(unsigned long) * 2;
207
208                 /*
209                  * Now print the actual finished series
210                  */
211                 pt_dump_seq_printf(m, st->to_dmesg,  "0x%0*lx-0x%0*lx   ",
212                                    width, st->start_address,
213                                    width, st->current_address);
214
215                 delta = (st->current_address - st->start_address) >> 10;
216                 while (!(delta & 1023) && unit[1]) {
217                         delta >>= 10;
218                         unit++;
219                 }
220                 pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ", delta, *unit);
221                 printk_prot(m, st->current_prot, st->level, st->to_dmesg);
222
223                 /*
224                  * We print markers for special areas of address space,
225                  * such as the start of vmalloc space etc.
226                  * This helps in the interpretation.
227                  */
228                 if (st->current_address >= st->marker[1].start_address) {
229                         st->marker++;
230                         pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
231                                            st->marker->name);
232                 }
233
234                 st->start_address = st->current_address;
235                 st->current_prot = new_prot;
236                 st->level = level;
237         }
238 }
239
240 static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
241                                                         unsigned long P)
242 {
243         int i;
244         pte_t *start;
245
246         start = (pte_t *) pmd_page_vaddr(addr);
247         for (i = 0; i < PTRS_PER_PTE; i++) {
248                 pgprot_t prot = pte_pgprot(*start);
249
250                 st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT);
251                 note_page(m, st, prot, 4);
252                 start++;
253         }
254 }
255
256 #if PTRS_PER_PMD > 1
257
258 static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
259                                                         unsigned long P)
260 {
261         int i;
262         pmd_t *start;
263
264         start = (pmd_t *) pud_page_vaddr(addr);
265         for (i = 0; i < PTRS_PER_PMD; i++) {
266                 st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
267                 if (!pmd_none(*start)) {
268                         pgprotval_t prot = pmd_val(*start) & PTE_FLAGS_MASK;
269
270                         if (pmd_large(*start) || !pmd_present(*start))
271                                 note_page(m, st, __pgprot(prot), 3);
272                         else
273                                 walk_pte_level(m, st, *start,
274                                                P + i * PMD_LEVEL_MULT);
275                 } else
276                         note_page(m, st, __pgprot(0), 3);
277                 start++;
278         }
279 }
280
281 #else
282 #define walk_pmd_level(m,s,a,p) walk_pte_level(m,s,__pmd(pud_val(a)),p)
283 #define pud_large(a) pmd_large(__pmd(pud_val(a)))
284 #define pud_none(a)  pmd_none(__pmd(pud_val(a)))
285 #endif
286
287 #if PTRS_PER_PUD > 1
288
289 static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
290                                                         unsigned long P)
291 {
292         int i;
293         pud_t *start;
294
295         start = (pud_t *) pgd_page_vaddr(addr);
296
297         for (i = 0; i < PTRS_PER_PUD; i++) {
298                 st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
299                 if (!pud_none(*start)) {
300                         pgprotval_t prot = pud_val(*start) & PTE_FLAGS_MASK;
301
302                         if (pud_large(*start) || !pud_present(*start))
303                                 note_page(m, st, __pgprot(prot), 2);
304                         else
305                                 walk_pmd_level(m, st, *start,
306                                                P + i * PUD_LEVEL_MULT);
307                 } else
308                         note_page(m, st, __pgprot(0), 2);
309
310                 start++;
311         }
312 }
313
314 #else
315 #define walk_pud_level(m,s,a,p) walk_pmd_level(m,s,__pud(pgd_val(a)),p)
316 #define pgd_large(a) pud_large(__pud(pgd_val(a)))
317 #define pgd_none(a)  pud_none(__pud(pgd_val(a)))
318 #endif
319
320 void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
321 {
322 #ifdef CONFIG_X86_64
323         pgd_t *start = (pgd_t *) &init_level4_pgt;
324 #else
325         pgd_t *start = swapper_pg_dir;
326 #endif
327         int i;
328         struct pg_state st = {};
329
330         if (pgd) {
331                 start = pgd;
332                 st.to_dmesg = true;
333         }
334
335         for (i = 0; i < PTRS_PER_PGD; i++) {
336                 st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
337                 if (!pgd_none(*start)) {
338                         pgprotval_t prot = pgd_val(*start) & PTE_FLAGS_MASK;
339
340                         if (pgd_large(*start) || !pgd_present(*start))
341                                 note_page(m, &st, __pgprot(prot), 1);
342                         else
343                                 walk_pud_level(m, &st, *start,
344                                                i * PGD_LEVEL_MULT);
345                 } else
346                         note_page(m, &st, __pgprot(0), 1);
347
348                 start++;
349         }
350
351         /* Flush out the last page */
352         st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT);
353         note_page(m, &st, __pgprot(0), 0);
354 }
355
356 static int ptdump_show(struct seq_file *m, void *v)
357 {
358         ptdump_walk_pgd_level(m, NULL);
359         return 0;
360 }
361
362 static int ptdump_open(struct inode *inode, struct file *filp)
363 {
364         return single_open(filp, ptdump_show, NULL);
365 }
366
367 static const struct file_operations ptdump_fops = {
368         .open           = ptdump_open,
369         .read           = seq_read,
370         .llseek         = seq_lseek,
371         .release        = single_release,
372 };
373
374 static int pt_dump_init(void)
375 {
376         struct dentry *pe;
377
378 #ifdef CONFIG_X86_32
379         /* Not a compile-time constant on x86-32 */
380         address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
381         address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
382 # ifdef CONFIG_HIGHMEM
383         address_markers[PKMAP_BASE_NR].start_address = PKMAP_BASE;
384 # endif
385         address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
386 #endif
387
388         pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
389                                  &ptdump_fops);
390         if (!pe)
391                 return -ENOMEM;
392
393         return 0;
394 }
395
396 __initcall(pt_dump_init);
397 MODULE_LICENSE("GPL");
398 MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
399 MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables");