162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Debug helper to dump the current kernel pagetables of the system 462306a36Sopenharmony_ci * so that we can see what the various memory ranges are set to. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) Copyright 2008 Intel Corporation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Arjan van de Ven <arjan@linux.intel.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/debugfs.h> 1262306a36Sopenharmony_ci#include <linux/kasan.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/seq_file.h> 1762306a36Sopenharmony_ci#include <linux/highmem.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/ptdump.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/e820/types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * The dumper groups pagetable entries of the same type into one, and for 2562306a36Sopenharmony_ci * that it needs to keep some state when walking, and flush this state 2662306a36Sopenharmony_ci * when a "break" in the continuity is found. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistruct pg_state { 2962306a36Sopenharmony_ci struct ptdump_state ptdump; 3062306a36Sopenharmony_ci int level; 3162306a36Sopenharmony_ci pgprotval_t current_prot; 3262306a36Sopenharmony_ci pgprotval_t effective_prot; 3362306a36Sopenharmony_ci pgprotval_t prot_levels[5]; 3462306a36Sopenharmony_ci unsigned long start_address; 3562306a36Sopenharmony_ci const struct addr_marker *marker; 3662306a36Sopenharmony_ci unsigned long lines; 3762306a36Sopenharmony_ci bool to_dmesg; 3862306a36Sopenharmony_ci bool check_wx; 3962306a36Sopenharmony_ci unsigned long wx_pages; 4062306a36Sopenharmony_ci struct seq_file *seq; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct addr_marker { 4462306a36Sopenharmony_ci unsigned long start_address; 4562306a36Sopenharmony_ci const char *name; 4662306a36Sopenharmony_ci unsigned long max_lines; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Address space markers hints */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#ifdef CONFIG_X86_64 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cienum address_markers_idx { 5462306a36Sopenharmony_ci USER_SPACE_NR = 0, 5562306a36Sopenharmony_ci KERNEL_SPACE_NR, 5662306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL 5762306a36Sopenharmony_ci LDT_NR, 5862306a36Sopenharmony_ci#endif 5962306a36Sopenharmony_ci LOW_KERNEL_NR, 6062306a36Sopenharmony_ci VMALLOC_START_NR, 6162306a36Sopenharmony_ci VMEMMAP_START_NR, 6262306a36Sopenharmony_ci#ifdef CONFIG_KASAN 6362306a36Sopenharmony_ci KASAN_SHADOW_START_NR, 6462306a36Sopenharmony_ci KASAN_SHADOW_END_NR, 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci CPU_ENTRY_AREA_NR, 6762306a36Sopenharmony_ci#ifdef CONFIG_X86_ESPFIX64 6862306a36Sopenharmony_ci ESPFIX_START_NR, 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci#ifdef CONFIG_EFI 7162306a36Sopenharmony_ci EFI_END_NR, 7262306a36Sopenharmony_ci#endif 7362306a36Sopenharmony_ci HIGH_KERNEL_NR, 7462306a36Sopenharmony_ci MODULES_VADDR_NR, 7562306a36Sopenharmony_ci MODULES_END_NR, 7662306a36Sopenharmony_ci FIXADDR_START_NR, 7762306a36Sopenharmony_ci END_OF_SPACE_NR, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic struct addr_marker address_markers[] = { 8162306a36Sopenharmony_ci [USER_SPACE_NR] = { 0, "User Space" }, 8262306a36Sopenharmony_ci [KERNEL_SPACE_NR] = { (1UL << 63), "Kernel Space" }, 8362306a36Sopenharmony_ci [LOW_KERNEL_NR] = { 0UL, "Low Kernel Mapping" }, 8462306a36Sopenharmony_ci [VMALLOC_START_NR] = { 0UL, "vmalloc() Area" }, 8562306a36Sopenharmony_ci [VMEMMAP_START_NR] = { 0UL, "Vmemmap" }, 8662306a36Sopenharmony_ci#ifdef CONFIG_KASAN 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * These fields get initialized with the (dynamic) 8962306a36Sopenharmony_ci * KASAN_SHADOW_{START,END} values in pt_dump_init(). 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci [KASAN_SHADOW_START_NR] = { 0UL, "KASAN shadow" }, 9262306a36Sopenharmony_ci [KASAN_SHADOW_END_NR] = { 0UL, "KASAN shadow end" }, 9362306a36Sopenharmony_ci#endif 9462306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL 9562306a36Sopenharmony_ci [LDT_NR] = { 0UL, "LDT remap" }, 9662306a36Sopenharmony_ci#endif 9762306a36Sopenharmony_ci [CPU_ENTRY_AREA_NR] = { CPU_ENTRY_AREA_BASE,"CPU entry Area" }, 9862306a36Sopenharmony_ci#ifdef CONFIG_X86_ESPFIX64 9962306a36Sopenharmony_ci [ESPFIX_START_NR] = { ESPFIX_BASE_ADDR, "ESPfix Area", 16 }, 10062306a36Sopenharmony_ci#endif 10162306a36Sopenharmony_ci#ifdef CONFIG_EFI 10262306a36Sopenharmony_ci [EFI_END_NR] = { EFI_VA_END, "EFI Runtime Services" }, 10362306a36Sopenharmony_ci#endif 10462306a36Sopenharmony_ci [HIGH_KERNEL_NR] = { __START_KERNEL_map, "High Kernel Mapping" }, 10562306a36Sopenharmony_ci [MODULES_VADDR_NR] = { MODULES_VADDR, "Modules" }, 10662306a36Sopenharmony_ci [MODULES_END_NR] = { MODULES_END, "End Modules" }, 10762306a36Sopenharmony_ci [FIXADDR_START_NR] = { FIXADDR_START, "Fixmap Area" }, 10862306a36Sopenharmony_ci [END_OF_SPACE_NR] = { -1, NULL } 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define INIT_PGD ((pgd_t *) &init_top_pgt) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#else /* CONFIG_X86_64 */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cienum address_markers_idx { 11662306a36Sopenharmony_ci USER_SPACE_NR = 0, 11762306a36Sopenharmony_ci KERNEL_SPACE_NR, 11862306a36Sopenharmony_ci VMALLOC_START_NR, 11962306a36Sopenharmony_ci VMALLOC_END_NR, 12062306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 12162306a36Sopenharmony_ci PKMAP_BASE_NR, 12262306a36Sopenharmony_ci#endif 12362306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL 12462306a36Sopenharmony_ci LDT_NR, 12562306a36Sopenharmony_ci#endif 12662306a36Sopenharmony_ci CPU_ENTRY_AREA_NR, 12762306a36Sopenharmony_ci FIXADDR_START_NR, 12862306a36Sopenharmony_ci END_OF_SPACE_NR, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct addr_marker address_markers[] = { 13262306a36Sopenharmony_ci [USER_SPACE_NR] = { 0, "User Space" }, 13362306a36Sopenharmony_ci [KERNEL_SPACE_NR] = { PAGE_OFFSET, "Kernel Mapping" }, 13462306a36Sopenharmony_ci [VMALLOC_START_NR] = { 0UL, "vmalloc() Area" }, 13562306a36Sopenharmony_ci [VMALLOC_END_NR] = { 0UL, "vmalloc() End" }, 13662306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 13762306a36Sopenharmony_ci [PKMAP_BASE_NR] = { 0UL, "Persistent kmap() Area" }, 13862306a36Sopenharmony_ci#endif 13962306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL 14062306a36Sopenharmony_ci [LDT_NR] = { 0UL, "LDT remap" }, 14162306a36Sopenharmony_ci#endif 14262306a36Sopenharmony_ci [CPU_ENTRY_AREA_NR] = { 0UL, "CPU entry area" }, 14362306a36Sopenharmony_ci [FIXADDR_START_NR] = { 0UL, "Fixmap area" }, 14462306a36Sopenharmony_ci [END_OF_SPACE_NR] = { -1, NULL } 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define INIT_PGD (swapper_pg_dir) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#endif /* !CONFIG_X86_64 */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Multipliers for offsets within the PTEs */ 15262306a36Sopenharmony_ci#define PTE_LEVEL_MULT (PAGE_SIZE) 15362306a36Sopenharmony_ci#define PMD_LEVEL_MULT (PTRS_PER_PTE * PTE_LEVEL_MULT) 15462306a36Sopenharmony_ci#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT) 15562306a36Sopenharmony_ci#define P4D_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT) 15662306a36Sopenharmony_ci#define PGD_LEVEL_MULT (PTRS_PER_P4D * P4D_LEVEL_MULT) 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define pt_dump_seq_printf(m, to_dmesg, fmt, args...) \ 15962306a36Sopenharmony_ci({ \ 16062306a36Sopenharmony_ci if (to_dmesg) \ 16162306a36Sopenharmony_ci printk(KERN_INFO fmt, ##args); \ 16262306a36Sopenharmony_ci else \ 16362306a36Sopenharmony_ci if (m) \ 16462306a36Sopenharmony_ci seq_printf(m, fmt, ##args); \ 16562306a36Sopenharmony_ci}) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define pt_dump_cont_printf(m, to_dmesg, fmt, args...) \ 16862306a36Sopenharmony_ci({ \ 16962306a36Sopenharmony_ci if (to_dmesg) \ 17062306a36Sopenharmony_ci printk(KERN_CONT fmt, ##args); \ 17162306a36Sopenharmony_ci else \ 17262306a36Sopenharmony_ci if (m) \ 17362306a36Sopenharmony_ci seq_printf(m, fmt, ##args); \ 17462306a36Sopenharmony_ci}) 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * Print a readable form of a pgprot_t to the seq_file 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic void printk_prot(struct seq_file *m, pgprotval_t pr, int level, bool dmsg) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci static const char * const level_name[] = 18262306a36Sopenharmony_ci { "pgd", "p4d", "pud", "pmd", "pte" }; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!(pr & _PAGE_PRESENT)) { 18562306a36Sopenharmony_ci /* Not present */ 18662306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci if (pr & _PAGE_USER) 18962306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "USR "); 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 19262306a36Sopenharmony_ci if (pr & _PAGE_RW) 19362306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "RW "); 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "ro "); 19662306a36Sopenharmony_ci if (pr & _PAGE_PWT) 19762306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "PWT "); 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 20062306a36Sopenharmony_ci if (pr & _PAGE_PCD) 20162306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "PCD "); 20262306a36Sopenharmony_ci else 20362306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Bit 7 has a different meaning on level 3 vs 4 */ 20662306a36Sopenharmony_ci if (level <= 3 && pr & _PAGE_PSE) 20762306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "PSE "); 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 21062306a36Sopenharmony_ci if ((level == 4 && pr & _PAGE_PAT) || 21162306a36Sopenharmony_ci ((level == 3 || level == 2) && pr & _PAGE_PAT_LARGE)) 21262306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "PAT "); 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 21562306a36Sopenharmony_ci if (pr & _PAGE_GLOBAL) 21662306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "GLB "); 21762306a36Sopenharmony_ci else 21862306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, " "); 21962306a36Sopenharmony_ci if (pr & _PAGE_NX) 22062306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "NX "); 22162306a36Sopenharmony_ci else 22262306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "x "); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void note_wx(struct pg_state *st, unsigned long addr) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci unsigned long npages; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci npages = (addr - st->start_address) / PAGE_SIZE; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#ifdef CONFIG_PCI_BIOS 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * If PCI BIOS is enabled, the PCI BIOS area is forced to WX. 23662306a36Sopenharmony_ci * Inform about it, but avoid the warning. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (pcibios_enabled && st->start_address >= PAGE_OFFSET + BIOS_BEGIN && 23962306a36Sopenharmony_ci addr <= PAGE_OFFSET + BIOS_END) { 24062306a36Sopenharmony_ci pr_warn_once("x86/mm: PCI BIOS W+X mapping %lu pages\n", npages); 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci#endif 24462306a36Sopenharmony_ci /* Account the WX pages */ 24562306a36Sopenharmony_ci st->wx_pages += npages; 24662306a36Sopenharmony_ci WARN_ONCE(__supported_pte_mask & _PAGE_NX, 24762306a36Sopenharmony_ci "x86/mm: Found insecure W+X mapping at address %pS\n", 24862306a36Sopenharmony_ci (void *)st->start_address); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void effective_prot(struct ptdump_state *pt_st, int level, u64 val) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct pg_state *st = container_of(pt_st, struct pg_state, ptdump); 25462306a36Sopenharmony_ci pgprotval_t prot = val & PTE_FLAGS_MASK; 25562306a36Sopenharmony_ci pgprotval_t effective; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (level > 0) { 25862306a36Sopenharmony_ci pgprotval_t higher_prot = st->prot_levels[level - 1]; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci effective = (higher_prot & prot & (_PAGE_USER | _PAGE_RW)) | 26162306a36Sopenharmony_ci ((higher_prot | prot) & _PAGE_NX); 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci effective = prot; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci st->prot_levels[level] = effective; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * This function gets called on a break in a continuous series 27162306a36Sopenharmony_ci * of PTE entries; the next one is different so we need to 27262306a36Sopenharmony_ci * print what we collected so far. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic void note_page(struct ptdump_state *pt_st, unsigned long addr, int level, 27562306a36Sopenharmony_ci u64 val) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct pg_state *st = container_of(pt_st, struct pg_state, ptdump); 27862306a36Sopenharmony_ci pgprotval_t new_prot, new_eff; 27962306a36Sopenharmony_ci pgprotval_t cur, eff; 28062306a36Sopenharmony_ci static const char units[] = "BKMGTPE"; 28162306a36Sopenharmony_ci struct seq_file *m = st->seq; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci new_prot = val & PTE_FLAGS_MASK; 28462306a36Sopenharmony_ci if (!val) 28562306a36Sopenharmony_ci new_eff = 0; 28662306a36Sopenharmony_ci else 28762306a36Sopenharmony_ci new_eff = st->prot_levels[level]; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * If we have a "break" in the series, we need to flush the state that 29162306a36Sopenharmony_ci * we have now. "break" is either changing perms, levels or 29262306a36Sopenharmony_ci * address space marker. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci cur = st->current_prot; 29562306a36Sopenharmony_ci eff = st->effective_prot; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (st->level == -1) { 29862306a36Sopenharmony_ci /* First entry */ 29962306a36Sopenharmony_ci st->current_prot = new_prot; 30062306a36Sopenharmony_ci st->effective_prot = new_eff; 30162306a36Sopenharmony_ci st->level = level; 30262306a36Sopenharmony_ci st->marker = address_markers; 30362306a36Sopenharmony_ci st->lines = 0; 30462306a36Sopenharmony_ci pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n", 30562306a36Sopenharmony_ci st->marker->name); 30662306a36Sopenharmony_ci } else if (new_prot != cur || new_eff != eff || level != st->level || 30762306a36Sopenharmony_ci addr >= st->marker[1].start_address) { 30862306a36Sopenharmony_ci const char *unit = units; 30962306a36Sopenharmony_ci unsigned long delta; 31062306a36Sopenharmony_ci int width = sizeof(unsigned long) * 2; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (st->check_wx && (eff & _PAGE_RW) && !(eff & _PAGE_NX)) 31362306a36Sopenharmony_ci note_wx(st, addr); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * Now print the actual finished series 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci if (!st->marker->max_lines || 31962306a36Sopenharmony_ci st->lines < st->marker->max_lines) { 32062306a36Sopenharmony_ci pt_dump_seq_printf(m, st->to_dmesg, 32162306a36Sopenharmony_ci "0x%0*lx-0x%0*lx ", 32262306a36Sopenharmony_ci width, st->start_address, 32362306a36Sopenharmony_ci width, addr); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci delta = addr - st->start_address; 32662306a36Sopenharmony_ci while (!(delta & 1023) && unit[1]) { 32762306a36Sopenharmony_ci delta >>= 10; 32862306a36Sopenharmony_ci unit++; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ", 33162306a36Sopenharmony_ci delta, *unit); 33262306a36Sopenharmony_ci printk_prot(m, st->current_prot, st->level, 33362306a36Sopenharmony_ci st->to_dmesg); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci st->lines++; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* 33862306a36Sopenharmony_ci * We print markers for special areas of address space, 33962306a36Sopenharmony_ci * such as the start of vmalloc space etc. 34062306a36Sopenharmony_ci * This helps in the interpretation. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci if (addr >= st->marker[1].start_address) { 34362306a36Sopenharmony_ci if (st->marker->max_lines && 34462306a36Sopenharmony_ci st->lines > st->marker->max_lines) { 34562306a36Sopenharmony_ci unsigned long nskip = 34662306a36Sopenharmony_ci st->lines - st->marker->max_lines; 34762306a36Sopenharmony_ci pt_dump_seq_printf(m, st->to_dmesg, 34862306a36Sopenharmony_ci "... %lu entr%s skipped ... \n", 34962306a36Sopenharmony_ci nskip, 35062306a36Sopenharmony_ci nskip == 1 ? "y" : "ies"); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci st->marker++; 35362306a36Sopenharmony_ci st->lines = 0; 35462306a36Sopenharmony_ci pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n", 35562306a36Sopenharmony_ci st->marker->name); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci st->start_address = addr; 35962306a36Sopenharmony_ci st->current_prot = new_prot; 36062306a36Sopenharmony_ci st->effective_prot = new_eff; 36162306a36Sopenharmony_ci st->level = level; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void ptdump_walk_pgd_level_core(struct seq_file *m, 36662306a36Sopenharmony_ci struct mm_struct *mm, pgd_t *pgd, 36762306a36Sopenharmony_ci bool checkwx, bool dmesg) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci const struct ptdump_range ptdump_ranges[] = { 37062306a36Sopenharmony_ci#ifdef CONFIG_X86_64 37162306a36Sopenharmony_ci {0, PTRS_PER_PGD * PGD_LEVEL_MULT / 2}, 37262306a36Sopenharmony_ci {GUARD_HOLE_END_ADDR, ~0UL}, 37362306a36Sopenharmony_ci#else 37462306a36Sopenharmony_ci {0, ~0UL}, 37562306a36Sopenharmony_ci#endif 37662306a36Sopenharmony_ci {0, 0} 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci struct pg_state st = { 38062306a36Sopenharmony_ci .ptdump = { 38162306a36Sopenharmony_ci .note_page = note_page, 38262306a36Sopenharmony_ci .effective_prot = effective_prot, 38362306a36Sopenharmony_ci .range = ptdump_ranges 38462306a36Sopenharmony_ci }, 38562306a36Sopenharmony_ci .level = -1, 38662306a36Sopenharmony_ci .to_dmesg = dmesg, 38762306a36Sopenharmony_ci .check_wx = checkwx, 38862306a36Sopenharmony_ci .seq = m 38962306a36Sopenharmony_ci }; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ptdump_walk_pgd(&st.ptdump, mm, pgd); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!checkwx) 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci if (st.wx_pages) 39662306a36Sopenharmony_ci pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n", 39762306a36Sopenharmony_ci st.wx_pages); 39862306a36Sopenharmony_ci else 39962306a36Sopenharmony_ci pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n"); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_civoid ptdump_walk_pgd_level(struct seq_file *m, struct mm_struct *mm) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci ptdump_walk_pgd_level_core(m, mm, mm->pgd, false, true); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_civoid ptdump_walk_pgd_level_debugfs(struct seq_file *m, struct mm_struct *mm, 40862306a36Sopenharmony_ci bool user) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci pgd_t *pgd = mm->pgd; 41162306a36Sopenharmony_ci#ifdef CONFIG_PAGE_TABLE_ISOLATION 41262306a36Sopenharmony_ci if (user && boot_cpu_has(X86_FEATURE_PTI)) 41362306a36Sopenharmony_ci pgd = kernel_to_user_pgdp(pgd); 41462306a36Sopenharmony_ci#endif 41562306a36Sopenharmony_ci ptdump_walk_pgd_level_core(m, mm, pgd, false, false); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptdump_walk_pgd_level_debugfs); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_civoid ptdump_walk_user_pgd_level_checkwx(void) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci#ifdef CONFIG_PAGE_TABLE_ISOLATION 42262306a36Sopenharmony_ci pgd_t *pgd = INIT_PGD; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!(__supported_pte_mask & _PAGE_NX) || 42562306a36Sopenharmony_ci !boot_cpu_has(X86_FEATURE_PTI)) 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci pr_info("x86/mm: Checking user space page tables\n"); 42962306a36Sopenharmony_ci pgd = kernel_to_user_pgdp(pgd); 43062306a36Sopenharmony_ci ptdump_walk_pgd_level_core(NULL, &init_mm, pgd, true, false); 43162306a36Sopenharmony_ci#endif 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_civoid ptdump_walk_pgd_level_checkwx(void) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci ptdump_walk_pgd_level_core(NULL, &init_mm, INIT_PGD, true, false); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int __init pt_dump_init(void) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * Various markers are not compile-time constants, so assign them 44362306a36Sopenharmony_ci * here. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci#ifdef CONFIG_X86_64 44662306a36Sopenharmony_ci address_markers[LOW_KERNEL_NR].start_address = PAGE_OFFSET; 44762306a36Sopenharmony_ci address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; 44862306a36Sopenharmony_ci address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START; 44962306a36Sopenharmony_ci#ifdef CONFIG_MODIFY_LDT_SYSCALL 45062306a36Sopenharmony_ci address_markers[LDT_NR].start_address = LDT_BASE_ADDR; 45162306a36Sopenharmony_ci#endif 45262306a36Sopenharmony_ci#ifdef CONFIG_KASAN 45362306a36Sopenharmony_ci address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START; 45462306a36Sopenharmony_ci address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END; 45562306a36Sopenharmony_ci#endif 45662306a36Sopenharmony_ci#endif 45762306a36Sopenharmony_ci#ifdef CONFIG_X86_32 45862306a36Sopenharmony_ci address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; 45962306a36Sopenharmony_ci address_markers[VMALLOC_END_NR].start_address = VMALLOC_END; 46062306a36Sopenharmony_ci# ifdef CONFIG_HIGHMEM 46162306a36Sopenharmony_ci address_markers[PKMAP_BASE_NR].start_address = PKMAP_BASE; 46262306a36Sopenharmony_ci# endif 46362306a36Sopenharmony_ci address_markers[FIXADDR_START_NR].start_address = FIXADDR_START; 46462306a36Sopenharmony_ci address_markers[CPU_ENTRY_AREA_NR].start_address = CPU_ENTRY_AREA_BASE; 46562306a36Sopenharmony_ci# ifdef CONFIG_MODIFY_LDT_SYSCALL 46662306a36Sopenharmony_ci address_markers[LDT_NR].start_address = LDT_BASE_ADDR; 46762306a36Sopenharmony_ci# endif 46862306a36Sopenharmony_ci#endif 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci__initcall(pt_dump_init); 472