Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/aarch64/aarch64 aarch64/pmap(9): Teach pmap_protect...



details:   https://anonhg.NetBSD.org/src/rev/633df62a7e5d
branches:  trunk
changeset: 372200:633df62a7e5d
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun Oct 30 10:26:48 2022 +0000

description:
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

diffstat:

 sys/arch/aarch64/aarch64/pmap.c |  36 ++++++++++++++++++++----------------
 1 files changed, 20 insertions(+), 16 deletions(-)

diffs (71 lines):

diff -r 36b08695c66a -r 633df62a7e5d sys/arch/aarch64/aarch64/pmap.c
--- a/sys/arch/aarch64/aarch64/pmap.c   Sun Oct 30 10:20:45 2022 +0000
+++ b/sys/arch/aarch64/aarch64/pmap.c   Sun Oct 30 10:26:48 2022 +0000
@@ -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 <ryo%nerv.org@localhost>
@@ -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 @@
 #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 @@
                        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) {



Home | Main Index | Thread Index | Old Index