Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2014, The Linux Foundation. All rights reserved.
0004  * Debug helper to dump the current kernel pagetables of the system
0005  * so that we can see what the various memory ranges are set to.
0006  *
0007  * Derived from x86 and arm implementation:
0008  * (C) Copyright 2008 Intel Corporation
0009  *
0010  * Author: Arjan van de Ven <arjan@linux.intel.com>
0011  */
0012 #include <linux/debugfs.h>
0013 #include <linux/errno.h>
0014 #include <linux/fs.h>
0015 #include <linux/io.h>
0016 #include <linux/init.h>
0017 #include <linux/mm.h>
0018 #include <linux/ptdump.h>
0019 #include <linux/sched.h>
0020 #include <linux/seq_file.h>
0021 
0022 #include <asm/fixmap.h>
0023 #include <asm/kasan.h>
0024 #include <asm/memory.h>
0025 #include <asm/pgtable-hwdef.h>
0026 #include <asm/ptdump.h>
0027 
0028 
0029 enum address_markers_idx {
0030     PAGE_OFFSET_NR = 0,
0031     PAGE_END_NR,
0032 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
0033     KASAN_START_NR,
0034 #endif
0035 };
0036 
0037 static struct addr_marker address_markers[] = {
0038     { PAGE_OFFSET,          "Linear Mapping start" },
0039     { 0 /* PAGE_END */,     "Linear Mapping end" },
0040 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
0041     { 0 /* KASAN_SHADOW_START */,   "Kasan shadow start" },
0042     { KASAN_SHADOW_END,     "Kasan shadow end" },
0043 #endif
0044     { MODULES_VADDR,        "Modules start" },
0045     { MODULES_END,          "Modules end" },
0046     { VMALLOC_START,        "vmalloc() area" },
0047     { VMALLOC_END,          "vmalloc() end" },
0048     { FIXADDR_START,        "Fixmap start" },
0049     { FIXADDR_TOP,          "Fixmap end" },
0050     { PCI_IO_START,         "PCI I/O start" },
0051     { PCI_IO_END,           "PCI I/O end" },
0052     { VMEMMAP_START,        "vmemmap start" },
0053     { VMEMMAP_START + VMEMMAP_SIZE, "vmemmap end" },
0054     { -1,               NULL },
0055 };
0056 
0057 #define pt_dump_seq_printf(m, fmt, args...) \
0058 ({                      \
0059     if (m)                  \
0060         seq_printf(m, fmt, ##args); \
0061 })
0062 
0063 #define pt_dump_seq_puts(m, fmt)    \
0064 ({                  \
0065     if (m)              \
0066         seq_printf(m, fmt); \
0067 })
0068 
0069 /*
0070  * The page dumper groups page table entries of the same type into a single
0071  * description. It uses pg_state to track the range information while
0072  * iterating over the pte entries. When the continuity is broken it then
0073  * dumps out a description of the range.
0074  */
0075 struct pg_state {
0076     struct ptdump_state ptdump;
0077     struct seq_file *seq;
0078     const struct addr_marker *marker;
0079     unsigned long start_address;
0080     int level;
0081     u64 current_prot;
0082     bool check_wx;
0083     unsigned long wx_pages;
0084     unsigned long uxn_pages;
0085 };
0086 
0087 struct prot_bits {
0088     u64     mask;
0089     u64     val;
0090     const char  *set;
0091     const char  *clear;
0092 };
0093 
0094 static const struct prot_bits pte_bits[] = {
0095     {
0096         .mask   = PTE_VALID,
0097         .val    = PTE_VALID,
0098         .set    = " ",
0099         .clear  = "F",
0100     }, {
0101         .mask   = PTE_USER,
0102         .val    = PTE_USER,
0103         .set    = "USR",
0104         .clear  = "   ",
0105     }, {
0106         .mask   = PTE_RDONLY,
0107         .val    = PTE_RDONLY,
0108         .set    = "ro",
0109         .clear  = "RW",
0110     }, {
0111         .mask   = PTE_PXN,
0112         .val    = PTE_PXN,
0113         .set    = "NX",
0114         .clear  = "x ",
0115     }, {
0116         .mask   = PTE_SHARED,
0117         .val    = PTE_SHARED,
0118         .set    = "SHD",
0119         .clear  = "   ",
0120     }, {
0121         .mask   = PTE_AF,
0122         .val    = PTE_AF,
0123         .set    = "AF",
0124         .clear  = "  ",
0125     }, {
0126         .mask   = PTE_NG,
0127         .val    = PTE_NG,
0128         .set    = "NG",
0129         .clear  = "  ",
0130     }, {
0131         .mask   = PTE_CONT,
0132         .val    = PTE_CONT,
0133         .set    = "CON",
0134         .clear  = "   ",
0135     }, {
0136         .mask   = PTE_TABLE_BIT,
0137         .val    = PTE_TABLE_BIT,
0138         .set    = "   ",
0139         .clear  = "BLK",
0140     }, {
0141         .mask   = PTE_UXN,
0142         .val    = PTE_UXN,
0143         .set    = "UXN",
0144         .clear  = "   ",
0145     }, {
0146         .mask   = PTE_GP,
0147         .val    = PTE_GP,
0148         .set    = "GP",
0149         .clear  = "  ",
0150     }, {
0151         .mask   = PTE_ATTRINDX_MASK,
0152         .val    = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
0153         .set    = "DEVICE/nGnRnE",
0154     }, {
0155         .mask   = PTE_ATTRINDX_MASK,
0156         .val    = PTE_ATTRINDX(MT_DEVICE_nGnRE),
0157         .set    = "DEVICE/nGnRE",
0158     }, {
0159         .mask   = PTE_ATTRINDX_MASK,
0160         .val    = PTE_ATTRINDX(MT_NORMAL_NC),
0161         .set    = "MEM/NORMAL-NC",
0162     }, {
0163         .mask   = PTE_ATTRINDX_MASK,
0164         .val    = PTE_ATTRINDX(MT_NORMAL),
0165         .set    = "MEM/NORMAL",
0166     }, {
0167         .mask   = PTE_ATTRINDX_MASK,
0168         .val    = PTE_ATTRINDX(MT_NORMAL_TAGGED),
0169         .set    = "MEM/NORMAL-TAGGED",
0170     }
0171 };
0172 
0173 struct pg_level {
0174     const struct prot_bits *bits;
0175     const char *name;
0176     size_t num;
0177     u64 mask;
0178 };
0179 
0180 static struct pg_level pg_level[] = {
0181     { /* pgd */
0182         .name   = "PGD",
0183         .bits   = pte_bits,
0184         .num    = ARRAY_SIZE(pte_bits),
0185     }, { /* p4d */
0186         .name   = "P4D",
0187         .bits   = pte_bits,
0188         .num    = ARRAY_SIZE(pte_bits),
0189     }, { /* pud */
0190         .name   = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
0191         .bits   = pte_bits,
0192         .num    = ARRAY_SIZE(pte_bits),
0193     }, { /* pmd */
0194         .name   = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
0195         .bits   = pte_bits,
0196         .num    = ARRAY_SIZE(pte_bits),
0197     }, { /* pte */
0198         .name   = "PTE",
0199         .bits   = pte_bits,
0200         .num    = ARRAY_SIZE(pte_bits),
0201     },
0202 };
0203 
0204 static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
0205             size_t num)
0206 {
0207     unsigned i;
0208 
0209     for (i = 0; i < num; i++, bits++) {
0210         const char *s;
0211 
0212         if ((st->current_prot & bits->mask) == bits->val)
0213             s = bits->set;
0214         else
0215             s = bits->clear;
0216 
0217         if (s)
0218             pt_dump_seq_printf(st->seq, " %s", s);
0219     }
0220 }
0221 
0222 static void note_prot_uxn(struct pg_state *st, unsigned long addr)
0223 {
0224     if (!st->check_wx)
0225         return;
0226 
0227     if ((st->current_prot & PTE_UXN) == PTE_UXN)
0228         return;
0229 
0230     WARN_ONCE(1, "arm64/mm: Found non-UXN mapping at address %p/%pS\n",
0231           (void *)st->start_address, (void *)st->start_address);
0232 
0233     st->uxn_pages += (addr - st->start_address) / PAGE_SIZE;
0234 }
0235 
0236 static void note_prot_wx(struct pg_state *st, unsigned long addr)
0237 {
0238     if (!st->check_wx)
0239         return;
0240     if ((st->current_prot & PTE_RDONLY) == PTE_RDONLY)
0241         return;
0242     if ((st->current_prot & PTE_PXN) == PTE_PXN)
0243         return;
0244 
0245     WARN_ONCE(1, "arm64/mm: Found insecure W+X mapping at address %p/%pS\n",
0246           (void *)st->start_address, (void *)st->start_address);
0247 
0248     st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
0249 }
0250 
0251 static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
0252               u64 val)
0253 {
0254     struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
0255     static const char units[] = "KMGTPE";
0256     u64 prot = 0;
0257 
0258     if (level >= 0)
0259         prot = val & pg_level[level].mask;
0260 
0261     if (st->level == -1) {
0262         st->level = level;
0263         st->current_prot = prot;
0264         st->start_address = addr;
0265         pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
0266     } else if (prot != st->current_prot || level != st->level ||
0267            addr >= st->marker[1].start_address) {
0268         const char *unit = units;
0269         unsigned long delta;
0270 
0271         if (st->current_prot) {
0272             note_prot_uxn(st, addr);
0273             note_prot_wx(st, addr);
0274         }
0275 
0276         pt_dump_seq_printf(st->seq, "0x%016lx-0x%016lx   ",
0277                    st->start_address, addr);
0278 
0279         delta = (addr - st->start_address) >> 10;
0280         while (!(delta & 1023) && unit[1]) {
0281             delta >>= 10;
0282             unit++;
0283         }
0284         pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
0285                    pg_level[st->level].name);
0286         if (st->current_prot && pg_level[st->level].bits)
0287             dump_prot(st, pg_level[st->level].bits,
0288                   pg_level[st->level].num);
0289         pt_dump_seq_puts(st->seq, "\n");
0290 
0291         if (addr >= st->marker[1].start_address) {
0292             st->marker++;
0293             pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
0294         }
0295 
0296         st->start_address = addr;
0297         st->current_prot = prot;
0298         st->level = level;
0299     }
0300 
0301     if (addr >= st->marker[1].start_address) {
0302         st->marker++;
0303         pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
0304     }
0305 
0306 }
0307 
0308 void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
0309 {
0310     unsigned long end = ~0UL;
0311     struct pg_state st;
0312 
0313     if (info->base_addr < TASK_SIZE_64)
0314         end = TASK_SIZE_64;
0315 
0316     st = (struct pg_state){
0317         .seq = s,
0318         .marker = info->markers,
0319         .level = -1,
0320         .ptdump = {
0321             .note_page = note_page,
0322             .range = (struct ptdump_range[]){
0323                 {info->base_addr, end},
0324                 {0, 0}
0325             }
0326         }
0327     };
0328 
0329     ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
0330 }
0331 
0332 static void __init ptdump_initialize(void)
0333 {
0334     unsigned i, j;
0335 
0336     for (i = 0; i < ARRAY_SIZE(pg_level); i++)
0337         if (pg_level[i].bits)
0338             for (j = 0; j < pg_level[i].num; j++)
0339                 pg_level[i].mask |= pg_level[i].bits[j].mask;
0340 }
0341 
0342 static struct ptdump_info kernel_ptdump_info = {
0343     .mm     = &init_mm,
0344     .markers    = address_markers,
0345     .base_addr  = PAGE_OFFSET,
0346 };
0347 
0348 void ptdump_check_wx(void)
0349 {
0350     struct pg_state st = {
0351         .seq = NULL,
0352         .marker = (struct addr_marker[]) {
0353             { 0, NULL},
0354             { -1, NULL},
0355         },
0356         .level = -1,
0357         .check_wx = true,
0358         .ptdump = {
0359             .note_page = note_page,
0360             .range = (struct ptdump_range[]) {
0361                 {PAGE_OFFSET, ~0UL},
0362                 {0, 0}
0363             }
0364         }
0365     };
0366 
0367     ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
0368 
0369     if (st.wx_pages || st.uxn_pages)
0370         pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
0371             st.wx_pages, st.uxn_pages);
0372     else
0373         pr_info("Checked W+X mappings: passed, no W+X pages found\n");
0374 }
0375 
0376 static int __init ptdump_init(void)
0377 {
0378     address_markers[PAGE_END_NR].start_address = PAGE_END;
0379 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
0380     address_markers[KASAN_START_NR].start_address = KASAN_SHADOW_START;
0381 #endif
0382     ptdump_initialize();
0383     ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
0384     return 0;
0385 }
0386 device_initcall(ptdump_init);