A Data Segment Interrupt does not indicate whether it was the result
authorkettenis <kettenis@openbsd.org>
Tue, 11 May 2021 18:21:12 +0000 (18:21 +0000)
committerkettenis <kettenis@openbsd.org>
Tue, 11 May 2021 18:21:12 +0000 (18:21 +0000)
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
sys/arch/powerpc64/powerpc64/pmap.c
sys/arch/powerpc64/powerpc64/trap.c

index 06d44ed..a531278 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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);
index 04348c7..f18c919 100644 (file)
@@ -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)
 {
index 1a7ccf0..fdba2f0 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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);