linux/arch/arm64/mm/dump.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
   3 * Debug helper to dump the current kernel pagetables of the system
   4 * so that we can see what the various memory ranges are set to.
   5 *
   6 * Derived from x86 and arm implementation:
   7 * (C) Copyright 2008 Intel Corporation
   8 *
   9 * Author: Arjan van de Ven <arjan@linux.intel.com>
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License
  13 * as published by the Free Software Foundation; version 2
  14 * of the License.
  15 */
  16#include <linux/debugfs.h>
  17#include <linux/errno.h>
  18#include <linux/fs.h>
  19#include <linux/io.h>
  20#include <linux/init.h>
  21#include <linux/mm.h>
  22#include <linux/sched.h>
  23#include <linux/seq_file.h>
  24
  25#include <asm/fixmap.h>
  26#include <asm/memory.h>
  27#include <asm/pgtable.h>
  28#include <asm/pgtable-hwdef.h>
  29
  30struct addr_marker {
  31        unsigned long start_address;
  32        const char *name;
  33};
  34
  35enum address_markers_idx {
  36        MODULES_START_NR = 0,
  37        MODULES_END_NR,
  38        VMALLOC_START_NR,
  39        VMALLOC_END_NR,
  40#ifdef CONFIG_SPARSEMEM_VMEMMAP
  41        VMEMMAP_START_NR,
  42        VMEMMAP_END_NR,
  43#endif
  44        FIXADDR_START_NR,
  45        FIXADDR_END_NR,
  46        PCI_START_NR,
  47        PCI_END_NR,
  48        KERNEL_SPACE_NR,
  49};
  50
  51static struct addr_marker address_markers[] = {
  52        { MODULES_VADDR,        "Modules start" },
  53        { MODULES_END,          "Modules end" },
  54        { VMALLOC_START,        "vmalloc() Area" },
  55        { VMALLOC_END,          "vmalloc() End" },
  56#ifdef CONFIG_SPARSEMEM_VMEMMAP
  57        { 0,                    "vmemmap start" },
  58        { 0,                    "vmemmap end" },
  59#endif
  60        { FIXADDR_START,        "Fixmap start" },
  61        { FIXADDR_TOP,          "Fixmap end" },
  62        { PCI_IO_START,         "PCI I/O start" },
  63        { PCI_IO_END,           "PCI I/O end" },
  64        { PAGE_OFFSET,          "Linear Mapping" },
  65        { -1,                   NULL },
  66};
  67
  68/*
  69 * The page dumper groups page table entries of the same type into a single
  70 * description. It uses pg_state to track the range information while
  71 * iterating over the pte entries. When the continuity is broken it then
  72 * dumps out a description of the range.
  73 */
  74struct pg_state {
  75        struct seq_file *seq;
  76        const struct addr_marker *marker;
  77        unsigned long start_address;
  78        unsigned level;
  79        u64 current_prot;
  80};
  81
  82struct prot_bits {
  83        u64             mask;
  84        u64             val;
  85        const char      *set;
  86        const char      *clear;
  87};
  88
  89static const struct prot_bits pte_bits[] = {
  90        {
  91                .mask   = PTE_VALID,
  92                .val    = PTE_VALID,
  93                .set    = " ",
  94                .clear  = "F",
  95        }, {
  96                .mask   = PTE_USER,
  97                .val    = PTE_USER,
  98                .set    = "USR",
  99                .clear  = "   ",
 100        }, {
 101                .mask   = PTE_RDONLY,
 102                .val    = PTE_RDONLY,
 103                .set    = "ro",
 104                .clear  = "RW",
 105        }, {
 106                .mask   = PTE_PXN,
 107                .val    = PTE_PXN,
 108                .set    = "NX",
 109                .clear  = "x ",
 110        }, {
 111                .mask   = PTE_SHARED,
 112                .val    = PTE_SHARED,
 113                .set    = "SHD",
 114                .clear  = "   ",
 115        }, {
 116                .mask   = PTE_AF,
 117                .val    = PTE_AF,
 118                .set    = "AF",
 119                .clear  = "  ",
 120        }, {
 121                .mask   = PTE_NG,
 122                .val    = PTE_NG,
 123                .set    = "NG",
 124                .clear  = "  ",
 125        }, {
 126                .mask   = PTE_CONT,
 127                .val    = PTE_CONT,
 128                .set    = "CON",
 129                .clear  = "   ",
 130        }, {
 131                .mask   = PTE_TABLE_BIT,
 132                .val    = PTE_TABLE_BIT,
 133                .set    = "   ",
 134                .clear  = "BLK",
 135        }, {
 136                .mask   = PTE_UXN,
 137                .val    = PTE_UXN,
 138                .set    = "UXN",
 139        }, {
 140                .mask   = PTE_ATTRINDX_MASK,
 141                .val    = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
 142                .set    = "DEVICE/nGnRnE",
 143        }, {
 144                .mask   = PTE_ATTRINDX_MASK,
 145                .val    = PTE_ATTRINDX(MT_DEVICE_nGnRE),
 146                .set    = "DEVICE/nGnRE",
 147        }, {
 148                .mask   = PTE_ATTRINDX_MASK,
 149                .val    = PTE_ATTRINDX(MT_DEVICE_GRE),
 150                .set    = "DEVICE/GRE",
 151        }, {
 152                .mask   = PTE_ATTRINDX_MASK,
 153                .val    = PTE_ATTRINDX(MT_NORMAL_NC),
 154                .set    = "MEM/NORMAL-NC",
 155        }, {
 156                .mask   = PTE_ATTRINDX_MASK,
 157                .val    = PTE_ATTRINDX(MT_NORMAL),
 158                .set    = "MEM/NORMAL",
 159        }
 160};
 161
 162struct pg_level {
 163        const struct prot_bits *bits;
 164        size_t num;
 165        u64 mask;
 166};
 167
 168static struct pg_level pg_level[] = {
 169        {
 170        }, { /* pgd */
 171                .bits   = pte_bits,
 172                .num    = ARRAY_SIZE(pte_bits),
 173        }, { /* pud */
 174                .bits   = pte_bits,
 175                .num    = ARRAY_SIZE(pte_bits),
 176        }, { /* pmd */
 177                .bits   = pte_bits,
 178                .num    = ARRAY_SIZE(pte_bits),
 179        }, { /* pte */
 180                .bits   = pte_bits,
 181                .num    = ARRAY_SIZE(pte_bits),
 182        },
 183};
 184
 185static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
 186                        size_t num)
 187{
 188        unsigned i;
 189
 190        for (i = 0; i < num; i++, bits++) {
 191                const char *s;
 192
 193                if ((st->current_prot & bits->mask) == bits->val)
 194                        s = bits->set;
 195                else
 196                        s = bits->clear;
 197
 198                if (s)
 199                        seq_printf(st->seq, " %s", s);
 200        }
 201}
 202
 203static void note_page(struct pg_state *st, unsigned long addr, unsigned level,
 204                                u64 val)
 205{
 206        static const char units[] = "KMGTPE";
 207        u64 prot = val & pg_level[level].mask;
 208
 209        if (!st->level) {
 210                st->level = level;
 211                st->current_prot = prot;
 212                st->start_address = addr;
 213                seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
 214        } else if (prot != st->current_prot || level != st->level ||
 215                   addr >= st->marker[1].start_address) {
 216                const char *unit = units;
 217                unsigned long delta;
 218
 219                if (st->current_prot) {
 220                        seq_printf(st->seq, "0x%016lx-0x%016lx   ",
 221                                   st->start_address, addr);
 222
 223                        delta = (addr - st->start_address) >> 10;
 224                        while (!(delta & 1023) && unit[1]) {
 225                                delta >>= 10;
 226                                unit++;
 227                        }
 228                        seq_printf(st->seq, "%9lu%c", delta, *unit);
 229                        if (pg_level[st->level].bits)
 230                                dump_prot(st, pg_level[st->level].bits,
 231                                          pg_level[st->level].num);
 232                        seq_puts(st->seq, "\n");
 233                }
 234
 235                if (addr >= st->marker[1].start_address) {
 236                        st->marker++;
 237                        seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
 238                }
 239
 240                st->start_address = addr;
 241                st->current_prot = prot;
 242                st->level = level;
 243        }
 244
 245        if (addr >= st->marker[1].start_address) {
 246                st->marker++;
 247                seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
 248        }
 249
 250}
 251
 252static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
 253{
 254        pte_t *pte = pte_offset_kernel(pmd, 0);
 255        unsigned long addr;
 256        unsigned i;
 257
 258        for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
 259                addr = start + i * PAGE_SIZE;
 260                note_page(st, addr, 4, pte_val(*pte));
 261        }
 262}
 263
 264static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
 265{
 266        pmd_t *pmd = pmd_offset(pud, 0);
 267        unsigned long addr;
 268        unsigned i;
 269
 270        for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
 271                addr = start + i * PMD_SIZE;
 272                if (pmd_none(*pmd) || pmd_sect(*pmd)) {
 273                        note_page(st, addr, 3, pmd_val(*pmd));
 274                } else {
 275                        BUG_ON(pmd_bad(*pmd));
 276                        walk_pte(st, pmd, addr);
 277                }
 278        }
 279}
 280
 281static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
 282{
 283        pud_t *pud = pud_offset(pgd, 0);
 284        unsigned long addr;
 285        unsigned i;
 286
 287        for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
 288                addr = start + i * PUD_SIZE;
 289                if (pud_none(*pud) || pud_sect(*pud)) {
 290                        note_page(st, addr, 2, pud_val(*pud));
 291                } else {
 292                        BUG_ON(pud_bad(*pud));
 293                        walk_pmd(st, pud, addr);
 294                }
 295        }
 296}
 297
 298static void walk_pgd(struct pg_state *st, struct mm_struct *mm, unsigned long start)
 299{
 300        pgd_t *pgd = pgd_offset(mm, 0UL);
 301        unsigned i;
 302        unsigned long addr;
 303
 304        for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
 305                addr = start + i * PGDIR_SIZE;
 306                if (pgd_none(*pgd)) {
 307                        note_page(st, addr, 1, pgd_val(*pgd));
 308                } else {
 309                        BUG_ON(pgd_bad(*pgd));
 310                        walk_pud(st, pgd, addr);
 311                }
 312        }
 313}
 314
 315static int ptdump_show(struct seq_file *m, void *v)
 316{
 317        struct pg_state st = {
 318                .seq = m,
 319                .marker = address_markers,
 320        };
 321
 322        walk_pgd(&st, &init_mm, VA_START);
 323
 324        note_page(&st, 0, 0, 0);
 325        return 0;
 326}
 327
 328static int ptdump_open(struct inode *inode, struct file *file)
 329{
 330        return single_open(file, ptdump_show, NULL);
 331}
 332
 333static const struct file_operations ptdump_fops = {
 334        .open           = ptdump_open,
 335        .read           = seq_read,
 336        .llseek         = seq_lseek,
 337        .release        = single_release,
 338};
 339
 340static int ptdump_init(void)
 341{
 342        struct dentry *pe;
 343        unsigned i, j;
 344
 345        for (i = 0; i < ARRAY_SIZE(pg_level); i++)
 346                if (pg_level[i].bits)
 347                        for (j = 0; j < pg_level[i].num; j++)
 348                                pg_level[i].mask |= pg_level[i].bits[j].mask;
 349
 350#ifdef CONFIG_SPARSEMEM_VMEMMAP
 351        address_markers[VMEMMAP_START_NR].start_address =
 352                                (unsigned long)virt_to_page(PAGE_OFFSET);
 353        address_markers[VMEMMAP_END_NR].start_address =
 354                                (unsigned long)virt_to_page(high_memory);
 355#endif
 356
 357        pe = debugfs_create_file("kernel_page_tables", 0400, NULL, NULL,
 358                                 &ptdump_fops);
 359        return pe ? 0 : -ENOMEM;
 360}
 361device_initcall(ptdump_init);
 362