From c481cdca925ba5f9c0380cdf2910a2eca7108249 Mon Sep 17 00:00:00 2001 From: bjh21 Date: Wed, 27 Dec 2000 18:35:18 +0000 Subject: [PATCH] Overhaul of data-abort handling: - Make data_abort_address() return the whole affected range, rather than just one end. - Don't rely on pmap_confess(). Instead, for LDM/STM, behave as if two faults had occurred, one at each end of the range. - Separate out fault handling into do_fault(), and call it from data_abort_handler() (twice) and prefetch_abort_handler(). --- sys/arch/arm26/arm26/except.c | 99 +++++++++++++++-------------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/sys/arch/arm26/arm26/except.c b/sys/arch/arm26/arm26/except.c index a19cfb98e1a6..3938c0a90f60 100644 --- a/sys/arch/arm26/arm26/except.c +++ b/sys/arch/arm26/arm26/except.c @@ -1,4 +1,4 @@ -/* $NetBSD: except.c,v 1.21 2000/12/27 16:57:09 bjh21 Exp $ */ +/* $NetBSD: except.c,v 1.22 2000/12/27 18:35:18 bjh21 Exp $ */ /*- * Copyright (c) 1998, 1999, 2000 Ben Harris * All rights reserved. @@ -32,7 +32,7 @@ #include -__KERNEL_RCSID(0, "$NetBSD: except.c,v 1.21 2000/12/27 16:57:09 bjh21 Exp $"); +__KERNEL_RCSID(0, "$NetBSD: except.c,v 1.22 2000/12/27 18:35:18 bjh21 Exp $"); #include "opt_cputypes.h" #include "opt_ddb.h" @@ -65,8 +65,10 @@ __KERNEL_RCSID(0, "$NetBSD: except.c,v 1.21 2000/12/27 16:57:09 bjh21 Exp $"); #endif void syscall(struct trapframe *); +static void do_fault(struct trapframe *, struct proc *, vm_map_t, vaddr_t, + vm_prot_t); static void data_abort_fixup(struct trapframe *); -static vaddr_t data_abort_address(struct trapframe *); +static vaddr_t data_abort_address(struct trapframe *, vsize_t *); static vm_prot_t data_abort_atype(struct trapframe *); static boolean_t data_abort_usrmode(struct trapframe *); #ifdef DEBUG @@ -349,7 +351,6 @@ prefetch_abort_handler(struct trapframe *tf) u_quad_t sticks; vaddr_t pc; struct proc *p; - int ret; /* Enable interrupts if they were enabled before the trap. */ if ((tf->tf_r15 & R15_IRQ_DISABLE) == 0) @@ -382,28 +383,8 @@ prefetch_abort_handler(struct trapframe *tf) /* User-mode prefetch abort */ pc = tf->tf_r15 & R15_PC; - if (pmap_fault(p->p_vmspace->vm_map.pmap, pc, VM_PROT_EXECUTE)) - goto out; - for (;;) { - ret = uvm_fault(&p->p_vmspace->vm_map, pc, 0, VM_PROT_EXECUTE); - if (ret != KERN_RESOURCE_SHORTAGE) - break; - log(LOG_WARNING, "pid %d: VM shortage, sleeping\n", p->p_pid); - tsleep(&lbolt, PVM, "abtretry", 0); - } + do_fault(tf, p, &p->p_vmspace->vm_map, pc, VM_PROT_EXECUTE); - if (ret != KERN_SUCCESS) { -#ifdef DEBUG - printf("unhandled fault at %p (ret = %d)\n", (void *)pc, ret); - printf("Prefetch abort:\n"); - printregs(tf); -#ifdef DDB - Debugger(); -#endif -#endif - trapsignal(p, SIGSEGV, pc); - } -out: userret(p, pc, sticks); } @@ -412,12 +393,11 @@ data_abort_handler(struct trapframe *tf) { u_quad_t sticks; vaddr_t pc, va; - int ret; + vsize_t asize; struct proc *p; vm_prot_t atype; - boolean_t usrmode; + boolean_t usrmode, twopages; vm_map_t map; - struct pcb *curpcb; /* * Data aborts in kernel mode are possible (copyout etc), so @@ -442,15 +422,34 @@ data_abort_handler(struct trapframe *tf) sticks = p->p_sticks; pc = tf->tf_r15 & R15_PC; data_abort_fixup(tf); - va = data_abort_address(tf); + va = data_abort_address(tf, &asize); atype = data_abort_atype(tf); usrmode = data_abort_usrmode(tf); + twopages = (trunc_page(va) != round_page(va + asize) - PAGE_SIZE); if (!usrmode && va >= VM_MIN_KERNEL_ADDRESS) map = kernel_map; else map = &p->p_vmspace->vm_map; + do_fault(tf, p, map, va, atype); + if (twopages) + do_fault(tf, p, map, va + asize - 4, atype); + + if ((tf->tf_r15 & R15_MODE) == R15_MODE_USR) + userret(p, pc, sticks); +} + +/* + * General page fault handler. + */ +void +do_fault(struct trapframe *tf, struct proc *p, + vm_map_t map, vaddr_t va, vm_prot_t atype) +{ + int ret; + struct pcb *curpcb; + if (pmap_fault(map->pmap, va, atype)) - goto out; + return; for (;;) { ret = uvm_fault(map, va, 0, atype); if (ret != KERN_RESOURCE_SHORTAGE) @@ -462,7 +461,6 @@ data_abort_handler(struct trapframe *tf) if (ret != KERN_SUCCESS) { #ifdef DEBUG printf("unhandled fault at %p (ret = %d)\n", (void *)va, ret); - printf("Data abort:\n"); printregs(tf); printf("pc -> "); disassemble(tf->tf_r15 & R15_PC); @@ -480,10 +478,6 @@ data_abort_handler(struct trapframe *tf) longjmp(curpcb->pcb_onfault_lj); trapsignal(p, SIGSEGV, va); } - -out: - if ((tf->tf_r15 & R15_MODE) == R15_MODE_USR) - userret(p, pc, sticks); } #define getreg(r) (((register_t *)tf)[r]) @@ -527,7 +521,7 @@ data_abort_fixup(struct trapframe *tf) * abort fixup stuff too. */ static vaddr_t -data_abort_address(struct trapframe *tf) +data_abort_address(struct trapframe *tf, vsize_t *vsp) { register_t insn; int rn, rm, offset, shift, p, i, u; @@ -537,6 +531,7 @@ data_abort_address(struct trapframe *tf) insn = *(register_t *)(tf->tf_r15 & R15_PC); if ((insn & 0x0c000000) == 0x04000000) { /* Single data transfer */ + *vsp = 1; /* or 4, but it doesn't really matter */ rn = (insn & 0x000f0000) >> 16; base = getreg(rn); if (rn == 15) @@ -585,41 +580,33 @@ data_abort_address(struct trapframe *tf) else return base + offset; } else if ((insn & 0x0e000000) == 0x08000000) { - vaddr_t sva, eva; int loop, count; /* LDM/STM */ rn = (insn >> 16) & 0x0f; - /* Need to find both ends of the range being transferred. */ p = insn & 1 << 24; u = insn & 1 << 23; - if (p == 0) - sva = getreg(rn); - else - if (u == 0) - sva = getreg(rn) - 4; - else - sva = getreg(rn) + 4; - /* - * This is currently bogus. We should check the - * lowest address first. I doubt they'll notice. - */ - if (pmap_confess(sva, data_abort_atype(tf))) - return sva; /* Count registers transferred */ count = 0; - for (loop = 0; loop < 16; ++loop) { + for (loop = 0; loop < 16; ++loop) if (insn & (1<