/* $NetBSD: trap.c,v 1.57 2008/10/21 12:16:59 ad Exp $ */ /*- * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Matthew Fredette. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* $OpenBSD: trap.c,v 1.30 2001/09/19 20:50:56 mickey Exp $ */ /* * Copyright (c) 1998-2000 Michael Shalayeff * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Michael Shalayeff. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.57 2008/10/21 12:16:59 ad Exp $"); /* #define INTRDEBUG */ /* #define TRAPDEBUG */ /* #define USERTRACE */ #include "opt_kgdb.h" #include "opt_ptrace.h" #include "opt_sa.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/syscall.h> #include <sys/syscallvar.h> #include <sys/sa.h> #include <sys/savar.h> #include <sys/mutex.h> #include <sys/ktrace.h> #include <sys/proc.h> #include <sys/signalvar.h> #include <sys/user.h> #include <sys/acct.h> #include <sys/signal.h> #include <sys/device.h> #include <sys/pool.h> #include <sys/userret.h> #include <net/netisr.h> #ifdef KGDB #include <sys/kgdb.h> #endif #include <uvm/uvm.h> #include <machine/iomod.h> #include <machine/cpufunc.h> #include <machine/reg.h> #include <machine/autoconf.h> #include <machine/db_machdep.h> #include <hppa/hppa/machdep.h> #include <ddb/db_output.h> #include <ddb/db_interface.h> #ifdef PTRACE void ss_clear_breakpoints(struct lwp *l); int ss_put_value(struct lwp *, vaddr_t, u_int); int ss_get_value(struct lwp *, vaddr_t, u_int *); #endif /* single-step breakpoint */ #define SSBREAKPOINT (HPPA_BREAK_KERNEL | (HPPA_BREAK_SS << 13)) #if defined(DEBUG) || defined(DIAGNOSTIC) /* * 0x6fc1000 is a stwm r1, d(sr0, sp), which is the last * instruction in the function prologue that gcc -O0 uses. * When we have this instruction we know the relationship * between the stack pointer and the gcc -O0 frame pointer * (in r3, loaded with the initial sp) for the body of a * function. * * If the given instruction is a stwm r1, d(sr0, sp) where * d > 0, we evaluate to d, else we evaluate to zero. */ #define STWM_R1_D_SR0_SP(inst) \ (((inst) & 0xffffc001) == 0x6fc10000 ? (((inst) & 0x00003ff) >> 1) : 0) #endif /* DEBUG || DIAGNOSTIC */ const char *trap_type[] = { "invalid", "HPMC", "power failure", "recovery counter", "external interrupt", "LPMC", "ITLB miss fault", "instruction protection", "Illegal instruction", "break instruction", "privileged operation", "privileged register", "overflow", "conditional", "assist exception", "DTLB miss", "ITLB non-access miss", "DTLB non-access miss", "data protection/rights/alignment", "data break", "TLB dirty", "page reference", "assist emulation", "higher-priv transfer", "lower-priv transfer", "taken branch", "data access rights", "data protection", "unaligned data ref", }; int trap_types = sizeof(trap_type)/sizeof(trap_type[0]); uint8_t fpopmap[] = { 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; volatile int astpending; void pmap_hptdump(void); void syscall(struct trapframe *, int *); #if defined(DEBUG) struct trapframe *sanity_frame; struct lwp *sanity_lwp; int sanity_checked = 0; void frame_sanity_check(int, int, struct trapframe *, struct lwp *); #endif #ifdef USERTRACE /* * USERTRACE is a crude facility that traces the PC of * a single user process. This tracing is normally * activated by the dispatching of a certain syscall * with certain arguments - see the activation code in * syscall(). */ static void user_backtrace(struct trapframe *, struct lwp *, int); static void user_backtrace_raw(u_int, u_int); u_int rctr_next_iioq; #endif static inline void userret(struct lwp *l, register_t pc, u_quad_t oticks) { struct proc *p = l->l_proc; if (curcpu()->ci_want_resched) { preempt(); } mi_userret(l); /* * If profiling, charge recent system time to the trapped pc. */ if (p->p_stflag & PST_PROFIL) { extern int psratio; addupc_task(l, pc, (int)(p->p_sticks - oticks) * psratio); } } /* * This handles some messy kernel debugger details. * It dispatches into either kgdb or DDB, and knows * about some special things to do, like skipping over * break instructions and how to really set up for * a single-step. */ #if defined(KGDB) || defined(DDB) static int trap_kdebug(int type, int code, struct trapframe *frame) { int handled; u_int tf_iioq_head_old; u_int tf_iioq_tail_old; for(;;) { /* This trap has not been handled. */ handled = 0; /* Remember the instruction offset queue. */ tf_iioq_head_old = frame->tf_iioq_head; tf_iioq_tail_old = frame->tf_iioq_tail; #ifdef KGDB /* Let KGDB handle it (if connected) */ if (!handled) handled = kgdb_trap(type, frame); #endif #ifdef DDB /* Let DDB handle it. */ if (!handled) handled = kdb_trap(type, code, frame); #endif /* If this trap wasn't handled, return now. */ if (!handled) return(0); /* * If the instruction offset queue head changed, * but the offset queue tail didn't, assume that * the user wants to jump to the head offset, and * adjust the tail accordingly. This should fix * the kgdb `jump' command, and can help DDB users * who `set' the offset head but forget the tail. */ if (frame->tf_iioq_head != tf_iioq_head_old && frame->tf_iioq_tail == tf_iioq_tail_old) frame->tf_iioq_tail = frame->tf_iioq_head + 4; /* * This is some single-stepping support. * If we're trying to step through a nullified * instruction, just advance by hand and trap * again. Otherwise, load the recovery counter * with zero. */ if (frame->tf_ipsw & PSW_R) { #ifdef TRAPDEBUG printf("(single stepping at head 0x%x tail 0x%x)\n", frame->tf_iioq_head, frame->tf_iioq_tail); #endif if (frame->tf_ipsw & PSW_N) { #ifdef TRAPDEBUG printf("(single stepping past nullified)\n"); #endif /* Advance the program counter. */ frame->tf_iioq_head = frame->tf_iioq_tail; frame->tf_iioq_tail = frame->tf_iioq_head + 4; /* Clear flags. */ frame->tf_ipsw &= ~(PSW_N|PSW_X|PSW_Y|PSW_Z|PSW_B|PSW_T|PSW_H|PSW_L); /* Simulate another trap. */ type = T_RECOVERY; continue; } frame->tf_rctr = 0; } /* We handled this trap. */ return (1); } /* NOTREACHED */ } #else /* !KGDB && !DDB */ #define trap_kdebug(t, c, f) (0) #endif /* !KGDB && !DDB */ #if defined(DEBUG) || defined(USERTRACE) /* * These functions give a crude usermode backtrace. They * really only work when code has been compiled without * optimization, as they assume a certain function prologue * sets up a frame pointer and stores the return pointer * and arguments in it. */ static void user_backtrace_raw(u_int pc, u_int fp) { int frame_number; int arg_number; for (frame_number = 0; frame_number < 100 && pc > HPPA_PC_PRIV_MASK && fp; frame_number++) { printf("%3d: pc=%08x%s fp=0x%08x", frame_number, pc & ~HPPA_PC_PRIV_MASK, USERMODE(pc) ? " " : "**", fp); for(arg_number = 0; arg_number < 4; arg_number++) printf(" arg%d=0x%08x", arg_number, (int) fuword(HPPA_FRAME_CARG(arg_number, fp))); printf("\n"); pc = fuword(((register_t *) fp) - 5); /* fetch rp */ if (pc == -1) { printf(" fuword for pc failed\n"); break; } fp = fuword(((register_t *) fp) + 0); /* fetch previous fp */ if (fp == -1) { printf(" fuword for fp failed\n"); break; } } printf(" backtrace stopped with pc %08x fp 0x%08x\n", pc, fp); } static void user_backtrace(struct trapframe *tf, struct lwp *l, int type) { struct proc *p = l->l_proc; u_int pc, fp, inst; /* * Display any trap type that we have. */ if (type >= 0) printf("pid %d (%s) trap #%d\n", p->p_pid, p->p_comm, type & ~T_USER); /* * Assuming that the frame pointer in r3 is valid, * dump out a stack trace. */ fp = tf->tf_r3; printf("pid %d (%s) backtrace, starting with fp 0x%08x\n", p->p_pid, p->p_comm, fp); user_backtrace_raw(tf->tf_iioq_head, fp); /* * In case the frame pointer in r3 is not valid, * assuming the stack pointer is valid and the * faulting function is a non-leaf, if we can * find its prologue we can recover its frame * pointer. */ pc = tf->tf_iioq_head; fp = tf->tf_sp - HPPA_FRAME_SIZE; printf("pid %d (%s) backtrace, starting with sp 0x%08x pc 0x%08x\n", p->p_pid, p->p_comm, tf->tf_sp, pc); for (pc &= ~HPPA_PC_PRIV_MASK; pc > 0; pc -= sizeof(inst)) { inst = fuword((register_t *) pc); if (inst == -1) { printf(" fuword for inst at pc %08x failed\n", pc); break; } /* Check for the prologue instruction that sets sp. */ if (STWM_R1_D_SR0_SP(inst)) { fp = tf->tf_sp - STWM_R1_D_SR0_SP(inst); printf(" sp from fp at pc %08x: %08x\n", pc, inst); break; } } user_backtrace_raw(tf->tf_iioq_head, fp); } #endif /* DEBUG || USERTRACE */ #ifdef DEBUG /* * This sanity-checks a trapframe. It is full of various * assumptions about what a healthy CPU state should be, * with some documented elsewhere, some not. */ void frame_sanity_check(int where, int type, struct trapframe *tf, struct lwp *l) { extern int kernel_text; extern int etext; extern register_t kpsw; extern vaddr_t hpt_base; extern vsize_t hpt_mask; #define SANITY(e) \ do { \ if (sanity_frame == NULL && !(e)) { \ sanity_frame = tf; \ sanity_lwp = l; \ sanity_checked = __LINE__; \ } \ } while (/* CONSTCOND */ 0) SANITY((tf->tf_ipsw & kpsw) == kpsw); SANITY(tf->tf_hptm == hpt_mask && tf->tf_vtop == hpt_base); SANITY((kpsw & PSW_I) == 0 || tf->tf_eiem != 0); if (tf->tf_iisq_head == HPPA_SID_KERNEL) { vaddr_t minsp, maxsp; /* * If the trap happened in the gateway * page, we take the easy way out and * assume that the trapframe is okay. */ if ((tf->tf_iioq_head & ~PAGE_MASK) == SYSCALLGATE) goto out; SANITY(!USERMODE(tf->tf_iioq_head)); SANITY(!USERMODE(tf->tf_iioq_tail)); /* * Don't check the instruction queues or stack on interrupts * as we could be be in the sti code (outside normal kernel * text) or switching LWPs (curlwp and sp are not in sync) */ if ((type & ~T_USER) == T_INTERRUPT) goto out; SANITY(tf->tf_iioq_head >= (u_int) &kernel_text); SANITY(tf->tf_iioq_head < (u_int) &etext); SANITY(tf->tf_iioq_tail >= (u_int) &kernel_text); SANITY(tf->tf_iioq_tail < (u_int) &etext); #ifdef HPPA_REDZONE maxsp = (u_int)(l->l_addr) + HPPA_REDZONE; #else maxsp = (u_int)(l->l_addr) + USPACE; #endif minsp = (u_int)(l->l_addr) + PAGE_SIZE; SANITY(l != NULL || (tf->tf_sp >= minsp && tf->tf_sp < maxsp)); } else { SANITY(USERMODE(tf->tf_iioq_head)); SANITY(USERMODE(tf->tf_iioq_tail)); SANITY(l != NULL && tf->tf_cr30 == kvtop((void *)l->l_addr)); } #undef SANITY out: if (sanity_frame == tf) { printf("insanity: where 0x%x type 0x%x tf %p lwp %p line %d " "sp 0x%x pc 0x%x\n", where, type, sanity_frame, sanity_lwp, sanity_checked, tf->tf_sp, tf->tf_iioq_head); (void) trap_kdebug(T_IBREAK, 0, tf); sanity_frame = NULL; sanity_lwp = NULL; sanity_checked = 0; } } #endif /* DEBUG */ void trap(int type, struct trapframe *frame) { struct lwp *l; struct proc *p; struct pcb *pcbp; vaddr_t va; struct vm_map *map; struct vmspace *vm; vm_prot_t vftype; pa_space_t space; ksiginfo_t ksi; u_int opcode, onfault; int ret; const char *tts; int type_raw; #ifdef DIAGNOSTIC extern int emergency_stack_start, emergency_stack_end; #endif type_raw = type & ~T_USER; opcode = frame->tf_iir; if (type_raw == T_ITLBMISS || type_raw == T_ITLBMISSNA || type_raw == T_IBREAK || type_raw == T_TAKENBR) { va = frame->tf_iioq_head; space = frame->tf_iisq_head; vftype = VM_PROT_EXECUTE; } else { va = frame->tf_ior; space = frame->tf_isr; vftype = inst_store(opcode) ? VM_PROT_WRITE : VM_PROT_READ; } l = curlwp; p = l ? l->l_proc : NULL; if ((type & T_USER) != 0) LWP_CACHE_CREDS(l, p); tts = (type & ~T_USER) > trap_types ? "reserved" : trap_type[type & ~T_USER]; #ifdef DIAGNOSTIC /* * If we are on the emergency stack, then we either got * a fault on the kernel stack, or we're just handling * a trap for the machine check handler (which also * runs on the emergency stack). * * We *very crudely* differentiate between the two cases * by checking the faulting instruction: if it is the * function prologue instruction that stores the old * frame pointer and updates the stack pointer, we assume * that we faulted on the kernel stack. * * In this case, not completing that instruction will * probably confuse backtraces in kgdb/ddb. Completing * it would be difficult, because we already faulted on * that part of the stack, so instead we fix up the * frame as if the function called has just returned. * This has peculiar knowledge about what values are in * what registers during the "normal gcc -g" prologue. */ if (&type >= &emergency_stack_start && &type < &emergency_stack_end && type != T_IBREAK && STWM_R1_D_SR0_SP(opcode)) { /* Restore the caller's frame pointer. */ frame->tf_r3 = frame->tf_r1; /* Restore the caller's instruction offsets. */ frame->tf_iioq_head = frame->tf_rp; frame->tf_iioq_tail = frame->tf_iioq_head + 4; goto dead_end; } #endif /* DIAGNOSTIC */ #ifdef DEBUG frame_sanity_check(0xdead01, type, frame, l); #endif /* DEBUG */ /* If this is a trap, not an interrupt, reenable interrupts. */ if (type_raw != T_INTERRUPT) mtctl(frame->tf_eiem, CR_EIEM); if (frame->tf_flags & TFF_LAST) l->l_md.md_regs = frame; #ifdef TRAPDEBUG if (type_raw != T_INTERRUPT && type_raw != T_IBREAK) printf("trap: %d, %s for %x:%x at %x:%x, fp=%p, rp=%x\n", type, tts, space, (u_int)va, frame->tf_iisq_head, frame->tf_iioq_head, frame, frame->tf_rp); else if (type_raw == T_IBREAK) printf("trap: break instruction %x:%x at %x:%x, fp=%p\n", break5(opcode), break13(opcode), frame->tf_iisq_head, frame->tf_iioq_head, frame); { extern int etext; if (frame < (struct trapframe *)&etext) { printf("trap: bogus frame ptr %p\n", frame); goto dead_end; } } #endif switch (type) { case T_NONEXIST: case T_NONEXIST|T_USER: #if !defined(DDB) && !defined(KGDB) /* we've got screwed up by the central scrutinizer */ panic ("trap: elvis has just left the building!"); break; #else goto dead_end; #endif case T_RECOVERY|T_USER: #ifdef USERTRACE for(;;) { if (frame->tf_iioq_head != rctr_next_iioq) printf("-%08x\nr %08x", rctr_next_iioq - 4, frame->tf_iioq_head); rctr_next_iioq = frame->tf_iioq_head + 4; if (frame->tf_ipsw & PSW_N) { /* Advance the program counter. */ frame->tf_iioq_head = frame->tf_iioq_tail; frame->tf_iioq_tail = frame->tf_iioq_head + 4; /* Clear flags. */ frame->tf_ipsw &= ~(PSW_N|PSW_X|PSW_Y|PSW_Z|PSW_B|PSW_T|PSW_H|PSW_L); /* Simulate another trap. */ continue; } break; } frame->tf_rctr = 0; break; #endif /* USERTRACE */ case T_RECOVERY: #if !defined(DDB) && !defined(KGDB) /* XXX will implement later */ printf ("trap: handicapped"); break; #else goto dead_end; #endif case T_EMULATION | T_USER: #ifdef FPEMUL hppa_fpu_emulate(frame, l, opcode); #else /* !FPEMUL */ /* * We don't have FPU emulation, so signal the * process with a SIGFPE. */ KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGFPE; ksi.ksi_code = SI_NOINFO; ksi.ksi_trap = type; ksi.ksi_addr = (void *)frame->tf_iioq_head; trapsignal(l, &ksi); #endif /* !FPEMUL */ break; case T_DATALIGN: if (l->l_addr->u_pcb.pcb_onfault) { do_onfault: pcbp = &l->l_addr->u_pcb; frame->tf_iioq_tail = 4 + (frame->tf_iioq_head = pcbp->pcb_onfault); pcbp->pcb_onfault = 0; break; } /*FALLTHROUGH*/ #ifdef DIAGNOSTIC /* these just can't happen ever */ case T_PRIV_OP: case T_PRIV_REG: /* these just can't make it to the trap() ever */ case T_HPMC: case T_HPMC | T_USER: case T_EMULATION: case T_EXCEPTION: #endif case T_IBREAK: case T_DBREAK: dead_end: if (type & T_USER) { #ifdef DEBUG user_backtrace(frame, l, type); #endif KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLTRP; ksi.ksi_trap = type; ksi.ksi_addr = (void *)frame->tf_iioq_head; trapsignal(l, &ksi); break; } if (trap_kdebug(type, va, frame)) return; else if (type == T_DATALIGN) panic ("trap: %s at 0x%x", tts, (u_int) va); else panic ("trap: no debugger for \"%s\" (%d)", tts, type); break; case T_IBREAK | T_USER: case T_DBREAK | T_USER: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_code = TRAP_TRACE; ksi.ksi_trap = type_raw; ksi.ksi_addr = (void *)frame->tf_iioq_head; #ifdef PTRACE ss_clear_breakpoints(l); if (opcode == SSBREAKPOINT) ksi.ksi_code = TRAP_BRKPT; #endif /* pass to user debugger */ trapsignal(l, &ksi); break; #ifdef PTRACE case T_TAKENBR | T_USER: ss_clear_breakpoints(l); KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_code = TRAP_TRACE; ksi.ksi_trap = type_raw; ksi.ksi_addr = (void *)frame->tf_iioq_head; /* pass to user debugger */ trapsignal(l, &ksi); break; #endif case T_EXCEPTION | T_USER: { /* co-proc assist trap */ uint64_t *fpp; uint32_t *pex, ex, inst; int i; hppa_fpu_flush(l); fpp = l->l_addr->u_pcb.pcb_fpregs; pex = (uint32_t *)&fpp[1]; for (i = 1; i < 8 && !*pex; i++, pex++) ; KASSERT(i < 8); ex = *pex; *pex = 0; /* reset the trap flag, as if there was none */ fpp[0] &= ~(((uint64_t)HPPA_FPU_T) << 32); /* emulate the instruction */ inst = ((uint32_t)fpopmap[ex >> 26] << 26) | (ex & 0x03ffffff); hppa_fpu_emulate(frame, l, inst); } break; case T_OVERFLOW | T_USER: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGFPE; ksi.ksi_code = SI_NOINFO; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_CONDITION | T_USER: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGFPE; ksi.ksi_code = FPE_INTDIV; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_ILLEGAL | T_USER: #ifdef DEBUG user_backtrace(frame, l, type); #endif KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLOPC; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_PRIV_OP | T_USER: #ifdef DEBUG user_backtrace(frame, l, type); #endif KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_PRVOPC; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_PRIV_REG | T_USER: #ifdef DEBUG user_backtrace(frame, l, type); #endif KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_PRVREG; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; /* these should never got here */ case T_HIGHERPL | T_USER: case T_LOWERPL | T_USER: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_ACCERR; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_IPROT | T_USER: case T_DPROT | T_USER: KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_ACCERR; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_DATACC: case T_USER | T_DATACC: case T_ITLBMISS: case T_USER | T_ITLBMISS: case T_DTLBMISS: case T_USER | T_DTLBMISS: case T_ITLBMISSNA: case T_USER | T_ITLBMISSNA: case T_DTLBMISSNA: case T_USER | T_DTLBMISSNA: case T_TLB_DIRTY: case T_USER | T_TLB_DIRTY: vm = p->p_vmspace; if (!vm) { #ifdef TRAPDEBUG printf("trap: no vm, p=%p\n", p); #endif goto dead_end; } /* * it could be a kernel map for exec_map faults */ if (!(type & T_USER) && space == HPPA_SID_KERNEL) map = kernel_map; else { map = &vm->vm_map; if ((l->l_flag & LW_SA) && (~l->l_pflag & LP_SA_NOBLOCK)) { l->l_savp->savp_faultaddr = va; l->l_pflag |= LP_SA_PAGEFAULT; } } va = trunc_page(va); if (map->pmap->pmap_space != space) { #ifdef TRAPDEBUG printf("trap: space mismatch %d != %d\n", space, map->pmap->pmap_space); #endif /* actually dump the user, crap the kernel */ goto dead_end; } /* Never call uvm_fault in interrupt context. */ KASSERT(hppa_intr_depth == 0); onfault = l->l_addr->u_pcb.pcb_onfault; l->l_addr->u_pcb.pcb_onfault = 0; ret = uvm_fault(map, va, vftype); l->l_addr->u_pcb.pcb_onfault = onfault; #ifdef TRAPDEBUG printf("uvm_fault(%p, %x, %d)=%d\n", map, (u_int)va, vftype, ret); #endif if (map != kernel_map) l->l_pflag &= ~LP_SA_PAGEFAULT; /* * If this was a stack access we keep track of the maximum * accessed stack size. Also, if uvm_fault gets a protection * failure it is due to accessing the stack region outside * the current limit and we need to reflect that as an access * error. */ if (map != kernel_map && va >= (vaddr_t)vm->vm_minsaddr) { if (ret == 0) uvm_grow(l->l_proc, va); else if (ret == EACCES) ret = EFAULT; } if (ret != 0) { if (type & T_USER) { #ifdef DEBUG user_backtrace(frame, l, type); #endif KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGSEGV; ksi.ksi_code = (ret == EACCES ? SEGV_ACCERR : SEGV_MAPERR); ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); } else { if (l->l_addr->u_pcb.pcb_onfault) { goto do_onfault; } panic("trap: uvm_fault(%p, %lx, %d): %d", map, va, vftype, ret); } } break; case T_DATALIGN | T_USER: #ifdef DEBUG user_backtrace(frame, l, type); #endif KSI_INIT_TRAP(&ksi); ksi.ksi_signo = SIGBUS; ksi.ksi_code = BUS_ADRALN; ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); break; case T_INTERRUPT: case T_INTERRUPT|T_USER: hppa_intr(frame); mtctl(frame->tf_eiem, CR_EIEM); break; case T_LOWERPL: case T_DPROT: case T_IPROT: case T_OVERFLOW: case T_CONDITION: case T_ILLEGAL: case T_HIGHERPL: case T_TAKENBR: case T_POWERFAIL: case T_LPMC: case T_PAGEREF: case T_DATAPID: case T_DATAPID | T_USER: if (0 /* T-chip */) { break; } /* FALLTHROUGH to unimplemented */ default: panic ("trap: unimplemented \'%s\' (%d)", tts, type); } if (type & T_USER) userret(l, l->l_md.md_regs->tf_iioq_head, 0); #ifdef DEBUG frame_sanity_check(0xdead02, type, frame, l); if (frame->tf_flags & TFF_LAST && (curlwp->l_flag & LW_IDLE) == 0) frame_sanity_check(0xdead03, type, curlwp->l_md.md_regs, curlwp); #endif /* DEBUG */ } void child_return(void *arg) { struct lwp *l = arg; userret(l, l->l_md.md_regs->tf_iioq_head, 0); ktrsysret(SYS_fork, 0, 0); #ifdef DEBUG frame_sanity_check(0xdead04, 0, l->l_md.md_regs, l); #endif /* DEBUG */ } #ifdef PTRACE #include <sys/ptrace.h> int ss_get_value(struct lwp *l, vaddr_t addr, u_int *value) { struct uio uio; struct iovec iov; iov.iov_base = (void *)value; iov.iov_len = sizeof(u_int); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)addr; uio.uio_resid = sizeof(u_int); uio.uio_rw = UIO_READ; UIO_SETUP_SYSSPACE(&uio); return (process_domem(curlwp, l, &uio)); } int ss_put_value(struct lwp *l, vaddr_t addr, u_int value) { struct uio uio; struct iovec iov; iov.iov_base = (void *)&value; iov.iov_len = sizeof(u_int); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)addr; uio.uio_resid = sizeof(u_int); uio.uio_rw = UIO_WRITE; UIO_SETUP_SYSSPACE(&uio); return (process_domem(curlwp, l, &uio)); } void ss_clear_breakpoints(struct lwp *l) { /* Restore origional instructions. */ if (l->l_md.md_bpva != 0) { ss_put_value(l, l->l_md.md_bpva, l->l_md.md_bpsave[0]); ss_put_value(l, l->l_md.md_bpva + 4, l->l_md.md_bpsave[1]); l->l_md.md_bpva = 0; } } int process_sstep(struct lwp *l, int sstep) { struct trapframe *tf = l->l_md.md_regs; int error; ss_clear_breakpoints(l); /* We're continuing... */ /* Don't touch the syscall gateway page. */ /* XXX head */ if (sstep == 0 || (tf->tf_iioq_tail & ~PAGE_MASK) == SYSCALLGATE) { tf->tf_ipsw &= ~PSW_T; return 0; } l->l_md.md_bpva = tf->tf_iioq_tail & ~HPPA_PC_PRIV_MASK; /* * Insert two breakpoint instructions; the first one might be * nullified. Of course we need to save two instruction * first. */ error = ss_get_value(l, l->l_md.md_bpva, &l->l_md.md_bpsave[0]); if (error) return (error); error = ss_get_value(l, l->l_md.md_bpva + 4, &l->l_md.md_bpsave[1]); if (error) return (error); error = ss_put_value(l, l->l_md.md_bpva, SSBREAKPOINT); if (error) return error; error = ss_put_value(l, l->l_md.md_bpva + 4, SSBREAKPOINT); if (error) return error; tf->tf_ipsw |= PSW_T; return 0; } #endif /* * call actual syscall routine * from the low-level syscall handler: * - all HPPA_FRAME_NARGS syscall's arguments supposed to be copied onto * our stack, this wins compared to copyin just needed amount anyway * - register args are copied onto stack too */ void syscall(struct trapframe *frame, int *args) { struct lwp *l; struct proc *p; const struct sysent *callp; int nsys, code, error; int tmp; int rval[2]; uvmexp.syscalls++; #ifdef DEBUG frame_sanity_check(0xdead04, 0, frame, curlwp); #endif /* DEBUG */ if (!USERMODE(frame->tf_iioq_head)) panic("syscall"); l = curlwp; p = l->l_proc; l->l_md.md_regs = frame; nsys = p->p_emul->e_nsysent; callp = p->p_emul->e_sysent; code = frame->tf_t1; LWP_CACHE_CREDS(l, p); #ifdef KERN_SA if (__predict_false((l->l_savp) && (l->l_savp->savp_pflags & SAVP_FLAG_DELIVERING))) l->l_savp->savp_pflags &= ~SAVP_FLAG_DELIVERING; #endif /* * Restarting a system call is touchy on the HPPA, * because syscall arguments are passed in registers * and the program counter of the syscall "point" * isn't easily divined. * * We handle the first problem by assuming that we * will have to restart this system call, so we * stuff the first four words of the original arguments * back into the frame as arg0...arg3, which is where * we found them in the first place. Any further * arguments are (still) on the user's stack and the * syscall code will fetch them from there (again). * * The program counter problem is addressed below. */ frame->tf_arg0 = args[0]; frame->tf_arg1 = args[1]; frame->tf_arg2 = args[2]; frame->tf_arg3 = args[3]; /* * Some special handling for the syscall(2) and * __syscall(2) system calls. */ switch (code) { case SYS_syscall: code = *args; args += 1; break; case SYS___syscall: if (callp != sysent) break; /* * NB: even though __syscall(2) takes a quad_t * containing the system call number, because * our argument copying word-swaps 64-bit arguments, * the least significant word of that quad_t * is the first word in the argument array. */ code = *args; args += 2; } /* * Stacks growing from lower addresses to higher * addresses are not really such a good idea, because * it makes it impossible to overlay a struct on top * of C stack arguments (the arguments appear in * reversed order). * * You can do the obvious thing (as locore.S does) and * copy argument words one by one, laying them out in * the "right" order in the destination buffer, but this * ends up word-swapping multi-word arguments (like off_t). * * To compensate, we have some automatically-generated * code that word-swaps these multi-word arguments. * Right now the script that generates this code is * in Perl, because I don't know awk. * * FIXME - this works only on native binaries and * will probably screw up any and all emulation. */ switch (code) { /* * BEGIN automatically generated * by /home/fredette/project/hppa/makescargfix.pl * do not edit! */ case SYS_pread: /* * syscallarg(int) fd; * syscallarg(void *) buf; * syscallarg(size_t) nbyte; * syscallarg(int) pad; * syscallarg(off_t) offset; */ tmp = args[4]; args[4] = args[4 + 1]; args[4 + 1] = tmp; break; case SYS_pwrite: /* * syscallarg(int) fd; * syscallarg(const void *) buf; * syscallarg(size_t) nbyte; * syscallarg(int) pad; * syscallarg(off_t) offset; */ tmp = args[4]; args[4] = args[4 + 1]; args[4 + 1] = tmp; break; case SYS_mmap: /* * syscallarg(void *) addr; * syscallarg(size_t) len; * syscallarg(int) prot; * syscallarg(int) flags; * syscallarg(int) fd; * syscallarg(long) pad; * syscallarg(off_t) pos; */ tmp = args[6]; args[6] = args[6 + 1]; args[6 + 1] = tmp; break; case SYS_lseek: /* * syscallarg(int) fd; * syscallarg(int) pad; * syscallarg(off_t) offset; */ tmp = args[2]; args[2] = args[2 + 1]; args[2 + 1] = tmp; break; case SYS_truncate: /* * syscallarg(const char *) path; * syscallarg(int) pad; * syscallarg(off_t) length; */ tmp = args[2]; args[2] = args[2 + 1]; args[2 + 1] = tmp; break; case SYS_ftruncate: /* * syscallarg(int) fd; * syscallarg(int) pad; * syscallarg(off_t) length; */ tmp = args[2]; args[2] = args[2 + 1]; args[2 + 1] = tmp; break; case SYS_preadv: /* * syscallarg(int) fd; * syscallarg(const struct iovec *) iovp; * syscallarg(int) iovcnt; * syscallarg(int) pad; * syscallarg(off_t) offset; */ tmp = args[4]; args[4] = args[4 + 1]; args[4 + 1] = tmp; break; case SYS_pwritev: /* * syscallarg(int) fd; * syscallarg(const struct iovec *) iovp; * syscallarg(int) iovcnt; * syscallarg(int) pad; * syscallarg(off_t) offset; */ tmp = args[4]; args[4] = args[4 + 1]; args[4 + 1] = tmp; break; default: break; /* * END automatically generated * by /home/fredette/project/hppa/makescargfix.pl * do not edit! */ } #ifdef USERTRACE if (0) { user_backtrace(frame, l, -1); frame->tf_ipsw |= PSW_R; frame->tf_rctr = 0; printf("r %08x", frame->tf_iioq_head); rctr_next_iioq = frame->tf_iioq_head + 4; } #endif if (code < 0 || code >= nsys) callp += p->p_emul->e_nosys; /* bad syscall # */ else callp += code; if ((error = trace_enter(code, args, callp->sy_narg)) != 0) goto out; rval[0] = 0; rval[1] = 0; error = sy_call(callp, l, args, rval); out: switch (error) { case 0: l = curlwp; /* changes on exec() */ frame = l->l_md.md_regs; frame->tf_ret0 = rval[0]; frame->tf_ret1 = rval[1]; frame->tf_t1 = 0; break; case ERESTART: /* * Now we have to wind back the instruction * offset queue to the point where the system * call will be made again. This is inherently * tied to the SYSCALL macro. * * Currently, the part of the SYSCALL macro * that we want to rerun reads as: * * ldil L%SYSCALLGATE, r1 * ble 4(sr7, r1) * ldi __CONCAT(SYS_,x), t1 * comb,<> %r0, %t1, __cerror * * And our offset queue head points to the * comb instruction. So we need to * subtract twelve to reach the ldil. */ frame->tf_iioq_head -= 12; frame->tf_iioq_tail = frame->tf_iioq_head + 4; break; case EJUSTRETURN: p = curproc; break; default: if (p->p_emul->e_errno) error = p->p_emul->e_errno[error]; frame->tf_t1 = error; break; } trace_exit(code, rval, error); userret(l, frame->tf_iioq_head, 0); #ifdef DEBUG frame_sanity_check(0xdead05, 0, frame, l); #endif /* DEBUG */ } /* * Start a new LWP */ void startlwp(void *arg) { int err; ucontext_t *uc = arg; struct lwp *l = curlwp; err = cpu_setmcontext(l, &uc->uc_mcontext, uc->uc_flags); #if DIAGNOSTIC if (err) { printf("Error %d from cpu_setmcontext.", err); } #endif pool_put(&lwp_uc_pool, uc); userret(l, l->l_md.md_regs->tf_iioq_head, 0); } /* * XXX This is a terrible name. */ void upcallret(struct lwp *l) { userret(l, l->l_md.md_regs->tf_iioq_head, 0); }