Index: head/sys/riscv/riscv/pmap.c =================================================================== --- head/sys/riscv/riscv/pmap.c +++ head/sys/riscv/riscv/pmap.c @@ -131,6 +131,7 @@ #include #include #include +#include #include #include #include @@ -4487,3 +4488,170 @@ return (true); } + +/* + * Track a range of the kernel's virtual address space that is contiguous + * in various mapping attributes. + */ +struct pmap_kernel_map_range { + vm_offset_t sva; + pt_entry_t attrs; + int l3pages; + int l2pages; + int l1pages; +}; + +static void +sysctl_kmaps_dump(struct sbuf *sb, struct pmap_kernel_map_range *range, + vm_offset_t eva) +{ + + if (eva <= range->sva) + return; + + sbuf_printf(sb, "0x%016lx-0x%016lx r%c%c%c%c %d %d %d\n", + range->sva, eva, + (range->attrs & PTE_W) == PTE_W ? 'w' : '-', + (range->attrs & PTE_X) == PTE_X ? 'x' : '-', + (range->attrs & PTE_U) == PTE_U ? 'u' : 's', + (range->attrs & PTE_G) == PTE_G ? 'g' : '-', + range->l1pages, range->l2pages, range->l3pages); + + /* Reset to sentinel value. */ + range->sva = 0xfffffffffffffffful; +} + +/* + * Determine whether the attributes specified by a page table entry match those + * being tracked by the current range. + */ +static bool +sysctl_kmaps_match(struct pmap_kernel_map_range *range, pt_entry_t attrs) +{ + + return (range->attrs == attrs); +} + +static void +sysctl_kmaps_reinit(struct pmap_kernel_map_range *range, vm_offset_t va, + pt_entry_t attrs) +{ + + memset(range, 0, sizeof(*range)); + range->sva = va; + range->attrs = attrs; +} + +/* + * Given a leaf PTE, derive the mapping's attributes. If they do not match + * those of the current run, dump the address range and its attributes, and + * begin a new run. + */ +static void +sysctl_kmaps_check(struct sbuf *sb, struct pmap_kernel_map_range *range, + vm_offset_t va, pd_entry_t l1e, pd_entry_t l2e, pt_entry_t l3e) +{ + pt_entry_t attrs; + + /* The PTE global bit is inherited by lower levels. */ + attrs = l1e & PTE_G; + if ((l1e & PTE_RWX) != 0) + attrs |= l1e & (PTE_RWX | PTE_U); + else if (l2e != 0) + attrs |= l2e & PTE_G; + if ((l2e & PTE_RWX) != 0) + attrs |= l2e & (PTE_RWX | PTE_U); + else if (l3e != 0) + attrs |= l3e & (PTE_RWX | PTE_U | PTE_G); + + if (range->sva > va || !sysctl_kmaps_match(range, attrs)) { + sysctl_kmaps_dump(sb, range, va); + sysctl_kmaps_reinit(range, va, attrs); + } +} + +static int +sysctl_kmaps(SYSCTL_HANDLER_ARGS) +{ + struct pmap_kernel_map_range range; + struct sbuf sbuf, *sb; + pd_entry_t l1e, *l2, l2e; + pt_entry_t *l3, l3e; + vm_offset_t sva; + vm_paddr_t pa; + int error, i, j, k; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + sb = &sbuf; + sbuf_new_for_sysctl(sb, NULL, PAGE_SIZE, req); + + /* Sentinel value. */ + range.sva = 0xfffffffffffffffful; + + /* + * Iterate over the kernel page tables without holding the kernel pmap + * lock. Kernel page table pages are never freed, so at worst we will + * observe inconsistencies in the output. + */ + sva = VM_MIN_KERNEL_ADDRESS; + for (i = pmap_l1_index(sva); i < Ln_ENTRIES; i++) { + if (i == pmap_l1_index(DMAP_MIN_ADDRESS)) + sbuf_printf(sb, "\nDirect map:\n"); + else if (i == pmap_l1_index(VM_MIN_KERNEL_ADDRESS)) + sbuf_printf(sb, "\nKernel map:\n"); + + l1e = kernel_pmap->pm_l1[i]; + if ((l1e & PTE_V) == 0) { + sysctl_kmaps_dump(sb, &range, sva); + sva += L1_SIZE; + continue; + } + if ((l1e & PTE_RWX) != 0) { + sysctl_kmaps_check(sb, &range, sva, l1e, 0, 0); + range.l1pages++; + sva += L1_SIZE; + continue; + } + pa = PTE_TO_PHYS(l1e); + l2 = (pd_entry_t *)PHYS_TO_DMAP(pa); + + for (j = pmap_l2_index(sva); j < Ln_ENTRIES; j++) { + l2e = l2[j]; + if ((l2e & PTE_V) == 0) { + sysctl_kmaps_dump(sb, &range, sva); + sva += L2_SIZE; + continue; + } + if ((l2e & PTE_RWX) != 0) { + sysctl_kmaps_check(sb, &range, sva, l1e, l2e, 0); + range.l2pages++; + sva += L2_SIZE; + continue; + } + pa = PTE_TO_PHYS(l2e); + l3 = (pd_entry_t *)PHYS_TO_DMAP(pa); + + for (k = pmap_l3_index(sva); k < Ln_ENTRIES; k++, + sva += L3_SIZE) { + l3e = l3[k]; + if ((l3e & PTE_V) == 0) { + sysctl_kmaps_dump(sb, &range, sva); + continue; + } + sysctl_kmaps_check(sb, &range, sva, + l1e, l2e, l3e); + range.l3pages++; + } + } + } + + error = sbuf_finish(sb); + sbuf_delete(sb); + return (error); +} +SYSCTL_OID(_vm_pmap, OID_AUTO, kernel_maps, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, sysctl_kmaps, "A", + "Dump kernel address layout");