From 2942dd665b2f5211be162d4dd1f187ed5277f9b7 Mon Sep 17 00:00:00 2001 From: kettenis Date: Tue, 11 May 2021 18:21:12 +0000 Subject: [PATCH] A Data Segment Interrupt does not indicate whether it was the result of a read or a write fault. Unfortunately that means we can't call uvm_fault(), as we have to pass the right access_type. In particular, passing PROT_READ for write access on a write-only page will fail. Fix this issue by inserting an appropriate SLB entry when a mapping exists at the fault address. A subsequent Data Storage Interrupt will call uvm_fault() to insert a mapping for the page into the page tables. Fixes the sys/kern/fork-exit regress test. Debugging done by bluhm@ and patrick@ ok bluhm@ --- sys/arch/powerpc64/include/pmap.h | 3 ++- sys/arch/powerpc64/powerpc64/pmap.c | 16 ++++++++++++- sys/arch/powerpc64/powerpc64/trap.c | 36 +++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/sys/arch/powerpc64/include/pmap.h b/sys/arch/powerpc64/include/pmap.h index 06d44eddb1a..a531278411b 100644 --- a/sys/arch/powerpc64/include/pmap.h +++ b/sys/arch/powerpc64/include/pmap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.h,v 1.15 2020/08/25 17:49:58 kettenis Exp $ */ +/* $OpenBSD: pmap.h,v 1.16 2021/05/11 18:21:12 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis @@ -68,6 +68,7 @@ void pmap_bootstrap(void); void pmap_bootstrap_cpu(void); int pmap_slbd_fault(pmap_t, vaddr_t); +int pmap_slbd_enter(pmap_t, vaddr_t); int pmap_set_user_slb(pmap_t, vaddr_t, vaddr_t *, vsize_t *); void pmap_clear_user_slb(void); void pmap_unset_user_slb(void); diff --git a/sys/arch/powerpc64/powerpc64/pmap.c b/sys/arch/powerpc64/powerpc64/pmap.c index 04348c7a6f5..f18c919049e 100644 --- a/sys/arch/powerpc64/powerpc64/pmap.c +++ b/sys/arch/powerpc64/powerpc64/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.55 2020/12/23 17:54:04 kettenis Exp $ */ +/* $OpenBSD: pmap.c,v 1.56 2021/05/11 18:21:12 kettenis Exp $ */ /* * Copyright (c) 2015 Martin Pieuchot @@ -438,6 +438,20 @@ pmap_slbd_alloc(pmap_t pm, vaddr_t va) return slbd; } +int +pmap_slbd_enter(pmap_t pm, vaddr_t va) +{ + struct slb_desc *slbd; + + PMAP_VP_LOCK(pm); + slbd = pmap_slbd_lookup(pm, va); + if (slbd == NULL) + slbd = pmap_slbd_alloc(pm, va); + PMAP_VP_UNLOCK(pm); + + return slbd ? 0 : EFAULT; +} + int pmap_set_user_slb(pmap_t pm, vaddr_t va, vaddr_t *kva, vsize_t *len) { diff --git a/sys/arch/powerpc64/powerpc64/trap.c b/sys/arch/powerpc64/powerpc64/trap.c index 1a7ccf04886..fdba2f0e325 100644 --- a/sys/arch/powerpc64/powerpc64/trap.c +++ b/sys/arch/powerpc64/powerpc64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.50 2021/05/05 07:29:01 mpi Exp $ */ +/* $OpenBSD: trap.c,v 1.51 2021/05/11 18:21:12 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis @@ -53,6 +53,7 @@ trap(struct trapframe *frame) int type = frame->exc; union sigval sv; struct vm_map *map; + struct vm_map_entry *entry; pmap_t pm; vaddr_t va; int access_type; @@ -123,7 +124,7 @@ trap(struct trapframe *frame) va = curpcb->pcb_userva | (va & SEGMENT_MASK); } if (frame->dsisr & DSISR_STORE) - access_type = PROT_READ | PROT_WRITE; + access_type = PROT_WRITE; else access_type = PROT_READ; error = uvm_fault(map, trunc_page(va), 0, access_type); @@ -220,8 +221,33 @@ trap(struct trapframe *frame) error = pmap_slbd_fault(pm, frame->dar); if (error == 0) break; - frame->dsisr = 0; - /* FALLTHROUGH */ + + if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p), + "[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n", + uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial)) + goto out; + + /* + * Unfortunately, the hardware doesn't tell us whether + * this was a read or a write fault. So we check + * whether there is a mapping at the fault address and + * insert a new SLB entry. Executing the faulting + * instruction again should result in a Data Storage + * Interrupt that does indicate whether we're dealing + * with with a read or a write fault. + */ + map = &p->p_vmspace->vm_map; + vm_map_lock_read(map); + if (uvm_map_lookup_entry(map, frame->dar, &entry)) + error = pmap_slbd_enter(pm, frame->dar); + else + error = EFAULT; + vm_map_unlock_read(map); + if (error) { + sv.sival_ptr = (void *)frame->dar; + trapsignal(p, SIGSEGV, 0, SEGV_MAPERR, sv); + } + break; case EXC_DSI|EXC_USER: if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p), @@ -232,7 +258,7 @@ trap(struct trapframe *frame) map = &p->p_vmspace->vm_map; va = frame->dar; if (frame->dsisr & DSISR_STORE) - access_type = PROT_READ | PROT_WRITE; + access_type = PROT_WRITE; else access_type = PROT_READ; error = uvm_fault(map, trunc_page(va), 0, access_type); -- 2.20.1