Index: src/sys/arch/evbarm/conf/GENERIC64_PMAPMI diff -u src/sys/arch/evbarm/conf/GENERIC64_PMAPMI:1.2 src/sys/arch/evbarm/conf/GENERIC64_PMAPMI:1.3 --- src/sys/arch/evbarm/conf/GENERIC64_PMAPMI:1.2 Tue May 12 11:45:33 2026 +++ src/sys/arch/evbarm/conf/GENERIC64_PMAPMI Thu May 21 11:35:40 2026 @@ -13,3 +13,4 @@ #options UVMHIST_PRINT,KERNHIST_DELAY=0 #options LOCKDEBUG +#options PMAP_DEBUG Index: src/sys/arch/evbmips/conf/OCTEON diff -u src/sys/arch/evbmips/conf/OCTEON:1.17 src/sys/arch/evbmips/conf/OCTEON:1.18 --- src/sys/arch/evbmips/conf/OCTEON:1.17 Mon Dec 8 21:33:21 2025 +++ src/sys/arch/evbmips/conf/OCTEON Thu May 21 11:35:40 2026 @@ -37,6 +37,7 @@ #options DEBUG # extra kernel debugging support #options USERCONF # userconf(4) support #options SYSCTL_INCLUDE_DESCR # Include sysctl descriptions in kernel +#options PMAP_DEBUG options DDB # kernel dynamic debugger options DDB_HISTORY_SIZE=100 # enable history editing in DDB makeoptions DEBUG="-g" # compile full symbol table Index: src/sys/arch/evbppc/conf/MPC8536DS diff -u src/sys/arch/evbppc/conf/MPC8536DS:1.30 src/sys/arch/evbppc/conf/MPC8536DS:1.31 --- src/sys/arch/evbppc/conf/MPC8536DS:1.30 Mon Jan 29 18:27:12 2024 +++ src/sys/arch/evbppc/conf/MPC8536DS Thu May 21 11:35:40 2026 @@ -42,6 +42,7 @@ options DIAGNOSTIC # cheap kernel consistency checks options DEBUG # expensive debugging checks/support #options SYSCALL_DEBUG # syscall debugging +#options PMAP_DEBUG options DDB # in-kernel debugger options DDB_HISTORY_SIZE=512 # enable history editing in DDB options TRAP_PANICWAIT Index: src/sys/arch/evbppc/conf/MPC8548CDS diff -u src/sys/arch/evbppc/conf/MPC8548CDS:1.29 src/sys/arch/evbppc/conf/MPC8548CDS:1.30 --- src/sys/arch/evbppc/conf/MPC8548CDS:1.29 Mon Jan 29 18:27:12 2024 +++ src/sys/arch/evbppc/conf/MPC8548CDS Thu May 21 11:35:40 2026 @@ -44,6 +44,7 @@ options DIAGNOSTIC # cheap kernel consistency checks options DEBUG # expensive debugging checks/support #options SYSCALL_DEBUG # syscall debugging +#options PMAP_DEBUG options DDB # in-kernel debugger options DDB_HISTORY_SIZE=512 # enable history editing in DDB options TRAP_PANICWAIT Index: src/sys/arch/riscv/conf/GENERIC64 diff -u src/sys/arch/riscv/conf/GENERIC64:1.25 src/sys/arch/riscv/conf/GENERIC64:1.26 --- src/sys/arch/riscv/conf/GENERIC64:1.25 Mon May 18 05:55:24 2026 +++ src/sys/arch/riscv/conf/GENERIC64 Thu May 21 11:35:40 2026 @@ -42,6 +42,7 @@ #options PMAP_FAULTINFO #options RISCV_SYSCALL_DEBUG #options SYSCALL_DEBUG +#options PMAP_DEBUG #options COMPAT_NETBSD32 #options EXEC_ELF32 Index: src/sys/uvm/pmap/pmap.c diff -u src/sys/uvm/pmap/pmap.c:1.93 src/sys/uvm/pmap/pmap.c:1.94 --- src/sys/uvm/pmap/pmap.c:1.93 Sun May 17 15:55:55 2026 +++ src/sys/uvm/pmap/pmap.c Thu May 21 11:35:41 2026 @@ -2552,3 +2552,176 @@ pmap_db_tlb_print(pm, pr); } #endif /* DDB */ + + +/***************************** PMAP DEBUGGING ********************************/ + +#ifdef PMAP_DEBUG + +void pmap_test_mod_ref(void); + +void +pmap_test_mod_ref(void) +{ + int val; + bool mod, ref; + bool exp_mod, exp_ref; + + vaddr_t va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, + UVM_KMF_VAONLY | UVM_KMF_NOWAIT); + + if (va == 0) { + printf("%s: couldn't allocate a VA to use\n", + __func__); + return; + } + + struct lwp *l = curlwp; + pmap_deactivate(l); + + struct vm_page * const pg = pmap_md_alloc_poolpage(0); + const paddr_t pa = VM_PAGE_TO_PHYS(pg); + volatile int * const loc = (volatile int *)va; + + /* Initialize page and mod/ref state to pristine. */ + pmap_zero_page(pa); + pmap_clear_modify(pg); + pmap_clear_reference(pg); + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = false; + exp_ref = false; + printf("%s: validating pristine page: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* Enter non-seeded R/W mapping. */ + pmap_enter(pmap_kernel(), va, pa, UVM_PROT_ALL, 0); + pmap_update(pmap_kernel()); + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = false; + exp_ref = false; + printf("%s: enter(ALL, 0): mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* reference page */ + val = *loc; + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = false; + exp_ref = true; + printf("%s: ref 1: val=%d: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + val, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* validate clear behavior. */ + exp_mod = mod; + exp_ref = ref; + mod = pmap_clear_modify(pg); + ref = pmap_clear_reference(pg); + printf("%s: checking clear 1: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = false; + exp_ref = false; + printf("%s: checking clear 2: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* reference page again */ + val = *loc; + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = false; + exp_ref = true; + printf("%s: ref 2: val=%d: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + val, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* modify page */ + *loc = 0xff; + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = true; + exp_ref = true; + printf("%s: mod 1: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* write-protect page */ + pmap_page_protect(pg, UVM_PROT_READ); + + mod = pmap_clear_modify(pg); + ref = pmap_clear_reference(pg); + exp_mod = true; + exp_ref = true; + printf("%s: mod 2: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = false; + exp_ref = false; + printf("%s: checking clear 3: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* modify page again */ + pmap_enter(pmap_kernel(), va, pa, UVM_PROT_ALL, 0); + *loc = 0xaa; + + mod = pmap_is_modified(pg); + ref = pmap_is_referenced(pg); + exp_mod = true; + exp_ref = true; + printf("%s: mod 3: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* remove all mappings of page */ + pmap_page_protect(pg, UVM_PROT_NONE); + + mod = pmap_clear_modify(pg); + ref = pmap_clear_reference(pg); + exp_mod = true; + exp_ref = true; + printf("%s: mod 4: mod=%d(%d) ref=%d(%d) (%s)\n", + __func__, + mod, exp_mod, ref, exp_ref, + mod == exp_mod && ref == exp_ref ? "OK" : "FAIL"); + + /* all done. */ + pmap_remove(pmap_kernel(), va, va + PAGE_SIZE); + pmap_update(pmap_kernel()); + + printf("%s: done\n", __func__); + + pmap_activate(l); + + uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY); +} + +#endif /* PMAP_DEBUG */