#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.128 2025/09/19 18:06:37 skrll Exp $");
#include "opt_kgdb.h"
#include "opt_ptrace.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/syscall.h>
#include <sys/syscallvar.h>
#include <sys/mutex.h>
#include <sys/ktrace.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/acct.h>
#include <sys/signal.h>
#include <sys/device.h>
#include <sys/kauth.h>
#include <sys/kmem.h>
#include <sys/userret.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 *);
#define SSBREAKPOINT (HPPA_BREAK_KERNEL | (HPPA_BREAK_SS << 13))
#endif
#if defined(DEBUG) || defined(DIAGNOSTIC)
#define STWM_R1_D_SR0_SP(inst) \
(((inst) & 0xffffc001) == 0x6fc10000 ? (((inst) & 0x00003ff) >> 1) : 0)
#endif
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
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;
do {
l->l_md.md_astpending = 0;
mi_userret(l);
} while (l->l_md.md_astpending);
if (p->p_stflag & PST_PROFIL) {
extern int psratio;
addupc_task(l, tf->tf_iioq_head,
(int)(p->p_sticks - oticks) * psratio);
}
}
#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 (;;) {
handled = 0;
tf_iioq_head_old = frame->tf_iioq_head;
tf_iioq_tail_old = frame->tf_iioq_tail;
#ifdef KGDB
if (!handled)
handled = kgdb_trap(type, frame);
#endif
#ifdef DDB
if (!handled)
handled = kdb_trap(type, code, frame);
#endif
if (!handled)
return(0);
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;
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
frame->tf_iioq_head = frame->tf_iioq_tail;
frame->tf_iioq_tail = frame->tf_iioq_head + 4;
frame->tf_ipsw &= ~(PSW_N|PSW_X|PSW_Y|PSW_Z|PSW_B|PSW_T|PSW_H|PSW_L);
type = T_RECOVERY;
continue;
}
frame->tf_rctr = 0;
}
return (1);
}
}
#else
#define trap_kdebug(t, c, f) (0)
#endif
#if defined(DEBUG) || defined(USERTRACE)
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);
}
static void
user_backtrace(struct trapframe *tf, struct lwp *l, int type)
{
struct proc *p = l->l_proc;
u_int pc, fp, inst;
if (type >= 0)
printf("pid %d (%s) trap #%d\n",
p->p_pid, p->p_comm, type & ~T_USER);
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);
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;
}
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
#ifdef DEBUG
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 ( 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 ((tf->tf_iioq_head & ~PAGE_MASK) == SYSCALLGATE)
goto out;
SANITY(!USERMODE(tf->tf_iioq_head));
SANITY(!USERMODE(tf->tf_iioq_tail));
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
#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(11, 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))
#define PLMASK __BITS(1, 0)
CTASSERT(sizeof(register_t) == sizeof(u_int));
size_t hppa_regmap[] = {
0,
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 (&type >= &emergency_stack_start &&
&type < &emergency_stack_end &&
type != T_IBREAK && STWM_R1_D_SR0_SP(opcode)) {
frame->tf_r3 = frame->tf_r1;
frame->tf_iioq_head = frame->tf_rp;
frame->tf_iioq_tail = frame->tf_iioq_head + 4;
goto dead_end;
}
#endif
#ifdef DEBUG
frame_sanity_check(__func__, __LINE__, type, frame, l);
#endif
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 (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)
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) {
frame->tf_iioq_head = frame->tf_iioq_tail;
frame->tf_iioq_tail = frame->tf_iioq_head + 4;
frame->tf_ipsw &= ~(PSW_N|PSW_X|PSW_Y|PSW_Z|PSW_B|PSW_T|PSW_H|PSW_L);
continue;
}
break;
}
frame->tf_rctr = 0;
break;
#endif
case T_RECOVERY:
#if !defined(DDB) && !defined(KGDB)
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;
}
#ifdef DIAGNOSTIC
case T_PRIV_OP:
case T_PRIV_REG:
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
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);
trapsignal(l, &ksi);
break;
#endif
case T_EXCEPTION | T_USER: {
uint64_t *fpp;
uint32_t *pex, ex, inst;
int i;
hppa_fpu_flush(l);
fpp = (uint64_t *)pcb->pcb_fpregs;
pex = (uint32_t *)&fpp[0];
pex++;
for (i = 1; i < 8 && !*pex; i++, pex++)
;
KASSERT(i < 8);
ex = *pex;
*pex = 0;
fpp[0] &= ~(((uint64_t)HPPA_FPU_T) << 32);
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;
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;
}
if (!user && space == HPPA_SID_KERNEL)
map = kernel_map;
else {
map = &vm->vm_map;
}
va = trunc_page(va);
if ((opcode & LPA_MASK) == LPA) {
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, PROBE_PL) & PLMASK;
} else {
const u_int plreg =
__SHIFTOUT(opcode, PROBE_PL);
pl = tf_getregno(frame, plreg) & PLMASK;
}
bool ok = true;
if ((user && space == HPPA_SID_KERNEL) ||
(frame->tf_iioq_head & HPPA_PC_PRIV_MASK) != pl ||
(user && va >= VM_MAXUSER_ADDRESS)) {
ok = false;
} else {
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;
}
}
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;
}
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
goto dead_end;
}
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 (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 ) {
break;
}
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
}
void
md_child_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
}
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
}
#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)
{
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);
if (sstep == 0) {
tf->tf_ipsw &= ~PSW_T;
return 0;
}
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;
}
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
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;
frame->tf_arg0 = args[0];
frame->tf_arg1 = args[1];
frame->tf_arg2 = args[2];
frame->tf_arg3 = args[3];
switch (code) {
case SYS_syscall:
code = *args;
args += 1;
break;
case SYS___syscall:
if (callp != sysent)
break;
code = *args;
args += 2;
}
if (code < 0 || code >= nsys)
callp += p->p_emul->e_nosys;
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;
frame = l->l_md.md_regs;
frame->tf_ret0 = rval[0];
frame->tf_ret1 = rval[1];
frame->tf_t1 = 0;
break;
case ERESTART:
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
}
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);
}