Module Name: src Committed By: riastradh Date: Sun Oct 30 10:26:48 UTC 2022
Modified Files: src/sys/arch/aarch64/aarch64: pmap.c Log Message: aarch64/pmap(9): Teach pmap_protect about pmap_kenter_pa mappings. Pages mapped with pmap_kenter_pa are necessarily unmanaged, so there are no P->V records, and pmap_kenter_pa leaves pp->pp_pv.pv_va zero with no modified/referenced state. However, pmap_protect erroneously examined pp->pp_pv.pv_va to ascertain the modified/referenced state -- and if the page was not marked referenced, pmap_protect would clear the LX_BLKPAG_AF bit (Access Flag), with the effect that subsequent uses of the page fault and require a detour through pmap_fault_fixup. This caused problems for the kernel module loader: - When loading the text section, kobj_load first allocates kva with uvm_km_alloc(UVM_KMF_WIRED|UVM_KMF_EXEC), which creates ptes with pmap_kenter_pa. These ptes are writable, so we can copy the text section into them, and have LX_BLKPAG_AF set so there will be no fault when they are used by the kernel. - But then kobj_affix makes the text section read/execute-only (and nonwritable) with uvm_km_protect(VM_PROT_READ|VM_PROT_EXECUTE), which updates the ptes with pmap_protect. This _should_ leave LX_BLKPAG_AF set, but by inadvertently treating the page as managed when it should be unmanaged, pmap_protect cleared it instead. - Most of the time, clearing LX_BLKPAG_AF caused no problem, because pmap_fault_fixup would silently resolve it. But if a hard interrupt handler tried to use any page in the module's text (or rodata, I suspect) that was not yet fixed up, the CPU would fault and enter pmap_fault_fixup -- which would promptly crash (or hang) by trying to take the pmap lock in interrupt context, which is forbidden. I observed this by loading dtrace.kmod early at boot and trying to dtrace hard interrupt handlers. With this change, pmap_protect now recognizes wired mappings (as created by pmap_kenter_pa) before consulting pp->pp_pv.pv_va, and preserves then LX_BLKPAG_AF bit in that case. ok skrll To generate a diff of this commit: cvs rdiff -u -r1.145 -r1.146 src/sys/arch/aarch64/aarch64/pmap.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/aarch64/aarch64/pmap.c diff -u src/sys/arch/aarch64/aarch64/pmap.c:1.145 src/sys/arch/aarch64/aarch64/pmap.c:1.146 --- src/sys/arch/aarch64/aarch64/pmap.c:1.145 Sat Oct 29 07:21:41 2022 +++ src/sys/arch/aarch64/aarch64/pmap.c Sun Oct 30 10:26:48 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.c,v 1.145 2022/10/29 07:21:41 skrll Exp $ */ +/* $NetBSD: pmap.c,v 1.146 2022/10/30 10:26:48 riastradh Exp $ */ /* * Copyright (c) 2017 Ryo Shimizu <r...@nerv.org> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.145 2022/10/29 07:21:41 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.146 2022/10/30 10:26:48 riastradh Exp $"); #include "opt_arm_debug.h" #include "opt_cpuoptions.h" @@ -1410,9 +1410,7 @@ pmap_protect(struct pmap *pm, vaddr_t sv #ifdef UVMHIST pt_entry_t opte; #endif - struct vm_page *pg; struct pmap_page *pp; - paddr_t pa; uint32_t mdattr; bool executable; @@ -1428,24 +1426,30 @@ pmap_protect(struct pmap *pm, vaddr_t sv continue; } - pa = lxpde_pa(pte); - pg = PHYS_TO_VM_PAGE(pa); - if (pg != NULL) { - pp = VM_PAGE_TO_PP(pg); - PMAP_COUNT(protect_managed); - } else { + if (pte & LX_BLKPAG_NG) { + const paddr_t pa = lxpde_pa(pte); + struct vm_page *const pg = PHYS_TO_VM_PAGE(pa); + + if (pg != NULL) { + pp = VM_PAGE_TO_PP(pg); + PMAP_COUNT(protect_managed); + } else { #ifdef __HAVE_PMAP_PV_TRACK - pp = pmap_pv_tracked(pa); + pp = pmap_pv_tracked(pa); #ifdef PMAPCOUNTERS - if (pp != NULL) - PMAP_COUNT(protect_pvmanaged); - else - PMAP_COUNT(protect_unmanaged); + if (pp != NULL) + PMAP_COUNT(protect_pvmanaged); + else + PMAP_COUNT(protect_unmanaged); #endif #else + pp = NULL; + PMAP_COUNT(protect_unmanaged); +#endif /* __HAVE_PMAP_PV_TRACK */ + } + } else { /* kenter */ pp = NULL; PMAP_COUNT(protect_unmanaged); -#endif /* __HAVE_PMAP_PV_TRACK */ } if (pp != NULL) {