/* $NetBSD: trap.c,v 1.123 2023/10/05 19:41:04 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-2004 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. * * 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 OR HIS RELATIVES 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 MIND, 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 __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.123 2023/10/05 19:41:04 ad Exp $"); /* #define INTRDEBUG */ /* #define TRAPDEBUG */ /* #define USERTRACE */ #include "opt_kgdb.h" #include "opt_ptrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KGDB #include #endif #include #include #include #include #include #include #include #include #include #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 *); /* single-step breakpoint */ #define SSBREAKPOINT (HPPA_BREAK_KERNEL | (HPPA_BREAK_SS << 13)) #endif #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 = __arraycount(trap_type); 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, }; void pmap_hptdump(void); void syscall(struct trapframe *, int *); #if defined(DEBUG) struct trapframe *sanity_frame; struct lwp *sanity_lwp; const char *sanity_string; void frame_sanity_check(const char *, 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, struct trapframe *tf) { struct proc *p = l->l_proc; int oticks = 0; /* XXX why zero? */ do { l->l_md.md_astpending = 0; //curcpu()->ci_data.cpu_nast++; mi_userret(l); } while (l->l_md.md_astpending); /* * If profiling, charge recent system time to the trapped pc. */ if (p->p_stflag & PST_PROFIL) { extern int psratio; addupc_task(l, tf->tf_iioq_head, (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 func- * tion prologue sets up a frame pointer and stores the return pointer and arg- * uments in it. */ static void user_backtrace_raw(u_int pc, u_int fp) { int frame_number; int arg_number; uint32_t val; 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++) { if (ufetch_32(HPPA_FRAME_CARG(arg_number, fp), &val) == 0) { printf(" arg%d=0x%08x", arg_number, val); } else { printf(" arg%d=", arg_number); } } printf("\n"); if (ufetch_int((((uint32_t *) fp) - 5), &pc) != 0) { printf(" ufetch for pc failed\n"); break; } if (ufetch_int((((uint32_t *) fp) + 0), &fp) != 0) { printf(" ufetch 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)) { if (ufetch_int((u_int *) pc, &inst) != 0) { printf(" ufetch 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(const char *func, int line, int type, struct trapframe *tf, struct lwp *l) { #if 0 extern int kernel_text; extern int etext; #endif struct cpu_info *ci = curcpu(); #define SANITY(e) \ do { \ if (sanity_frame == NULL && !(e)) { \ sanity_frame = tf; \ sanity_lwp = l; \ sanity_string = #e; \ } \ } while (/* CONSTCOND */ 0) KASSERT(l != NULL); SANITY((tf->tf_ipsw & ci->ci_psw) == ci->ci_psw); SANITY((ci->ci_psw & PSW_I) == 0 || tf->tf_eiem != 0); if (tf->tf_iisq_head == HPPA_SID_KERNEL) { vaddr_t minsp, maxsp, uv; uv = uvm_lwp_getuarea(l); /* * 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 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; #if 0 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); #endif maxsp = uv + USPACE + PAGE_SIZE; minsp = uv + PAGE_SIZE; SANITY(tf->tf_sp >= minsp && tf->tf_sp < maxsp); } else { struct pcb *pcb = lwp_getpcb(l); SANITY(USERMODE(tf->tf_iioq_head)); SANITY(USERMODE(tf->tf_iioq_tail)); SANITY(tf->tf_cr30 == (u_int)pcb->pcb_fpregs); } #undef SANITY out: if (sanity_frame == tf) { printf("insanity: '%s' at %s:%d type 0x%x tf %p lwp %p " "sp 0x%x pc 0x%x\n", sanity_string, func, line, type, sanity_frame, sanity_lwp, tf->tf_sp, tf->tf_iioq_head); (void) trap_kdebug(T_IBREAK, 0, tf); sanity_frame = NULL; sanity_lwp = NULL; } } #endif /* DEBUG */ #define __PABITS(x, y) __BITS(31 - (x), 31 - (y)) #define __PABIT(x) __BIT(31 - (x)) #define LPA_MASK \ ( __PABITS(0, 5) | \ __PABITS(18, 25)) #define LPA \ (__SHIFTIN(1, __PABITS(0, 5)) | \ __SHIFTIN(0x4d, __PABITS(18, 25))) #define PROBE_ENCS (0x46 | 0xc6 | 0x47 | 0xc7) #define PROBE_PL __PABITS(14, 15) #define PROBE_IMMED __PABIT(18) #define PROBE_RW __PABIT(25) #define PROBE_MASK \ (( __PABITS(0, 5) | \ __PABITS(18, 25) | \ __PABIT(26)) ^ \ (PROBE_IMMED | PROBE_RW)) #define PROBE \ ((__SHIFTIN(1, __PABITS(0, 5)) | \ __SHIFTIN(PROBE_ENCS, __PABITS(18, 25)) | \ __SHIFTIN(0, __PABIT(26))) ^ \ (PROBE_IMMED | PROBE_RW)) /* for hppa64 */ CTASSERT(sizeof(register_t) == sizeof(u_int)); size_t hppa_regmap[] = { 0, /* r0 is special case */ offsetof(struct trapframe, tf_r1 ) / sizeof(register_t), offsetof(struct trapframe, tf_rp ) / sizeof(register_t), offsetof(struct trapframe, tf_r3 ) / sizeof(register_t), offsetof(struct trapframe, tf_r4 ) / sizeof(register_t), offsetof(struct trapframe, tf_r5 ) / sizeof(register_t), offsetof(struct trapframe, tf_r6 ) / sizeof(register_t), offsetof(struct trapframe, tf_r7 ) / sizeof(register_t), offsetof(struct trapframe, tf_r8 ) / sizeof(register_t), offsetof(struct trapframe, tf_r9 ) / sizeof(register_t), offsetof(struct trapframe, tf_r10 ) / sizeof(register_t), offsetof(struct trapframe, tf_r11 ) / sizeof(register_t), offsetof(struct trapframe, tf_r12 ) / sizeof(register_t), offsetof(struct trapframe, tf_r13 ) / sizeof(register_t), offsetof(struct trapframe, tf_r14 ) / sizeof(register_t), offsetof(struct trapframe, tf_r15 ) / sizeof(register_t), offsetof(struct trapframe, tf_r16 ) / sizeof(register_t), offsetof(struct trapframe, tf_r17 ) / sizeof(register_t), offsetof(struct trapframe, tf_r18 ) / sizeof(register_t), offsetof(struct trapframe, tf_t4 ) / sizeof(register_t), offsetof(struct trapframe, tf_t3 ) / sizeof(register_t), offsetof(struct trapframe, tf_t2 ) / sizeof(register_t), offsetof(struct trapframe, tf_t1 ) / sizeof(register_t), offsetof(struct trapframe, tf_arg3) / sizeof(register_t), offsetof(struct trapframe, tf_arg2) / sizeof(register_t), offsetof(struct trapframe, tf_arg1) / sizeof(register_t), offsetof(struct trapframe, tf_arg0) / sizeof(register_t), offsetof(struct trapframe, tf_dp ) / sizeof(register_t), offsetof(struct trapframe, tf_ret0) / sizeof(register_t), offsetof(struct trapframe, tf_ret1) / sizeof(register_t), offsetof(struct trapframe, tf_sp ) / sizeof(register_t), offsetof(struct trapframe, tf_r31 ) / sizeof(register_t), }; static inline register_t tf_getregno(struct trapframe *tf, u_int regno) { register_t *tf_reg = (register_t *)tf; if (regno == 0) return 0; else return tf_reg[hppa_regmap[regno]]; } static inline void tf_setregno(struct trapframe *tf, u_int regno, register_t val) { register_t *tf_reg = (register_t *)tf; if (regno == 0) return; else tf_reg[hppa_regmap[regno]] = val; } void trap(int type, struct trapframe *frame) { struct lwp *l; struct proc *p; struct pcb *pcb; 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 = "reserved"; int trapnum; #ifdef DIAGNOSTIC extern int emergency_stack_start, emergency_stack_end; struct cpu_info *ci = curcpu(); int oldcpl = ci->ci_cpl; #endif trapnum = type & ~T_USER; opcode = frame->tf_iir; if (trapnum <= T_EXCEPTION || trapnum == T_HIGHERPL || trapnum == T_LOWERPL || trapnum == T_TAKENBR || trapnum == T_IDEBUG || trapnum == T_PERFMON) { 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; } KASSERT(curlwp != NULL); l = curlwp; p = l->l_proc; #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(__func__, __LINE__, type, frame, l); #endif /* DEBUG */ if (frame->tf_flags & TFF_LAST) l->l_md.md_regs = frame; if (trapnum <= trap_types) tts = trap_type[trapnum]; #ifdef TRAPDEBUG if (trapnum != T_INTERRUPT && trapnum != T_IBREAK) printf("trap: %d, %s for %x:%lx at %x:%x, fp=%p, rp=%x\n", type, tts, space, va, frame->tf_iisq_head, frame->tf_iioq_head, frame, frame->tf_rp); else if (trapnum == 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 pcb = lwp_getpcb(l); /* If this is a trap, not an interrupt, reenable interrupts. */ if (trapnum != T_INTERRUPT) { curcpu()->ci_data.cpu_ntrap++; mtctl(frame->tf_eiem, CR_EIEM); } const bool user = (type & T_USER) != 0; 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: hppa_fpu_emulate(frame, l, opcode); break; case T_DATALIGN: onfault = pcb->pcb_onfault; if (onfault) { ret = EFAULT; do_onfault: frame->tf_iioq_head = onfault; frame->tf_iioq_tail = frame->tf_iioq_head + 4; frame->tf_ret0 = ret; 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_BRKPT; ksi.ksi_trap = trapnum; ksi.ksi_addr = (void *)(frame->tf_iioq_head & ~HPPA_PC_PRIV_MASK); #ifdef PTRACE ss_clear_breakpoints(l); if (opcode == SSBREAKPOINT) ksi.ksi_code = TRAP_TRACE; #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 = trapnum; ksi.ksi_addr = (void *)(frame->tf_iioq_head & ~HPPA_PC_PRIV_MASK); /* 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 = (uint64_t *)pcb->pcb_fpregs; /* skip the status register */ pex = (uint32_t *)&fpp[0]; pex++; /* loop through the exception registers */ 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_ITLBMISSNA: case T_USER | T_ITLBMISSNA: case T_DTLBMISSNA: case T_USER | T_DTLBMISSNA: 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 (!user && space == HPPA_SID_KERNEL) map = kernel_map; else { map = &vm->vm_map; } va = trunc_page(va); if ((opcode & LPA_MASK) == LPA) { /* lpa failure case */ const u_int regno = __SHIFTOUT(opcode, __PABITS(27, 31)); tf_setregno(frame, regno, 0); frame->tf_ipsw |= PSW_N; } else if ((opcode & PROBE_MASK) == PROBE) { u_int pl; if ((opcode & PROBE_IMMED) == 0) { pl = __SHIFTOUT(opcode, __PABITS(14, 15)); } else { const u_int plreg = __SHIFTOUT(opcode, __PABITS(11, 15)); pl = tf_getregno(frame, plreg); } bool ok = true; if ((user && space == HPPA_SID_KERNEL) || (frame->tf_iioq_head & 3) != pl || (user && va >= VM_MAXUSER_ADDRESS)) { ok = false; } else { /* Never call uvm_fault in interrupt context. */ KASSERT(curcpu()->ci_intr_depth == 0); const bool read = __SHIFTOUT(opcode, PROBE_RW) == 0; onfault = pcb->pcb_onfault; pcb->pcb_onfault = 0; ret = uvm_fault(map, va, read ? VM_PROT_READ : VM_PROT_WRITE); pcb->pcb_onfault = onfault; if (ret) ok = false; } if (!ok) { const u_int regno = __SHIFTOUT(opcode, __PABITS(27, 31)); tf_setregno(frame, regno, 0); frame->tf_ipsw |= PSW_N; } } else { } 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_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; } va = trunc_page(va); if (map->pmap->pm_space != space) { #ifdef TRAPDEBUG printf("trap: space mismatch %d != %d\n", space, map->pmap->pm_space); #endif /* actually dump the user, crap the kernel */ goto dead_end; } /* Never call uvm_fault in interrupt context. */ KASSERT(curcpu()->ci_intr_depth == 0); onfault = pcb->pcb_onfault; pcb->pcb_onfault = 0; ret = uvm_fault(map, va, vftype); pcb->pcb_onfault = onfault; #ifdef TRAPDEBUG printf("uvm_fault(%p, %x, %d)=%d\n", map, (u_int)va, vftype, ret); #endif /* * 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); switch (ret) { case EACCES: ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_ACCERR; break; case ENOMEM: ksi.ksi_signo = SIGKILL; printf("UVM: pid %d (%s), uid %d " "killed: out of swap\n", p->p_pid, p->p_comm, l->l_cred ? kauth_cred_geteuid(l->l_cred) : -1); break; case EINVAL: ksi.ksi_signo = SIGBUS; ksi.ksi_code = BUS_ADRERR; break; default: ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_MAPERR; break; } ksi.ksi_trap = type; ksi.ksi_addr = (void *)va; trapsignal(l, &ksi); } else { if (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); } #ifdef DIAGNOSTIC if (ci->ci_cpl != oldcpl) printf("WARNING: SPL (%d) NOT LOWERED ON TRAP (%d) EXIT\n", ci->ci_cpl, trapnum); #endif if (type & T_USER) userret(l, l->l_md.md_regs); #ifdef DEBUG frame_sanity_check(__func__, __LINE__, type, frame, l); if (frame->tf_flags & TFF_LAST && (curlwp->l_flag & LW_IDLE) == 0) frame_sanity_check(__func__, __LINE__, type, curlwp->l_md.md_regs, curlwp); #endif /* DEBUG */ } void md_child_return(struct lwp *l) { /* * Return values in the frame set by cpu_lwp_fork(). */ userret(l, l->l_md.md_regs); #ifdef DEBUG frame_sanity_check(__func__, __LINE__, 0, l->l_md.md_regs, l); #endif /* DEBUG */ } /* * Process the tail end of a posix_spawn() for the child. */ void cpu_spawn_return(struct lwp *l) { userret(l, l->l_md.md_regs); #ifdef DEBUG frame_sanity_check(__func__, __LINE__, 0, l->l_md.md_regs, l); #endif /* DEBUG */ } #ifdef PTRACE #include 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 original 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... */ if (sstep == 0) { tf->tf_ipsw &= ~PSW_T; return 0; } /* * Don't touch the syscall gateway page. Instead, insert a * breakpoint where we're supposed to return. */ if ((tf->tf_iioq_tail & ~PAGE_MASK) == SYSCALLGATE) l->l_md.md_bpva = tf->tf_r31 & ~HPPA_PC_PRIV_MASK; else l->l_md.md_bpva = tf->tf_iioq_tail & ~HPPA_PC_PRIV_MASK; 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; if ((tf->tf_iioq_tail & ~PAGE_MASK) == SYSCALLGATE) tf->tf_ipsw &= ~PSW_T; else tf->tf_ipsw |= PSW_T; return 0; } #endif void syscall_intern(struct proc *p) { p->p_md.md_syscall = syscall; } /* * 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; size_t nargs64; int nsys, code, error; int tmp; int rval[2]; #ifdef DIAGNOSTIC struct cpu_info *ci = curcpu(); int oldcpl = ci->ci_cpl; #endif curcpu()->ci_data.cpu_nsyscall++; #ifdef DEBUG frame_sanity_check(__func__, __LINE__, 0, frame, curlwp); #endif /* DEBUG */ if (!USERMODE(frame->tf_iioq_head)) panic("syscall"); KASSERT(curlwp != NULL); 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; /* * 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 dest- * ination buffer, but this ends up word-swapping multi-word arguments * (like off_t). * * FIXME - this works only on native binaries and * will probably screw up any and all emulation. * */ if (code < 0 || code >= nsys) callp += p->p_emul->e_nosys; /* bad syscall # */ else callp += code; nargs64 = SYCALL_NARGS64(callp); if (nargs64 != 0) { size_t nargs = callp->sy_narg; for (size_t i = 0; i < nargs + nargs64;) { if (SYCALL_ARG_64_P(callp, i)) { tmp = args[i]; args[i] = args[i + 1]; args[i + 1] = tmp; i += 2; } else i++; } } #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 error = sy_invoke(callp, l, args, rval, code); 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 re- * run reads as: * * ldil L%SYSCALLGATE, r1 * ble 4(srX, 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; } userret(l, frame); #ifdef DIAGNOSTIC if (ci->ci_cpl != oldcpl) { printf("WARNING: SPL (0x%x) NOT LOWERED ON " "syscall(0x%x, 0x%x, 0x%x, 0x%x...) EXIT, PID %d\n", ci->ci_cpl, code, args[0], args[1], args[2], p->p_pid); ci->ci_cpl = oldcpl; } #endif #ifdef DEBUG frame_sanity_check(__func__, __LINE__, 0, frame, l); #endif /* DEBUG */ } /* * Start a new LWP */ void startlwp(void *arg) { ucontext_t *uc = arg; lwp_t *l = curlwp; int error __diagused; error = cpu_setmcontext(l, &uc->uc_mcontext, uc->uc_flags); KASSERT(error == 0); kmem_free(uc, sizeof(ucontext_t)); userret(l, l->l_md.md_regs); }