The branch main has been updated by bnovkov: URL: https://cgit.FreeBSD.org/src/commit/?id=7a5ef20a977c2d1ae5b94e6e6e927ac7a566ff10
commit 7a5ef20a977c2d1ae5b94e6e6e927ac7a566ff10 Author: Bojan Novković <[email protected]> AuthorDate: 2026-06-10 19:13:03 +0000 Commit: Bojan Novković <[email protected]> CommitDate: 2026-07-10 15:56:57 +0000 riscv/pmap.c: Handle spurious kernel page faults in critical sections The Privileged ISA specification permits caching of invalid PTEs 12.2.1. Supervisor Memory-Management Fence Instruction), which may result in a spurious page fault. Such faults are handled by 'pmap_fault' which locks the kernel pmap before inspecting and possibly updating the offending L2 entry. Unfortunately, spurious faults may also occur when we're already holding the kernel_pmap lock or running in a critical section, where any attempt to grab the pmap lock will result in a kernel panic. Fix this avoidable panic by performing a lockless lookup to determine whether a valid kernel mapping exits and flushing appropriate TLB entry. Differential Revision: https://reviews.freebsd.org/D56925 Reviewed by: jrtc27, mhorne, markj --- sys/riscv/riscv/pmap.c | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/sys/riscv/riscv/pmap.c b/sys/riscv/riscv/pmap.c index 35194ebd7c28..67123f9bfcc8 100644 --- a/sys/riscv/riscv/pmap.c +++ b/sys/riscv/riscv/pmap.c @@ -2911,28 +2911,52 @@ retryl3: PMAP_UNLOCK(pmap); } +static pt_entry_t * +pmap_fault_lookup(pmap_t pmap, vm_offset_t va) +{ + pd_entry_t *l2, l2e; + + l2 = pmap_l2(pmap, va); + if (l2 == NULL || ((l2e = pmap_load(l2)) & PTE_V) == 0) + return (NULL); + if ((l2e & PTE_RWX) == 0) + return (pmap_l2_to_l3(l2, va)); + return (l2); +} + int pmap_fault(pmap_t pmap, vm_offset_t va, vm_prot_t ftype) { - pd_entry_t *l2, l2e; pt_entry_t bits, *pte, oldpte; int rv; KASSERT(VIRT_IS_VALID(va), ("pmap_fault: invalid va %#lx", va)); + if (pmap == kernel_pmap) { + /* + * Locking the kernel pmap while processing spurious faults + * may lead to a panic since we might be running a critical section + * or already holding the kernel pmap lock. + * We deal with this by taking advantage of the fact that + * kernel PTPs are never freed and performing a lockless lookup + * to determine whether a valid mapping exits. + */ + pte = pmap_fault_lookup(pmap, va); + if (pte != NULL && (pmap_load(pte) & PTE_KERN) == PTE_KERN) { + sfence_vma_page(va); + return (1); + } + /* + * The entry is either not present or missing some bits. + * Fall back to the locked lookup below to handle the fault. + */ + } + rv = 0; PMAP_LOCK(pmap); - l2 = pmap_l2(pmap, va); - if (l2 == NULL || ((l2e = pmap_load(l2)) & PTE_V) == 0) + pte = pmap_fault_lookup(pmap, va); + if (pte == NULL || ((oldpte = pmap_load(pte)) & PTE_V) == 0) goto done; - if ((l2e & PTE_RWX) == 0) { - pte = pmap_l2_to_l3(l2, va); - if (((oldpte = pmap_load(pte)) & PTE_V) == 0) - goto done; - } else { - pte = l2; - oldpte = l2e; - } if ((pmap != kernel_pmap && (oldpte & PTE_U) == 0) || (ftype == VM_PROT_WRITE && (oldpte & PTE_W) == 0) ||
