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.
 
   5  * (C) Copyright 2008 Intel Corporation
 
   7  * Author: Arjan van de Ven <arjan@linux.intel.com>
 
   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
 
  15 #include <linux/debugfs.h>
 
  17 #include <linux/module.h>
 
  18 #include <linux/seq_file.h>
 
  20 #include <asm/pgtable.h>
 
  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.
 
  29         pgprot_t current_prot;
 
  30         unsigned long start_address;
 
  31         unsigned long current_address;
 
  32         const struct addr_marker *marker;
 
  36         unsigned long start_address;
 
  40 /* Address space markers hints */
 
  41 static struct addr_marker address_markers[] = {
 
  44         { 0x8000000000000000UL, "Kernel Space" },
 
  45         { PAGE_OFFSET,          "Low Kernel Mapping" },
 
  46         { VMALLOC_START,        "vmalloc() Area" },
 
  47         { VMEMMAP_START,        "Vmemmap" },
 
  48         { __START_KERNEL_map,   "High Kernel Mapping" },
 
  49         { MODULES_VADDR,        "Modules" },
 
  50         { MODULES_END,          "End Modules" },
 
  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" },
 
  58         { 0/*FIXADDR_START*/,   "Fixmap Area" },
 
  60         { -1, NULL }            /* End of list */
 
  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)
 
  70  * Print a readable form of a pgprot_t to the seq_file
 
  72 static void printk_prot(struct seq_file *m, pgprot_t prot, int level)
 
  74         pgprotval_t pr = pgprot_val(prot);
 
  75         static const char * const level_name[] =
 
  76                 { "cr3", "pgd", "pud", "pmd", "pte" };
 
  78         if (!pgprot_val(prot)) {
 
  83                         seq_printf(m, "USR ");
 
  91                         seq_printf(m, "PWT ");
 
  95                         seq_printf(m, "PCD ");
 
  99                 /* Bit 9 has a different meaning on level 3 vs 4 */
 
 102                                 seq_printf(m, "PSE ");
 
 107                                 seq_printf(m, "pat ");
 
 111                 if (pr & _PAGE_GLOBAL)
 
 112                         seq_printf(m, "GLB ");
 
 116                         seq_printf(m, "NX ");
 
 120         seq_printf(m, "%s\n", level_name[level]);
 
 124  * On 64 bits, sign-extend the 48 bit address to 64 bit
 
 126 static unsigned long normalize_addr(unsigned long u)
 
 129         return (signed long)(u << 16) >> 16;
 
 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.
 
 140 static void note_page(struct seq_file *m, struct pg_state *st,
 
 141                       pgprot_t new_prot, int level)
 
 143         pgprotval_t prot, cur;
 
 144         static const char units[] = "KMGTPE";
 
 147          * If we have a "break" in the series, we need to flush the state that
 
 148          * we have now. "break" is either changing perms, levels or
 
 149          * address space marker.
 
 151         prot = pgprot_val(new_prot) & PTE_FLAGS_MASK;
 
 152         cur = pgprot_val(st->current_prot) & PTE_FLAGS_MASK;
 
 156                 st->current_prot = new_prot;
 
 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;
 
 166                  * Now print the actual finished series
 
 168                 seq_printf(m, "0x%p-0x%p   ",
 
 169                            (void *)st->start_address,
 
 170                            (void *)st->current_address);
 
 172                 delta = (st->current_address - st->start_address) >> 10;
 
 173                 while (!(delta & 1023) && unit[1]) {
 
 177                 seq_printf(m, "%9lu%c ", delta, *unit);
 
 178                 printk_prot(m, st->current_prot, st->level);
 
 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.
 
 185                 if (st->current_address >= st->marker[1].start_address) {
 
 187                         seq_printf(m, "---[ %s ]---\n", st->marker->name);
 
 190                 st->start_address = st->current_address;
 
 191                 st->current_prot = new_prot;
 
 196 static void walk_pte_level(struct seq_file *m, struct pg_state *st, pmd_t addr,
 
 202         start = (pte_t *) pmd_page_vaddr(addr);
 
 203         for (i = 0; i < PTRS_PER_PTE; i++) {
 
 204                 pgprot_t prot = pte_pgprot(*start);
 
 206                 st->current_address = normalize_addr(P + i * PTE_LEVEL_MULT);
 
 207                 note_page(m, st, prot, 4);
 
 214 static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t addr,
 
 220         start = (pmd_t *) pud_page_vaddr(addr);
 
 221         for (i = 0; i < PTRS_PER_PMD; i++) {
 
 222                 st->current_address = normalize_addr(P + i * PMD_LEVEL_MULT);
 
 223                 if (!pmd_none(*start)) {
 
 224                         pgprotval_t prot = pmd_val(*start) & PTE_FLAGS_MASK;
 
 226                         if (pmd_large(*start) || !pmd_present(*start))
 
 227                                 note_page(m, st, __pgprot(prot), 3);
 
 229                                 walk_pte_level(m, st, *start,
 
 230                                                P + i * PMD_LEVEL_MULT);
 
 232                         note_page(m, st, __pgprot(0), 3);
 
 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)))
 
 245 static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
 
 251         start = (pud_t *) pgd_page_vaddr(addr);
 
 253         for (i = 0; i < PTRS_PER_PUD; i++) {
 
 254                 st->current_address = normalize_addr(P + i * PUD_LEVEL_MULT);
 
 255                 if (!pud_none(*start)) {
 
 256                         pgprotval_t prot = pud_val(*start) & PTE_FLAGS_MASK;
 
 258                         if (pud_large(*start) || !pud_present(*start))
 
 259                                 note_page(m, st, __pgprot(prot), 2);
 
 261                                 walk_pmd_level(m, st, *start,
 
 262                                                P + i * PUD_LEVEL_MULT);
 
 264                         note_page(m, st, __pgprot(0), 2);
 
 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)))
 
 276 static void walk_pgd_level(struct seq_file *m)
 
 279         pgd_t *start = (pgd_t *) &init_level4_pgt;
 
 281         pgd_t *start = swapper_pg_dir;
 
 286         memset(&st, 0, sizeof(st));
 
 288         for (i = 0; i < PTRS_PER_PGD; i++) {
 
 289                 st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
 
 290                 if (!pgd_none(*start)) {
 
 291                         pgprotval_t prot = pgd_val(*start) & PTE_FLAGS_MASK;
 
 293                         if (pgd_large(*start) || !pgd_present(*start))
 
 294                                 note_page(m, &st, __pgprot(prot), 1);
 
 296                                 walk_pud_level(m, &st, *start,
 
 299                         note_page(m, &st, __pgprot(0), 1);
 
 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);
 
 309 static int ptdump_show(struct seq_file *m, void *v)
 
 315 static int ptdump_open(struct inode *inode, struct file *filp)
 
 317         return single_open(filp, ptdump_show, NULL);
 
 320 static const struct file_operations ptdump_fops = {
 
 324         .release        = single_release,
 
 327 static int pt_dump_init(void)
 
 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;
 
 339         address_markers[4].start_address = FIXADDR_START;
 
 343         pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
 
 351 __initcall(pt_dump_init);
 
 352 MODULE_LICENSE("GPL");
 
 353 MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
 
 354 MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables");