162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2019 SiFive 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/efi.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/debugfs.h> 962306a36Sopenharmony_ci#include <linux/seq_file.h> 1062306a36Sopenharmony_ci#include <linux/ptdump.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/ptdump.h> 1362306a36Sopenharmony_ci#include <linux/pgtable.h> 1462306a36Sopenharmony_ci#include <asm/kasan.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define pt_dump_seq_printf(m, fmt, args...) \ 1762306a36Sopenharmony_ci({ \ 1862306a36Sopenharmony_ci if (m) \ 1962306a36Sopenharmony_ci seq_printf(m, fmt, ##args); \ 2062306a36Sopenharmony_ci}) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define pt_dump_seq_puts(m, fmt) \ 2362306a36Sopenharmony_ci({ \ 2462306a36Sopenharmony_ci if (m) \ 2562306a36Sopenharmony_ci seq_printf(m, fmt); \ 2662306a36Sopenharmony_ci}) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * The page dumper groups page table entries of the same type into a single 3062306a36Sopenharmony_ci * description. It uses pg_state to track the range information while 3162306a36Sopenharmony_ci * iterating over the pte entries. When the continuity is broken it then 3262306a36Sopenharmony_ci * dumps out a description of the range. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistruct pg_state { 3562306a36Sopenharmony_ci struct ptdump_state ptdump; 3662306a36Sopenharmony_ci struct seq_file *seq; 3762306a36Sopenharmony_ci const struct addr_marker *marker; 3862306a36Sopenharmony_ci unsigned long start_address; 3962306a36Sopenharmony_ci unsigned long start_pa; 4062306a36Sopenharmony_ci unsigned long last_pa; 4162306a36Sopenharmony_ci int level; 4262306a36Sopenharmony_ci u64 current_prot; 4362306a36Sopenharmony_ci bool check_wx; 4462306a36Sopenharmony_ci unsigned long wx_pages; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Address marker */ 4862306a36Sopenharmony_cistruct addr_marker { 4962306a36Sopenharmony_ci unsigned long start_address; 5062306a36Sopenharmony_ci const char *name; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Private information for debugfs */ 5462306a36Sopenharmony_cistruct ptd_mm_info { 5562306a36Sopenharmony_ci struct mm_struct *mm; 5662306a36Sopenharmony_ci const struct addr_marker *markers; 5762306a36Sopenharmony_ci unsigned long base_addr; 5862306a36Sopenharmony_ci unsigned long end; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cienum address_markers_idx { 6262306a36Sopenharmony_ci FIXMAP_START_NR, 6362306a36Sopenharmony_ci FIXMAP_END_NR, 6462306a36Sopenharmony_ci PCI_IO_START_NR, 6562306a36Sopenharmony_ci PCI_IO_END_NR, 6662306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 6762306a36Sopenharmony_ci VMEMMAP_START_NR, 6862306a36Sopenharmony_ci VMEMMAP_END_NR, 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci VMALLOC_START_NR, 7162306a36Sopenharmony_ci VMALLOC_END_NR, 7262306a36Sopenharmony_ci PAGE_OFFSET_NR, 7362306a36Sopenharmony_ci#ifdef CONFIG_KASAN 7462306a36Sopenharmony_ci KASAN_SHADOW_START_NR, 7562306a36Sopenharmony_ci KASAN_SHADOW_END_NR, 7662306a36Sopenharmony_ci#endif 7762306a36Sopenharmony_ci#ifdef CONFIG_64BIT 7862306a36Sopenharmony_ci MODULES_MAPPING_NR, 7962306a36Sopenharmony_ci KERNEL_MAPPING_NR, 8062306a36Sopenharmony_ci#endif 8162306a36Sopenharmony_ci END_OF_SPACE_NR 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct addr_marker address_markers[] = { 8562306a36Sopenharmony_ci {0, "Fixmap start"}, 8662306a36Sopenharmony_ci {0, "Fixmap end"}, 8762306a36Sopenharmony_ci {0, "PCI I/O start"}, 8862306a36Sopenharmony_ci {0, "PCI I/O end"}, 8962306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 9062306a36Sopenharmony_ci {0, "vmemmap start"}, 9162306a36Sopenharmony_ci {0, "vmemmap end"}, 9262306a36Sopenharmony_ci#endif 9362306a36Sopenharmony_ci {0, "vmalloc() area"}, 9462306a36Sopenharmony_ci {0, "vmalloc() end"}, 9562306a36Sopenharmony_ci {0, "Linear mapping"}, 9662306a36Sopenharmony_ci#ifdef CONFIG_KASAN 9762306a36Sopenharmony_ci {0, "Kasan shadow start"}, 9862306a36Sopenharmony_ci {0, "Kasan shadow end"}, 9962306a36Sopenharmony_ci#endif 10062306a36Sopenharmony_ci#ifdef CONFIG_64BIT 10162306a36Sopenharmony_ci {0, "Modules/BPF mapping"}, 10262306a36Sopenharmony_ci {0, "Kernel mapping"}, 10362306a36Sopenharmony_ci#endif 10462306a36Sopenharmony_ci {-1, NULL}, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct ptd_mm_info kernel_ptd_info = { 10862306a36Sopenharmony_ci .mm = &init_mm, 10962306a36Sopenharmony_ci .markers = address_markers, 11062306a36Sopenharmony_ci .base_addr = 0, 11162306a36Sopenharmony_ci .end = ULONG_MAX, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#ifdef CONFIG_EFI 11562306a36Sopenharmony_cistatic struct addr_marker efi_addr_markers[] = { 11662306a36Sopenharmony_ci { 0, "UEFI runtime start" }, 11762306a36Sopenharmony_ci { SZ_1G, "UEFI runtime end" }, 11862306a36Sopenharmony_ci { -1, NULL } 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct ptd_mm_info efi_ptd_info = { 12262306a36Sopenharmony_ci .mm = &efi_mm, 12362306a36Sopenharmony_ci .markers = efi_addr_markers, 12462306a36Sopenharmony_ci .base_addr = 0, 12562306a36Sopenharmony_ci .end = SZ_2G, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci#endif 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Page Table Entry */ 13062306a36Sopenharmony_cistruct prot_bits { 13162306a36Sopenharmony_ci u64 mask; 13262306a36Sopenharmony_ci u64 val; 13362306a36Sopenharmony_ci const char *set; 13462306a36Sopenharmony_ci const char *clear; 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const struct prot_bits pte_bits[] = { 13862306a36Sopenharmony_ci { 13962306a36Sopenharmony_ci .mask = _PAGE_SOFT, 14062306a36Sopenharmony_ci .val = _PAGE_SOFT, 14162306a36Sopenharmony_ci .set = "RSW", 14262306a36Sopenharmony_ci .clear = " ", 14362306a36Sopenharmony_ci }, { 14462306a36Sopenharmony_ci .mask = _PAGE_DIRTY, 14562306a36Sopenharmony_ci .val = _PAGE_DIRTY, 14662306a36Sopenharmony_ci .set = "D", 14762306a36Sopenharmony_ci .clear = ".", 14862306a36Sopenharmony_ci }, { 14962306a36Sopenharmony_ci .mask = _PAGE_ACCESSED, 15062306a36Sopenharmony_ci .val = _PAGE_ACCESSED, 15162306a36Sopenharmony_ci .set = "A", 15262306a36Sopenharmony_ci .clear = ".", 15362306a36Sopenharmony_ci }, { 15462306a36Sopenharmony_ci .mask = _PAGE_GLOBAL, 15562306a36Sopenharmony_ci .val = _PAGE_GLOBAL, 15662306a36Sopenharmony_ci .set = "G", 15762306a36Sopenharmony_ci .clear = ".", 15862306a36Sopenharmony_ci }, { 15962306a36Sopenharmony_ci .mask = _PAGE_USER, 16062306a36Sopenharmony_ci .val = _PAGE_USER, 16162306a36Sopenharmony_ci .set = "U", 16262306a36Sopenharmony_ci .clear = ".", 16362306a36Sopenharmony_ci }, { 16462306a36Sopenharmony_ci .mask = _PAGE_EXEC, 16562306a36Sopenharmony_ci .val = _PAGE_EXEC, 16662306a36Sopenharmony_ci .set = "X", 16762306a36Sopenharmony_ci .clear = ".", 16862306a36Sopenharmony_ci }, { 16962306a36Sopenharmony_ci .mask = _PAGE_WRITE, 17062306a36Sopenharmony_ci .val = _PAGE_WRITE, 17162306a36Sopenharmony_ci .set = "W", 17262306a36Sopenharmony_ci .clear = ".", 17362306a36Sopenharmony_ci }, { 17462306a36Sopenharmony_ci .mask = _PAGE_READ, 17562306a36Sopenharmony_ci .val = _PAGE_READ, 17662306a36Sopenharmony_ci .set = "R", 17762306a36Sopenharmony_ci .clear = ".", 17862306a36Sopenharmony_ci }, { 17962306a36Sopenharmony_ci .mask = _PAGE_PRESENT, 18062306a36Sopenharmony_ci .val = _PAGE_PRESENT, 18162306a36Sopenharmony_ci .set = "V", 18262306a36Sopenharmony_ci .clear = ".", 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* Page Level */ 18762306a36Sopenharmony_cistruct pg_level { 18862306a36Sopenharmony_ci const char *name; 18962306a36Sopenharmony_ci u64 mask; 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct pg_level pg_level[] = { 19362306a36Sopenharmony_ci { /* pgd */ 19462306a36Sopenharmony_ci .name = "PGD", 19562306a36Sopenharmony_ci }, { /* p4d */ 19662306a36Sopenharmony_ci .name = (CONFIG_PGTABLE_LEVELS > 4) ? "P4D" : "PGD", 19762306a36Sopenharmony_ci }, { /* pud */ 19862306a36Sopenharmony_ci .name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD", 19962306a36Sopenharmony_ci }, { /* pmd */ 20062306a36Sopenharmony_ci .name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD", 20162306a36Sopenharmony_ci }, { /* pte */ 20262306a36Sopenharmony_ci .name = "PTE", 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void dump_prot(struct pg_state *st) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci unsigned int i; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pte_bits); i++) { 21162306a36Sopenharmony_ci const char *s; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if ((st->current_prot & pte_bits[i].mask) == pte_bits[i].val) 21462306a36Sopenharmony_ci s = pte_bits[i].set; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci s = pte_bits[i].clear; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (s) 21962306a36Sopenharmony_ci pt_dump_seq_printf(st->seq, " %s", s); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci#ifdef CONFIG_64BIT 22462306a36Sopenharmony_ci#define ADDR_FORMAT "0x%016lx" 22562306a36Sopenharmony_ci#else 22662306a36Sopenharmony_ci#define ADDR_FORMAT "0x%08lx" 22762306a36Sopenharmony_ci#endif 22862306a36Sopenharmony_cistatic void dump_addr(struct pg_state *st, unsigned long addr) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci static const char units[] = "KMGTPE"; 23162306a36Sopenharmony_ci const char *unit = units; 23262306a36Sopenharmony_ci unsigned long delta; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci pt_dump_seq_printf(st->seq, ADDR_FORMAT "-" ADDR_FORMAT " ", 23562306a36Sopenharmony_ci st->start_address, addr); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pt_dump_seq_printf(st->seq, " " ADDR_FORMAT " ", st->start_pa); 23862306a36Sopenharmony_ci delta = (addr - st->start_address) >> 10; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci while (!(delta & 1023) && unit[1]) { 24162306a36Sopenharmony_ci delta >>= 10; 24262306a36Sopenharmony_ci unit++; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit, 24662306a36Sopenharmony_ci pg_level[st->level].name); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void note_prot_wx(struct pg_state *st, unsigned long addr) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (!st->check_wx) 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if ((st->current_prot & (_PAGE_WRITE | _PAGE_EXEC)) != 25562306a36Sopenharmony_ci (_PAGE_WRITE | _PAGE_EXEC)) 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci WARN_ONCE(1, "riscv/mm: Found insecure W+X mapping at address %p/%pS\n", 25962306a36Sopenharmony_ci (void *)st->start_address, (void *)st->start_address); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci st->wx_pages += (addr - st->start_address) / PAGE_SIZE; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void note_page(struct ptdump_state *pt_st, unsigned long addr, 26562306a36Sopenharmony_ci int level, u64 val) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct pg_state *st = container_of(pt_st, struct pg_state, ptdump); 26862306a36Sopenharmony_ci u64 pa = PFN_PHYS(pte_pfn(__pte(val))); 26962306a36Sopenharmony_ci u64 prot = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (level >= 0) 27262306a36Sopenharmony_ci prot = val & pg_level[level].mask; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (st->level == -1) { 27562306a36Sopenharmony_ci st->level = level; 27662306a36Sopenharmony_ci st->current_prot = prot; 27762306a36Sopenharmony_ci st->start_address = addr; 27862306a36Sopenharmony_ci st->start_pa = pa; 27962306a36Sopenharmony_ci st->last_pa = pa; 28062306a36Sopenharmony_ci pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); 28162306a36Sopenharmony_ci } else if (prot != st->current_prot || 28262306a36Sopenharmony_ci level != st->level || addr >= st->marker[1].start_address) { 28362306a36Sopenharmony_ci if (st->current_prot) { 28462306a36Sopenharmony_ci note_prot_wx(st, addr); 28562306a36Sopenharmony_ci dump_addr(st, addr); 28662306a36Sopenharmony_ci dump_prot(st); 28762306a36Sopenharmony_ci pt_dump_seq_puts(st->seq, "\n"); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci while (addr >= st->marker[1].start_address) { 29162306a36Sopenharmony_ci st->marker++; 29262306a36Sopenharmony_ci pt_dump_seq_printf(st->seq, "---[ %s ]---\n", 29362306a36Sopenharmony_ci st->marker->name); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci st->start_address = addr; 29762306a36Sopenharmony_ci st->start_pa = pa; 29862306a36Sopenharmony_ci st->last_pa = pa; 29962306a36Sopenharmony_ci st->current_prot = prot; 30062306a36Sopenharmony_ci st->level = level; 30162306a36Sopenharmony_ci } else { 30262306a36Sopenharmony_ci st->last_pa = pa; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void ptdump_walk(struct seq_file *s, struct ptd_mm_info *pinfo) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct pg_state st = { 30962306a36Sopenharmony_ci .seq = s, 31062306a36Sopenharmony_ci .marker = pinfo->markers, 31162306a36Sopenharmony_ci .level = -1, 31262306a36Sopenharmony_ci .ptdump = { 31362306a36Sopenharmony_ci .note_page = note_page, 31462306a36Sopenharmony_ci .range = (struct ptdump_range[]) { 31562306a36Sopenharmony_ci {pinfo->base_addr, pinfo->end}, 31662306a36Sopenharmony_ci {0, 0} 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci }; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ptdump_walk_pgd(&st.ptdump, pinfo->mm, NULL); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_civoid ptdump_check_wx(void) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct pg_state st = { 32762306a36Sopenharmony_ci .seq = NULL, 32862306a36Sopenharmony_ci .marker = (struct addr_marker[]) { 32962306a36Sopenharmony_ci {0, NULL}, 33062306a36Sopenharmony_ci {-1, NULL}, 33162306a36Sopenharmony_ci }, 33262306a36Sopenharmony_ci .level = -1, 33362306a36Sopenharmony_ci .check_wx = true, 33462306a36Sopenharmony_ci .ptdump = { 33562306a36Sopenharmony_ci .note_page = note_page, 33662306a36Sopenharmony_ci .range = (struct ptdump_range[]) { 33762306a36Sopenharmony_ci {KERN_VIRT_START, ULONG_MAX}, 33862306a36Sopenharmony_ci {0, 0} 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci }; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ptdump_walk_pgd(&st.ptdump, &init_mm, NULL); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (st.wx_pages) 34662306a36Sopenharmony_ci pr_warn("Checked W+X mappings: failed, %lu W+X pages found\n", 34762306a36Sopenharmony_ci st.wx_pages); 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci pr_info("Checked W+X mappings: passed, no W+X pages found\n"); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int ptdump_show(struct seq_file *m, void *v) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci ptdump_walk(m, m->private); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ptdump); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int __init ptdump_init(void) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci unsigned int i, j; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci address_markers[FIXMAP_START_NR].start_address = FIXADDR_START; 36662306a36Sopenharmony_ci address_markers[FIXMAP_END_NR].start_address = FIXADDR_TOP; 36762306a36Sopenharmony_ci address_markers[PCI_IO_START_NR].start_address = PCI_IO_START; 36862306a36Sopenharmony_ci address_markers[PCI_IO_END_NR].start_address = PCI_IO_END; 36962306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 37062306a36Sopenharmony_ci address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START; 37162306a36Sopenharmony_ci address_markers[VMEMMAP_END_NR].start_address = VMEMMAP_END; 37262306a36Sopenharmony_ci#endif 37362306a36Sopenharmony_ci address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; 37462306a36Sopenharmony_ci address_markers[VMALLOC_END_NR].start_address = VMALLOC_END; 37562306a36Sopenharmony_ci address_markers[PAGE_OFFSET_NR].start_address = PAGE_OFFSET; 37662306a36Sopenharmony_ci#ifdef CONFIG_KASAN 37762306a36Sopenharmony_ci address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START; 37862306a36Sopenharmony_ci address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END; 37962306a36Sopenharmony_ci#endif 38062306a36Sopenharmony_ci#ifdef CONFIG_64BIT 38162306a36Sopenharmony_ci address_markers[MODULES_MAPPING_NR].start_address = MODULES_VADDR; 38262306a36Sopenharmony_ci address_markers[KERNEL_MAPPING_NR].start_address = kernel_map.virt_addr; 38362306a36Sopenharmony_ci#endif 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci kernel_ptd_info.base_addr = KERN_VIRT_START; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci pg_level[1].name = pgtable_l5_enabled ? "P4D" : "PGD"; 38862306a36Sopenharmony_ci pg_level[2].name = pgtable_l4_enabled ? "PUD" : "PGD"; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pg_level); i++) 39162306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(pte_bits); j++) 39262306a36Sopenharmony_ci pg_level[i].mask |= pte_bits[j].mask; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci debugfs_create_file("kernel_page_tables", 0400, NULL, &kernel_ptd_info, 39562306a36Sopenharmony_ci &ptdump_fops); 39662306a36Sopenharmony_ci#ifdef CONFIG_EFI 39762306a36Sopenharmony_ci if (efi_enabled(EFI_RUNTIME_SERVICES)) 39862306a36Sopenharmony_ci debugfs_create_file("efi_page_tables", 0400, NULL, &efi_ptd_info, 39962306a36Sopenharmony_ci &ptdump_fops); 40062306a36Sopenharmony_ci#endif 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cidevice_initcall(ptdump_init); 406