/* $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.
*/
/*
* 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.
*/
#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 */
#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);
/*
* 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;
#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;
#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=<bad address>", 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);
}
/*
* 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();
/*
* 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;
/*
* 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
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 */
/* 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);
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;
/*
* 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;
}
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
/*
* 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;
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;
}