linux/arch/arm/mm/dump.c
<<
>>
Prefs
   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 * Derived from x86 implementation:
   6 * (C) Copyright 2008 Intel Corporation
   7 *
   8 * Author: Arjan van de Ven <arjan@linux.intel.com>
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License
  12 * as published by the Free Software Foundation; version 2
  13 * of the License.
  14 */
  15#include <linux/debugfs.h>
  16#include <linux/fs.h>
  17#include <linux/mm.h>
  18#include <linux/seq_file.h>
  19
  20#include <asm/domain.h>
  21#include <asm/fixmap.h>
  22#include <asm/memory.h>
  23#include <asm/pgtable.h>
  24
  25struct addr_marker {
  26        unsigned long start_address;
  27        const char *name;
  28};
  29
  30static struct addr_marker address_markers[] = {
  31        { MODULES_VADDR,        "Modules" },
  32        { PAGE_OFFSET,          "Kernel Mapping" },
  33        { 0,                    "vmalloc() Area" },
  34        { VMALLOC_END,          "vmalloc() End" },
  35        { FIXADDR_START,        "Fixmap Area" },
  36        { VECTORS_BASE, "Vectors" },
  37        { VECTORS_BASE + PAGE_SIZE * 2, "Vectors End" },
  38        { -1,                   NULL },
  39};
  40
  41struct pg_state {
  42        struct seq_file *seq;
  43        const struct addr_marker *marker;
  44        unsigned long start_address;
  45        unsigned level;
  46        u64 current_prot;
  47        const char *current_domain;
  48};
  49
  50struct prot_bits {
  51        u64             mask;
  52        u64             val;
  53        const char      *set;
  54        const char      *clear;
  55};
  56
  57static const struct prot_bits pte_bits[] = {
  58        {
  59                .mask   = L_PTE_USER,
  60                .val    = L_PTE_USER,
  61                .set    = "USR",
  62                .clear  = "   ",
  63        }, {
  64                .mask   = L_PTE_RDONLY,
  65                .val    = L_PTE_RDONLY,
  66                .set    = "ro",
  67                .clear  = "RW",
  68        }, {
  69                .mask   = L_PTE_XN,
  70                .val    = L_PTE_XN,
  71                .set    = "NX",
  72                .clear  = "x ",
  73        }, {
  74                .mask   = L_PTE_SHARED,
  75                .val    = L_PTE_SHARED,
  76                .set    = "SHD",
  77                .clear  = "   ",
  78        }, {
  79                .mask   = L_PTE_MT_MASK,
  80                .val    = L_PTE_MT_UNCACHED,
  81                .set    = "SO/UNCACHED",
  82        }, {
  83                .mask   = L_PTE_MT_MASK,
  84                .val    = L_PTE_MT_BUFFERABLE,
  85                .set    = "MEM/BUFFERABLE/WC",
  86        }, {
  87                .mask   = L_PTE_MT_MASK,
  88                .val    = L_PTE_MT_WRITETHROUGH,
  89                .set    = "MEM/CACHED/WT",
  90        }, {
  91                .mask   = L_PTE_MT_MASK,
  92                .val    = L_PTE_MT_WRITEBACK,
  93                .set    = "MEM/CACHED/WBRA",
  94#ifndef CONFIG_ARM_LPAE
  95        }, {
  96                .mask   = L_PTE_MT_MASK,
  97                .val    = L_PTE_MT_MINICACHE,
  98                .set    = "MEM/MINICACHE",
  99#endif
 100        }, {
 101                .mask   = L_PTE_MT_MASK,
 102                .val    = L_PTE_MT_WRITEALLOC,
 103                .set    = "MEM/CACHED/WBWA",
 104        }, {
 105                .mask   = L_PTE_MT_MASK,
 106                .val    = L_PTE_MT_DEV_SHARED,
 107                .set    = "DEV/SHARED",
 108#ifndef CONFIG_ARM_LPAE
 109        }, {
 110                .mask   = L_PTE_MT_MASK,
 111                .val    = L_PTE_MT_DEV_NONSHARED,
 112                .set    = "DEV/NONSHARED",
 113#endif
 114        }, {
 115                .mask   = L_PTE_MT_MASK,
 116                .val    = L_PTE_MT_DEV_WC,
 117                .set    = "DEV/WC",
 118        }, {
 119                .mask   = L_PTE_MT_MASK,
 120                .val    = L_PTE_MT_DEV_CACHED,
 121                .set    = "DEV/CACHED",
 122        },
 123};
 124
 125static const struct prot_bits section_bits[] = {
 126#ifdef CONFIG_ARM_LPAE
 127        {
 128                .mask   = PMD_SECT_USER,
 129                .val    = PMD_SECT_USER,
 130                .set    = "USR",
 131        }, {
 132                .mask   = L_PMD_SECT_RDONLY,
 133                .val    = L_PMD_SECT_RDONLY,
 134                .set    = "ro",
 135                .clear  = "RW",
 136#elif __LINUX_ARM_ARCH__ >= 6
 137        {
 138                .mask   = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 139                .val    = PMD_SECT_APX | PMD_SECT_AP_WRITE,
 140                .set    = "    ro",
 141        }, {
 142                .mask   = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 143                .val    = PMD_SECT_AP_WRITE,
 144                .set    = "    RW",
 145        }, {
 146                .mask   = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 147                .val    = PMD_SECT_AP_READ,
 148                .set    = "USR ro",
 149        }, {
 150                .mask   = PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 151                .val    = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 152                .set    = "USR RW",
 153#else /* ARMv4/ARMv5  */
 154        /* These are approximate */
 155        {
 156                .mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 157                .val    = 0,
 158                .set    = "    ro",
 159        }, {
 160                .mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 161                .val    = PMD_SECT_AP_WRITE,
 162                .set    = "    RW",
 163        }, {
 164                .mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 165                .val    = PMD_SECT_AP_READ,
 166                .set    = "USR ro",
 167        }, {
 168                .mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 169                .val    = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
 170                .set    = "USR RW",
 171#endif
 172        }, {
 173                .mask   = PMD_SECT_XN,
 174                .val    = PMD_SECT_XN,
 175                .set    = "NX",
 176                .clear  = "x ",
 177        }, {
 178                .mask   = PMD_SECT_S,
 179                .val    = PMD_SECT_S,
 180                .set    = "SHD",
 181                .clear  = "   ",
 182        },
 183};
 184
 185struct pg_level {
 186        const struct prot_bits *bits;
 187        size_t num;
 188        u64 mask;
 189};
 190
 191static struct pg_level pg_level[] = {
 192        {
 193        }, { /* pgd */
 194        }, { /* pud */
 195        }, { /* pmd */
 196                .bits   = section_bits,
 197                .num    = ARRAY_SIZE(section_bits),
 198        }, { /* pte */
 199                .bits   = pte_bits,
 200                .num    = ARRAY_SIZE(pte_bits),
 201        },
 202};
 203
 204static void dump_prot(struct pg_state *st, const struct prot_bits *bits, size_t num)
 205{
 206        unsigned i;
 207
 208        for (i = 0; i < num; i++, bits++) {
 209                const char *s;
 210
 211                if ((st->current_prot & bits->mask) == bits->val)
 212                        s = bits->set;
 213                else
 214                        s = bits->clear;
 215
 216                if (s)
 217                        seq_printf(st->seq, " %s", s);
 218        }
 219}
 220
 221static void note_page(struct pg_state *st, unsigned long addr,
 222                      unsigned int level, u64 val, const char *domain)
 223{
 224        static const char units[] = "KMGTPE";
 225        u64 prot = val & pg_level[level].mask;
 226
 227        if (!st->level) {
 228                st->level = level;
 229                st->current_prot = prot;
 230                st->current_domain = domain;
 231                seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
 232        } else if (prot != st->current_prot || level != st->level ||
 233                   domain != st->current_domain ||
 234                   addr >= st->marker[1].start_address) {
 235                const char *unit = units;
 236                unsigned long delta;
 237
 238                if (st->current_prot) {
 239                        seq_printf(st->seq, "0x%08lx-0x%08lx   ",
 240                                   st->start_address, addr);
 241
 242                        delta = (addr - st->start_address) >> 10;
 243                        while (!(delta & 1023) && unit[1]) {
 244                                delta >>= 10;
 245                                unit++;
 246                        }
 247                        seq_printf(st->seq, "%9lu%c", delta, *unit);
 248                        if (st->current_domain)
 249                                seq_printf(st->seq, " %s", st->current_domain);
 250                        if (pg_level[st->level].bits)
 251                                dump_prot(st, pg_level[st->level].bits, pg_level[st->level].num);
 252                        seq_printf(st->seq, "\n");
 253                }
 254
 255                if (addr >= st->marker[1].start_address) {
 256                        st->marker++;
 257                        seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
 258                }
 259                st->start_address = addr;
 260                st->current_prot = prot;
 261                st->current_domain = domain;
 262                st->level = level;
 263        }
 264}
 265
 266static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start,
 267                     const char *domain)
 268{
 269        pte_t *pte = pte_offset_kernel(pmd, 0);
 270        unsigned long addr;
 271        unsigned i;
 272
 273        for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
 274                addr = start + i * PAGE_SIZE;
 275                note_page(st, addr, 4, pte_val(*pte), domain);
 276        }
 277}
 278
 279static const char *get_domain_name(pmd_t *pmd)
 280{
 281#ifndef CONFIG_ARM_LPAE
 282        switch (pmd_val(*pmd) & PMD_DOMAIN_MASK) {
 283        case PMD_DOMAIN(DOMAIN_KERNEL):
 284                return "KERNEL ";
 285        case PMD_DOMAIN(DOMAIN_USER):
 286                return "USER   ";
 287        case PMD_DOMAIN(DOMAIN_IO):
 288                return "IO     ";
 289        case PMD_DOMAIN(DOMAIN_VECTORS):
 290                return "VECTORS";
 291        default:
 292                return "unknown";
 293        }
 294#endif
 295        return NULL;
 296}
 297
 298static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
 299{
 300        pmd_t *pmd = pmd_offset(pud, 0);
 301        unsigned long addr;
 302        unsigned i;
 303        const char *domain;
 304
 305        for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
 306                addr = start + i * PMD_SIZE;
 307                domain = get_domain_name(pmd);
 308                if (pmd_none(*pmd) || pmd_large(*pmd) || !pmd_present(*pmd))
 309                        note_page(st, addr, 3, pmd_val(*pmd), domain);
 310                else
 311                        walk_pte(st, pmd, addr, domain);
 312
 313                if (SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])) {
 314                        addr += SECTION_SIZE;
 315                        pmd++;
 316                        domain = get_domain_name(pmd);
 317                        note_page(st, addr, 3, pmd_val(*pmd), domain);
 318                }
 319        }
 320}
 321
 322static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
 323{
 324        pud_t *pud = pud_offset(pgd, 0);
 325        unsigned long addr;
 326        unsigned i;
 327
 328        for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
 329                addr = start + i * PUD_SIZE;
 330                if (!pud_none(*pud)) {
 331                        walk_pmd(st, pud, addr);
 332                } else {
 333                        note_page(st, addr, 2, pud_val(*pud), NULL);
 334                }
 335        }
 336}
 337
 338static void walk_pgd(struct seq_file *m)
 339{
 340        pgd_t *pgd = swapper_pg_dir;
 341        struct pg_state st;
 342        unsigned long addr;
 343        unsigned i;
 344
 345        memset(&st, 0, sizeof(st));
 346        st.seq = m;
 347        st.marker = address_markers;
 348
 349        for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
 350                addr = i * PGDIR_SIZE;
 351                if (!pgd_none(*pgd)) {
 352                        walk_pud(&st, pgd, addr);
 353                } else {
 354                        note_page(&st, addr, 1, pgd_val(*pgd), NULL);
 355                }
 356        }
 357
 358        note_page(&st, 0, 0, 0, NULL);
 359}
 360
 361static int ptdump_show(struct seq_file *m, void *v)
 362{
 363        walk_pgd(m);
 364        return 0;
 365}
 366
 367static int ptdump_open(struct inode *inode, struct file *file)
 368{
 369        return single_open(file, ptdump_show, NULL);
 370}
 371
 372static const struct file_operations ptdump_fops = {
 373        .open           = ptdump_open,
 374        .read           = seq_read,
 375        .llseek         = seq_lseek,
 376        .release        = single_release,
 377};
 378
 379static int ptdump_init(void)
 380{
 381        struct dentry *pe;
 382        unsigned i, j;
 383
 384        for (i = 0; i < ARRAY_SIZE(pg_level); i++)
 385                if (pg_level[i].bits)
 386                        for (j = 0; j < pg_level[i].num; j++)
 387                                pg_level[i].mask |= pg_level[i].bits[j].mask;
 388
 389        address_markers[2].start_address = VMALLOC_START;
 390
 391        pe = debugfs_create_file("kernel_page_tables", 0400, NULL, NULL,
 392                                 &ptdump_fops);
 393        return pe ? 0 : -ENOMEM;
 394}
 395__initcall(ptdump_init);
 396