From 665a4c992b819cc10c913d3822e272fa43154e35 Mon Sep 17 00:00:00 2001 From: benno Date: Sun, 19 May 2002 08:16:25 +0000 Subject: Make this more FreeBSD-ish. Requested by: jhb --- sys/powerpc/aim/trap.c | 750 +++++++++++++++++++++++++-------------------- sys/powerpc/powerpc/trap.c | 750 +++++++++++++++++++++++++-------------------- 2 files changed, 826 insertions(+), 674 deletions(-) (limited to 'sys/powerpc') diff --git a/sys/powerpc/aim/trap.c b/sys/powerpc/aim/trap.c index 5f1d8a4..8310a98 100644 --- a/sys/powerpc/aim/trap.c +++ b/sys/powerpc/aim/trap.c @@ -40,18 +40,21 @@ static const char rcsid[] = #include "opt_ktrace.h" #include +#include +#include #include #include -#include +#include #include #include -#include #include +#include #include #include #ifdef KTRACE #include #endif +#include #include #include @@ -81,13 +84,18 @@ static const char rcsid[] = extern int intr_depth; #endif -void *syscall = NULL; /* XXX dummy symbol for emul_netbsd */ +void trap(struct trapframe *); + +static void trap_fatal(struct trapframe *frame); +static void printtrap(u_int vector, struct trapframe *frame, int isfatal, + int user); +static int trap_pfault(struct trapframe *frame, int user); +static int fix_unaligned(struct thread *td, struct trapframe *frame); +static int handle_onfault(struct trapframe *frame); +static void syscall(struct trapframe *frame); -static int fix_unaligned(struct thread *td, - struct trapframe *frame); static __inline void setusr(u_int); -void trap(struct trapframe *); /* Called from locore / trap_subr */ int setfault(faultbuf); /* defined in locore.S */ /* Why are these not defined in a header? */ @@ -99,391 +107,459 @@ int kcopy(const void *, void *, size_t); extern char *syscallnames[]; #endif +struct powerpc_exception { + u_int vector; + char *name; +}; + +static struct powerpc_exception powerpc_exceptions[] = { + { 0x0100, "system reset" }, + { 0x0200, "machine check" }, + { 0x0300, "data storage interrupt" }, + { 0x0400, "instruction storage interrupt" }, + { 0x0500, "external interrupt" }, + { 0x0600, "alignment" }, + { 0x0700, "program" }, + { 0x0800, "floating-point unavailable" }, + { 0x0900, "decrementer" }, + { 0x0c00, "system call" }, + { 0x0d00, "trace" }, + { 0x0e00, "floating-point assist" }, + { 0x0f00, "performance monitoring" }, + { 0x0f20, "altivec unavailable" }, + { 0x1000, "instruction tlb miss" }, + { 0x1100, "data load tlb miss" }, + { 0x1200, "data store tlb miss" }, + { 0x1300, "instruction breakpoint" }, + { 0x1400, "system management" }, + { 0x1600, "altivec assist" }, + { 0x1700, "thermal management" }, + { 0x2000, "run mode/trace" }, + { 0x3000, NULL } +}; + +static const char * +trapname(u_int vector) +{ + struct powerpc_exception *pe; + + for (pe = powerpc_exceptions; pe->vector != 0x3000; pe++) { + if (pe->vector == vector) + return (pe->name); + } + + return ("unknown"); +} + void trap(struct trapframe *frame) { struct thread *td, *fputhread; struct proc *p; - int type, ftype, rv; + int sig, type, user; + u_int sticks, ucode; + + atomic_add_int(&cnt.v_trap, 1); td = PCPU_GET(curthread); p = td->td_proc; - type = frame->exc; - if (frame->srr1 & PSL_PR) - type |= EXC_USER; + type = ucode = frame->exc; + sig = 0; + user = frame->srr1 & PSL_PR; + sticks = 0; + + CTR3(KTR_TRAP, "trap: %s type=%s (%s)", p->p_comm, + trapname(type), user ? "user" : "kernel"); + + if (user) { + sticks = td->td_kse->ke_sticks; + td->td_frame = frame; + if (td->td_ucred != p->p_ucred) + cred_update_thread(td); + + /* User Mode Traps */ + switch (type) { + case EXC_RUNMODETRC: + case EXC_TRC: + frame->srr1 &= ~PSL_SE; + sig = SIGTRAP; + break; -#ifdef DIAGNOSTIC - if (curpcb->pcb_pmreal != curpm) - panic("trap: curpm (%p) != curpcb->pcb_pmreal (%p)", - curpm, curpcb->pcb_pmreal); -#endif + case EXC_DSI: + case EXC_ISI: + sig = trap_pfault(frame, 1); + break; - switch (type) { - case EXC_RUNMODETRC|EXC_USER: - /* FALLTHROUGH */ - case EXC_TRC|EXC_USER: - PROC_LOCK(p); - frame->srr1 &= ~PSL_SE; - trapsignal(p, SIGTRAP, EXC_TRC); - PROC_UNLOCK(p); - break; - case EXC_DSI: { - faultbuf *fb; - /* - * Only query UVM if no interrupts are active (this applies - * "on-fault" as well. - */ - if (intr_depth < 0) { - struct vm_map *map; - vm_offset_t va; + case EXC_SC: + syscall(frame); + break; - map = kernel_map; - va = frame->dar; - if ((va >> ADDR_SR_SHFT) == USER_SR) { - register_t user_sr; - - __asm ("mfsr %0, %1" - : "=r"(user_sr) : "K"(USER_SR)); - va &= ADDR_PIDX | ADDR_POFF; - va |= user_sr << ADDR_SR_SHFT; - /* KERNEL_PROC_LOCK(p); XXX */ - map = &p->p_vmspace->vm_map; + case EXC_FPU: + if ((fputhread = PCPU_GET(fputhread)) != NULL) { + KASSERT(fputhread != td, + ("floating-point already enabled")); + save_fpu(fputhread); + } + PCPU_SET(fputhread, td); + td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid); + enable_fpu(td); + frame->srr1 |= PSL_FP; + break; + +#ifdef ALTIVEC + case EXC_VEC: + if ((vecthread = PCPU_GET(vecthread)) != NULL) { + KASSERT(vecthread != td, + ("altivec already enabled")); + save_vec(vecthread); } - if (frame->dsisr & DSISR_STORE) - ftype = VM_PROT_WRITE; + PCPU_SET(vecthread, td); + td->td_pcb->pcb_veccpu = PCPU_GET(cpuid); + enable_vec(td); + frame->srr1 |= PSL_VEC; + break; +#endif /* ALTIVEC */ + + case EXC_ALI: + if (fix_unaligned(td, frame) != 0) + sig = SIGBUS; else - ftype = VM_PROT_READ; - rv = vm_fault(map, trunc_page(va), ftype, - VM_FAULT_NORMAL); - if (rv == 0) - return; - if (rv == EACCES) - rv = EFAULT; - } else { - rv = EFAULT; + frame->srr0 += 4; + break; + + case EXC_PGM: + /* XXX temporarily */ + /* XXX: Magic Number? */ + if (frame->srr1 & 0x0002000) + sig = SIGTRAP; + else + sig = SIGILL; + break; + + default: + trap_fatal(frame); } - if ((fb = td->td_pcb->pcb_onfault) != NULL) { - frame->srr0 = (*fb)[0]; - frame->fixreg[1] = (*fb)[1]; - frame->fixreg[2] = (*fb)[2]; - frame->fixreg[3] = rv; - frame->cr = (*fb)[3]; - memcpy(&frame->fixreg[13], &(*fb)[4], - 19 * sizeof(register_t)); - return; + } else { + /* Kernel Mode Traps */ + + KASSERT(cold || td->td_ucred != NULL, + ("kernel trap doesn't have ucred")); + switch (type) { + case EXC_DSI: + if (trap_pfault(frame, 0) == 0) + return; + break; + case EXC_MCHK: + if (handle_onfault(frame)) + return; + break; + default: + trap_fatal(frame); } - printf("trap: kernel %s DSI @ %#x by %#x (DSISR %#x, err=%d)\n", - (frame->dsisr & DSISR_STORE) ? "write" : "read", - frame->dar, frame->srr0, frame->dsisr, rv); - goto brain_damage2; } - case EXC_DSI|EXC_USER: - PROC_LOCK(p); - ++p->p_lock; - PROC_UNLOCK(p); - if (frame->dsisr & DSISR_STORE) - ftype = VM_PROT_WRITE; - else - ftype = VM_PROT_READ; - rv = vm_fault(&p->p_vmspace->vm_map, trunc_page(frame->dar), - ftype, VM_FAULT_NORMAL); - printf("trap: pid %d (%s): user %s DSI @ %#x " - "by %#x (DSISR %#x, err=%d)\n", - p->p_pid, p->p_comm, - (frame->dsisr & DSISR_STORE) ? "write" : "read", - frame->dar, frame->srr0, frame->dsisr, rv); - if (rv == ENOMEM) { - printf("UVM: pid %d (%s), uid %d killed: " - "out of swap\n", - p->p_pid, p->p_comm, - td->td_ucred ? td->td_ucred->cr_uid : -1); - trapsignal(p, SIGKILL, EXC_DSI); - } else { - trapsignal(p, SIGSEGV, EXC_DSI); - } - PROC_LOCK(p); - --p->p_lock; - PROC_UNLOCK(p); + + if (td != PCPU_GET(fputhread) || + td->td_pcb->pcb_fpcpu != PCPU_GET(cpuid)) + frame->srr1 &= ~PSL_FP; + +#ifdef ALTIVEC + if (td != PCPU_GET(vecthread) || + td->td_pcb->pcb_veccpu != PCPU_GET(cpuid)) + frame->srr1 &= ~PSL_VEC; +#endif /* ALTIVEC */ + + if (sig != 0) { + if (p->p_sysent->sv_transtrap != NULL) + sig = (p->p_sysent->sv_transtrap)(sig, type); + trapsignal(p, sig, ucode); + } + + userret(td, frame, sticks); + mtx_assert(&Giant, MA_NOTOWNED); +#ifdef DIAGNOSTIC + cred_free_thread(td); +#endif /* DIAGNOSTIC */ +} + +static void +trap_fatal(struct trapframe *frame) +{ + + printtrap(frame->exc, frame, 1, (frame->srr1 & PSL_PR)); +#ifdef DDB + if ((debugger_on_panic || db_active) && kdb_trap(frame->exc, 0, frame)) + return; +#endif + panic("%s trap", trapname(frame->exc)); +} + +static void +printtrap(u_int vector, struct trapframe *frame, int isfatal, int user) +{ + + printf("\n"); + printf("%s %s trap:\n", isfatal ? "fatal" : "handled", + user ? "user" : "kernel"); + printf("\n"); + printf(" exception = 0x%x (%s)\n", vector >> 8, + trapname(vector)); + switch (vector) { + case EXC_DSI: + printf(" virtual address = 0x%x\n", frame->dar); break; case EXC_ISI: - printf("trap: kernel ISI by %#x (SRR1 %#x)\n", - frame->srr0, frame->srr1); - goto brain_damage2; - case EXC_ISI|EXC_USER: - PROC_LOCK(p); - ++p->p_lock; - PROC_UNLOCK(p); - ftype = VM_PROT_READ | VM_PROT_EXECUTE; - rv = vm_fault(&p->p_vmspace->vm_map, trunc_page(frame->srr0), - ftype, VM_FAULT_NORMAL); - if (rv == 0) { - PROC_LOCK(p); - --p->p_lock; - PROC_UNLOCK(p); - break; - } - printf("trap: pid %d (%s): user ISI trap @ %#x " - "(SSR1=%#x)\n", - p->p_pid, p->p_comm, frame->srr0, frame->srr1); - trapsignal(p, SIGSEGV, EXC_ISI); - PROC_LOCK(p); - --p->p_lock; - PROC_UNLOCK(p); + printf(" virtual address = 0x%x\n", frame->srr0); break; - case EXC_SC|EXC_USER: - { - const struct sysent *callp; - size_t argsize; - register_t code, error; - register_t *params, rval[2]; - int n; - register_t args[10]; - - code = frame->fixreg[0]; - callp = &p->p_sysent->sv_table[0]; - params = frame->fixreg + FIRSTARG; - n = NARGREG; - - switch (code) { - case SYS_syscall: - /* - * code is first argument, - * followed by actual args. - */ - code = *params++; - n -= 1; - break; - case SYS___syscall: - params++; - code = *params++; - n -= 2; - break; - default: - break; - } + } + printf(" srr0 = 0x%x", frame->srr0); + printf(" curthread = %p\n", curthread); + if (curthread != NULL) + printf(" pid = %d, comm = %s\n", + curthread->td_proc->p_pid, curthread->td_proc->p_comm); + printf("\n"); +} - if (p->p_sysent->sv_mask) - code &= p->p_sysent->sv_mask; - callp += code; - argsize = callp->sy_narg & SYF_ARGMASK; - - if (argsize > n * sizeof(register_t)) { - memcpy(args, params, n * sizeof(register_t)); - error = copyin(MOREARGS(frame->fixreg[1]), - args + n, - argsize - n * sizeof(register_t)); - if (error) - goto syscall_bad; - params = args; - } +/* + * Handles a fatal fault when we have onfault state to recover. Returns + * non-zero if there was onfault recovery state available. + */ +static int +handle_onfault(struct trapframe *frame) +{ + struct thread *td; + faultbuf *fb; + + td = curthread; + fb = td->td_pcb->pcb_onfault; + if (fb != NULL) { + frame->srr0 = (*fb)[0]; + frame->fixreg[1] = (*fb)[1]; + frame->fixreg[2] = (*fb)[2]; + frame->cr = (*fb)[3]; + bcopy(&(*fb)[4], &frame->fixreg[13], + 19 * sizeof(register_t)); + return (1); + } + return (0); +} - /* - * Try to run the syscall without Giant if the syscall - * is MP safe. - */ - if ((callp->sy_narg & SYF_MPSAFE) == 0) - mtx_lock(&Giant); +void +syscall(struct trapframe *frame) +{ + caddr_t params; + struct sysent *callp; + struct thread *td; + struct proc *p; + int error, n; + size_t narg; + register_t args[10]; + u_int code; -#ifdef KTRACE - if (KTRPOINT(p, KTR_SYSCALL)) - ktrsyscall(p, code, argsize, params); -#endif + td = PCPU_GET(curthread); + p = td->td_proc; - rval[0] = 0; - rval[1] = 0; - - error = (*callp->sy_call)(td, params); - switch (error) { - case 0: - frame->fixreg[FIRSTARG] = rval[0]; - frame->fixreg[FIRSTARG + 1] = rval[1]; - frame->cr &= ~0x10000000; - break; - case ERESTART: - /* - * Set user's pc back to redo the system call. - */ - frame->srr0 -= 4; - break; - case EJUSTRETURN: - /* nothing to do */ - break; - default: -syscall_bad: -#if 0 - if (p->p_emul->e_errno) - error = p->p_emul->e_errno[error]; -#endif - frame->fixreg[FIRSTARG] = error; - frame->cr |= 0x10000000; - break; - } + atomic_add_int(&cnt.v_syscall, 1); + + code = frame->fixreg[0]; + params = (caddr_t)(frame->fixreg + FIRSTARG); + n = NARGREG; + + if (p->p_sysent->sv_prepsyscall) { + /* + * The prep code is MP aware. + */ + (*p->p_sysent->sv_prepsyscall)(frame, args, &code, ¶ms); + } else if (code == SYS_syscall) { + /* + * code is first argument, + * followed by actual args. + */ + code = *params++; + n -= 1; + } else if (code == SYS___syscall) { + /* + * Like syscall, but code is a quad, + * so as to maintain quad alignment + * for the rest of the args. + */ + params++; + code = *params++; + n -= 2; + } + + if (p->p_sysent->sv_mask) + code &= p->p_sysent->sv_mask; + + if (code >= p->p_sysent->sv_size) + callp = &p->p_sysent->sv_table[0]; + else + callp = &p->p_sysent->sv_table[code]; + + narg = callp->sy_narg & SYF_ARGMASK; - /* - * Release Giant if we had to get it. Don't use - * mtx_owned(), we want to catch broken syscalls. - */ - if ((callp->sy_narg & SYF_MPSAFE) == 0) - mtx_unlock(&Giant); + if (narg > n * sizeof(register_t)) { + bcopy(params, args, n * sizeof(register_t)); + error = copyin(MOREARGS(frame->fixreg[1]), args + n, + narg - n * sizeof(register_t)); + if (error) { #ifdef KTRACE - if (KTRPOINT(p, KTR_SYSRET)) - ktrsysret(p, code, error, rval[0]); + /* Can't get all the arguments! */ + if (KTRPOINT(p, KTR_SYSCALL)) + ktrsyscall(p->p_tracep, code, narg, args); #endif + goto bad; } - break; + params = (caddr_t)args; + } - case EXC_FPU|EXC_USER: - if ((fputhread = PCPU_GET(fputhread)) != NULL) { - KASSERT(fputhread != td, - ("floating-point already enabled")); - save_fpu(fputhread); - } - PCPU_SET(fputhread, td); - td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid); - enable_fpu(td); - frame->srr1 |= PSL_FP; - break; + /* + * Try to run the syscall without Giant if the syscall is MP safe. + */ + if ((callp->sy_narg & SYF_MPSAFE) == 0) + mtx_lock(&Giant); -#ifdef ALTIVEC - case EXC_VEC|EXC_USER: - if (vecproc) { - save_vec(vecproc); - } - vecproc = p; - enable_vec(p); - break; +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSCALL)) + ktrsyscall(p->p_tracep, code, narg, params); #endif - -#if 0 - case EXC_AST|EXC_USER: - astpending = 0; /* we are about to do it */ - PROC_LOCK(p); - if (p->p_flag & P_OWEUPC) { - p->p_flag &= ~P_OWEUPC; - ADDUPROF(p); - } - /* Check whether we are being preempted. */ - if (want_resched) - mi_switch(); - PROC_UNLOCK(p); + td->td_retval[0] = 0; + td->td_retval[1] = frame->fixreg[FIRSTARG + 1]; + + STOPEVENT(p, S_SCE, narg); + + error = (*callp->sy_call)(td, params); + switch (error) { + case 0: + frame->fixreg[FIRSTARG] = td->td_retval[0]; + frame->fixreg[FIRSTARG + 1] = td->td_retval[1]; + /* XXX: Magic number */ + frame->cr &= ~0x10000000; break; -#endif - - case EXC_ALI|EXC_USER: - PROC_LOCK(p); - if (fix_unaligned(td, frame) != 0) { - printf("trap: pid %d (%s): user ALI trap @ %#x " - "(SSR1=%#x)\n", - p->p_pid, p->p_comm, frame->srr0, - frame->srr1); - trapsignal(p, SIGBUS, EXC_ALI); - } else - frame->srr0 += 4; - PROC_UNLOCK(p); + case ERESTART: + /* + * Set user's pc back to redo the system call. + */ + frame->srr0 -= 4; break; - - case EXC_PGM|EXC_USER: -/* XXX temporarily */ - PROC_LOCK(p); - printf("trap: pid %d (%s): user PGM trap @ %#x " - "(SSR1=%#x)\n", - p->p_pid, p->p_comm, frame->srr0, frame->srr1); - if (frame->srr1 & 0x00020000) /* Bit 14 is set if trap */ - trapsignal(p, SIGTRAP, EXC_PGM); - else - trapsignal(p, SIGILL, EXC_PGM); - PROC_UNLOCK(p); + case EJUSTRETURN: + /* nothing to do */ break; - - case EXC_MCHK: { - faultbuf *fb; - - if ((fb = td->td_pcb->pcb_onfault) != NULL) { - frame->srr0 = (*fb)[0]; - frame->fixreg[1] = (*fb)[1]; - frame->fixreg[2] = (*fb)[2]; - frame->fixreg[3] = EFAULT; - frame->cr = (*fb)[3]; - memcpy(&frame->fixreg[13], &(*fb)[4], - 19 * sizeof(register_t)); - return; + default: +bad: + if (p->p_sysent->sv_errsize) { + if (error >= p->p_sysent->sv_errsize) + error = -1; /* XXX */ + else + error = p->p_sysent->sv_errtbl[error]; } - goto brain_damage; + frame->fixreg[FIRSTARG] = error; + /* XXX: Magic number: Carry Flag Equivalent? */ + frame->cr |= 0x10000000; + break; } - default: -brain_damage: - printf("trap type %x at %x\n", type, frame->srr0); -brain_damage2: -#ifdef DDBX - if (kdb_trap(type, frame)) - return; -#endif -#ifdef TRAP_PANICWAIT - printf("Press a key to panic.\n"); - cnpollc(1); - cngetc(); - cnpollc(0); + +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSRET)) + ktrsysret(p->p_tracep, code, error, td->td_retval[0]); #endif - panic("trap"); - } -#if 0 - /* Take pending signals. */ - { - int sig; - - while ((sig = CURSIG(p)) != 0) - postsig(sig); - } -#endif + if ((callp->sy_narg & SYF_MPSAFE) == 0) + mtx_unlock(&Giant); /* - * If someone stole the fp or vector unit while we were away, - * disable it + * Does the comment in the i386 code about errno apply here? */ - if (td != PCPU_GET(fputhread) || - td->td_pcb->pcb_fpcpu != PCPU_GET(cpuid)) - frame->srr1 &= ~PSL_FP; -#ifdef ALTIVEC - if (p != vecproc) - frame->srr1 &= ~PSL_VEC; -#endif + STOPEVENT(p, S_SCX, code); -#if 0 - p->p_priority = p->p_usrpri; +#ifdef WITNESS + if (witness_list(td)) { + panic("system call %s returning with mutex(s) held\n", + syscallnames[code]); + } #endif + mtx_assert(&sched_lock, MA_NOTOWNED); + mtx_assert(&Giant, MA_NOTOWNED); } -void child_return(void *); - -void -child_return(void *arg) +static int +trap_pfault(struct trapframe *frame, int user) { - struct thread *td; - struct proc *p; - struct trapframe *tf; + vm_offset_t eva, va; + struct thread *td; + struct proc *p; + vm_map_t map; + vm_prot_t ftype; + int rv; + u_int user_sr; - td = (struct thread *)arg; + td = curthread; p = td->td_proc; - tf = trapframe(td); + if (frame->exc == EXC_ISI) { + eva = frame->srr0; + ftype = VM_PROT_READ | VM_PROT_EXECUTE; + } else { + eva = frame->dar; + if (frame->dsisr & DSISR_STORE) + ftype = VM_PROT_READ | VM_PROT_WRITE; + else + ftype = VM_PROT_READ; + } - PROC_UNLOCK(p); + if (user) { + map = &p->p_vmspace->vm_map; + } else { + if ((eva >> ADDR_SR_SHFT) == USER_SR) { + if (p->p_vmspace == NULL) + return (SIGSEGV); + + __asm ("mfsr %0, %1" + : "=r"(user_sr) + : "K"(USER_SR)); + eva &= ADDR_PIDX | ADDR_POFF; + eva |= user_sr << ADDR_SR_SHFT; + map = &p->p_vmspace->vm_map; + } else { + map = kernel_map; + } + } + va = trunc_page(eva); - tf->fixreg[FIRSTARG] = 0; - tf->fixreg[FIRSTARG + 1] = 1; - tf->cr &= ~0x10000000; - tf->srr1 &= ~(PSL_FP|PSL_VEC); /* Disable FP & AltiVec, as we can't - be them. */ - td->td_pcb->pcb_fpcpu = NULL; -#ifdef KTRACE - if (KTRPOINT(p, KTR_SYSRET)) { + mtx_lock(&Giant); + if (map != kernel_map) { + /* + * Keep swapout from messing with us during this + * critical time. + */ PROC_LOCK(p); - ktrsysret(p, SYS_fork, 0, 0); + ++p->p_lock; PROC_UNLOCK(p); + + /* Fault in the user page: */ + rv = vm_fault(map, va, ftype, + (ftype & VM_PROT_WRITE) ? VM_FAULT_DIRTY + : VM_FAULT_NORMAL); + + PROC_LOCK(p); + --p->p_lock; + PROC_UNLOCK(p); + } else { + /* + * Don't have to worry about process locking or stacks in the + * kernel. + */ + rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL); } -#endif + mtx_unlock(&Giant); + + if (rv == KERN_SUCCESS) + return (0); + + if (!user && handle_onfault(frame)) + return (0); + + return (SIGSEGV); } static __inline void diff --git a/sys/powerpc/powerpc/trap.c b/sys/powerpc/powerpc/trap.c index 5f1d8a4..8310a98 100644 --- a/sys/powerpc/powerpc/trap.c +++ b/sys/powerpc/powerpc/trap.c @@ -40,18 +40,21 @@ static const char rcsid[] = #include "opt_ktrace.h" #include +#include +#include #include #include -#include +#include #include #include -#include #include +#include #include #include #ifdef KTRACE #include #endif +#include #include #include @@ -81,13 +84,18 @@ static const char rcsid[] = extern int intr_depth; #endif -void *syscall = NULL; /* XXX dummy symbol for emul_netbsd */ +void trap(struct trapframe *); + +static void trap_fatal(struct trapframe *frame); +static void printtrap(u_int vector, struct trapframe *frame, int isfatal, + int user); +static int trap_pfault(struct trapframe *frame, int user); +static int fix_unaligned(struct thread *td, struct trapframe *frame); +static int handle_onfault(struct trapframe *frame); +static void syscall(struct trapframe *frame); -static int fix_unaligned(struct thread *td, - struct trapframe *frame); static __inline void setusr(u_int); -void trap(struct trapframe *); /* Called from locore / trap_subr */ int setfault(faultbuf); /* defined in locore.S */ /* Why are these not defined in a header? */ @@ -99,391 +107,459 @@ int kcopy(const void *, void *, size_t); extern char *syscallnames[]; #endif +struct powerpc_exception { + u_int vector; + char *name; +}; + +static struct powerpc_exception powerpc_exceptions[] = { + { 0x0100, "system reset" }, + { 0x0200, "machine check" }, + { 0x0300, "data storage interrupt" }, + { 0x0400, "instruction storage interrupt" }, + { 0x0500, "external interrupt" }, + { 0x0600, "alignment" }, + { 0x0700, "program" }, + { 0x0800, "floating-point unavailable" }, + { 0x0900, "decrementer" }, + { 0x0c00, "system call" }, + { 0x0d00, "trace" }, + { 0x0e00, "floating-point assist" }, + { 0x0f00, "performance monitoring" }, + { 0x0f20, "altivec unavailable" }, + { 0x1000, "instruction tlb miss" }, + { 0x1100, "data load tlb miss" }, + { 0x1200, "data store tlb miss" }, + { 0x1300, "instruction breakpoint" }, + { 0x1400, "system management" }, + { 0x1600, "altivec assist" }, + { 0x1700, "thermal management" }, + { 0x2000, "run mode/trace" }, + { 0x3000, NULL } +}; + +static const char * +trapname(u_int vector) +{ + struct powerpc_exception *pe; + + for (pe = powerpc_exceptions; pe->vector != 0x3000; pe++) { + if (pe->vector == vector) + return (pe->name); + } + + return ("unknown"); +} + void trap(struct trapframe *frame) { struct thread *td, *fputhread; struct proc *p; - int type, ftype, rv; + int sig, type, user; + u_int sticks, ucode; + + atomic_add_int(&cnt.v_trap, 1); td = PCPU_GET(curthread); p = td->td_proc; - type = frame->exc; - if (frame->srr1 & PSL_PR) - type |= EXC_USER; + type = ucode = frame->exc; + sig = 0; + user = frame->srr1 & PSL_PR; + sticks = 0; + + CTR3(KTR_TRAP, "trap: %s type=%s (%s)", p->p_comm, + trapname(type), user ? "user" : "kernel"); + + if (user) { + sticks = td->td_kse->ke_sticks; + td->td_frame = frame; + if (td->td_ucred != p->p_ucred) + cred_update_thread(td); + + /* User Mode Traps */ + switch (type) { + case EXC_RUNMODETRC: + case EXC_TRC: + frame->srr1 &= ~PSL_SE; + sig = SIGTRAP; + break; -#ifdef DIAGNOSTIC - if (curpcb->pcb_pmreal != curpm) - panic("trap: curpm (%p) != curpcb->pcb_pmreal (%p)", - curpm, curpcb->pcb_pmreal); -#endif + case EXC_DSI: + case EXC_ISI: + sig = trap_pfault(frame, 1); + break; - switch (type) { - case EXC_RUNMODETRC|EXC_USER: - /* FALLTHROUGH */ - case EXC_TRC|EXC_USER: - PROC_LOCK(p); - frame->srr1 &= ~PSL_SE; - trapsignal(p, SIGTRAP, EXC_TRC); - PROC_UNLOCK(p); - break; - case EXC_DSI: { - faultbuf *fb; - /* - * Only query UVM if no interrupts are active (this applies - * "on-fault" as well. - */ - if (intr_depth < 0) { - struct vm_map *map; - vm_offset_t va; + case EXC_SC: + syscall(frame); + break; - map = kernel_map; - va = frame->dar; - if ((va >> ADDR_SR_SHFT) == USER_SR) { - register_t user_sr; - - __asm ("mfsr %0, %1" - : "=r"(user_sr) : "K"(USER_SR)); - va &= ADDR_PIDX | ADDR_POFF; - va |= user_sr << ADDR_SR_SHFT; - /* KERNEL_PROC_LOCK(p); XXX */ - map = &p->p_vmspace->vm_map; + case EXC_FPU: + if ((fputhread = PCPU_GET(fputhread)) != NULL) { + KASSERT(fputhread != td, + ("floating-point already enabled")); + save_fpu(fputhread); + } + PCPU_SET(fputhread, td); + td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid); + enable_fpu(td); + frame->srr1 |= PSL_FP; + break; + +#ifdef ALTIVEC + case EXC_VEC: + if ((vecthread = PCPU_GET(vecthread)) != NULL) { + KASSERT(vecthread != td, + ("altivec already enabled")); + save_vec(vecthread); } - if (frame->dsisr & DSISR_STORE) - ftype = VM_PROT_WRITE; + PCPU_SET(vecthread, td); + td->td_pcb->pcb_veccpu = PCPU_GET(cpuid); + enable_vec(td); + frame->srr1 |= PSL_VEC; + break; +#endif /* ALTIVEC */ + + case EXC_ALI: + if (fix_unaligned(td, frame) != 0) + sig = SIGBUS; else - ftype = VM_PROT_READ; - rv = vm_fault(map, trunc_page(va), ftype, - VM_FAULT_NORMAL); - if (rv == 0) - return; - if (rv == EACCES) - rv = EFAULT; - } else { - rv = EFAULT; + frame->srr0 += 4; + break; + + case EXC_PGM: + /* XXX temporarily */ + /* XXX: Magic Number? */ + if (frame->srr1 & 0x0002000) + sig = SIGTRAP; + else + sig = SIGILL; + break; + + default: + trap_fatal(frame); } - if ((fb = td->td_pcb->pcb_onfault) != NULL) { - frame->srr0 = (*fb)[0]; - frame->fixreg[1] = (*fb)[1]; - frame->fixreg[2] = (*fb)[2]; - frame->fixreg[3] = rv; - frame->cr = (*fb)[3]; - memcpy(&frame->fixreg[13], &(*fb)[4], - 19 * sizeof(register_t)); - return; + } else { + /* Kernel Mode Traps */ + + KASSERT(cold || td->td_ucred != NULL, + ("kernel trap doesn't have ucred")); + switch (type) { + case EXC_DSI: + if (trap_pfault(frame, 0) == 0) + return; + break; + case EXC_MCHK: + if (handle_onfault(frame)) + return; + break; + default: + trap_fatal(frame); } - printf("trap: kernel %s DSI @ %#x by %#x (DSISR %#x, err=%d)\n", - (frame->dsisr & DSISR_STORE) ? "write" : "read", - frame->dar, frame->srr0, frame->dsisr, rv); - goto brain_damage2; } - case EXC_DSI|EXC_USER: - PROC_LOCK(p); - ++p->p_lock; - PROC_UNLOCK(p); - if (frame->dsisr & DSISR_STORE) - ftype = VM_PROT_WRITE; - else - ftype = VM_PROT_READ; - rv = vm_fault(&p->p_vmspace->vm_map, trunc_page(frame->dar), - ftype, VM_FAULT_NORMAL); - printf("trap: pid %d (%s): user %s DSI @ %#x " - "by %#x (DSISR %#x, err=%d)\n", - p->p_pid, p->p_comm, - (frame->dsisr & DSISR_STORE) ? "write" : "read", - frame->dar, frame->srr0, frame->dsisr, rv); - if (rv == ENOMEM) { - printf("UVM: pid %d (%s), uid %d killed: " - "out of swap\n", - p->p_pid, p->p_comm, - td->td_ucred ? td->td_ucred->cr_uid : -1); - trapsignal(p, SIGKILL, EXC_DSI); - } else { - trapsignal(p, SIGSEGV, EXC_DSI); - } - PROC_LOCK(p); - --p->p_lock; - PROC_UNLOCK(p); + + if (td != PCPU_GET(fputhread) || + td->td_pcb->pcb_fpcpu != PCPU_GET(cpuid)) + frame->srr1 &= ~PSL_FP; + +#ifdef ALTIVEC + if (td != PCPU_GET(vecthread) || + td->td_pcb->pcb_veccpu != PCPU_GET(cpuid)) + frame->srr1 &= ~PSL_VEC; +#endif /* ALTIVEC */ + + if (sig != 0) { + if (p->p_sysent->sv_transtrap != NULL) + sig = (p->p_sysent->sv_transtrap)(sig, type); + trapsignal(p, sig, ucode); + } + + userret(td, frame, sticks); + mtx_assert(&Giant, MA_NOTOWNED); +#ifdef DIAGNOSTIC + cred_free_thread(td); +#endif /* DIAGNOSTIC */ +} + +static void +trap_fatal(struct trapframe *frame) +{ + + printtrap(frame->exc, frame, 1, (frame->srr1 & PSL_PR)); +#ifdef DDB + if ((debugger_on_panic || db_active) && kdb_trap(frame->exc, 0, frame)) + return; +#endif + panic("%s trap", trapname(frame->exc)); +} + +static void +printtrap(u_int vector, struct trapframe *frame, int isfatal, int user) +{ + + printf("\n"); + printf("%s %s trap:\n", isfatal ? "fatal" : "handled", + user ? "user" : "kernel"); + printf("\n"); + printf(" exception = 0x%x (%s)\n", vector >> 8, + trapname(vector)); + switch (vector) { + case EXC_DSI: + printf(" virtual address = 0x%x\n", frame->dar); break; case EXC_ISI: - printf("trap: kernel ISI by %#x (SRR1 %#x)\n", - frame->srr0, frame->srr1); - goto brain_damage2; - case EXC_ISI|EXC_USER: - PROC_LOCK(p); - ++p->p_lock; - PROC_UNLOCK(p); - ftype = VM_PROT_READ | VM_PROT_EXECUTE; - rv = vm_fault(&p->p_vmspace->vm_map, trunc_page(frame->srr0), - ftype, VM_FAULT_NORMAL); - if (rv == 0) { - PROC_LOCK(p); - --p->p_lock; - PROC_UNLOCK(p); - break; - } - printf("trap: pid %d (%s): user ISI trap @ %#x " - "(SSR1=%#x)\n", - p->p_pid, p->p_comm, frame->srr0, frame->srr1); - trapsignal(p, SIGSEGV, EXC_ISI); - PROC_LOCK(p); - --p->p_lock; - PROC_UNLOCK(p); + printf(" virtual address = 0x%x\n", frame->srr0); break; - case EXC_SC|EXC_USER: - { - const struct sysent *callp; - size_t argsize; - register_t code, error; - register_t *params, rval[2]; - int n; - register_t args[10]; - - code = frame->fixreg[0]; - callp = &p->p_sysent->sv_table[0]; - params = frame->fixreg + FIRSTARG; - n = NARGREG; - - switch (code) { - case SYS_syscall: - /* - * code is first argument, - * followed by actual args. - */ - code = *params++; - n -= 1; - break; - case SYS___syscall: - params++; - code = *params++; - n -= 2; - break; - default: - break; - } + } + printf(" srr0 = 0x%x", frame->srr0); + printf(" curthread = %p\n", curthread); + if (curthread != NULL) + printf(" pid = %d, comm = %s\n", + curthread->td_proc->p_pid, curthread->td_proc->p_comm); + printf("\n"); +} - if (p->p_sysent->sv_mask) - code &= p->p_sysent->sv_mask; - callp += code; - argsize = callp->sy_narg & SYF_ARGMASK; - - if (argsize > n * sizeof(register_t)) { - memcpy(args, params, n * sizeof(register_t)); - error = copyin(MOREARGS(frame->fixreg[1]), - args + n, - argsize - n * sizeof(register_t)); - if (error) - goto syscall_bad; - params = args; - } +/* + * Handles a fatal fault when we have onfault state to recover. Returns + * non-zero if there was onfault recovery state available. + */ +static int +handle_onfault(struct trapframe *frame) +{ + struct thread *td; + faultbuf *fb; + + td = curthread; + fb = td->td_pcb->pcb_onfault; + if (fb != NULL) { + frame->srr0 = (*fb)[0]; + frame->fixreg[1] = (*fb)[1]; + frame->fixreg[2] = (*fb)[2]; + frame->cr = (*fb)[3]; + bcopy(&(*fb)[4], &frame->fixreg[13], + 19 * sizeof(register_t)); + return (1); + } + return (0); +} - /* - * Try to run the syscall without Giant if the syscall - * is MP safe. - */ - if ((callp->sy_narg & SYF_MPSAFE) == 0) - mtx_lock(&Giant); +void +syscall(struct trapframe *frame) +{ + caddr_t params; + struct sysent *callp; + struct thread *td; + struct proc *p; + int error, n; + size_t narg; + register_t args[10]; + u_int code; -#ifdef KTRACE - if (KTRPOINT(p, KTR_SYSCALL)) - ktrsyscall(p, code, argsize, params); -#endif + td = PCPU_GET(curthread); + p = td->td_proc; - rval[0] = 0; - rval[1] = 0; - - error = (*callp->sy_call)(td, params); - switch (error) { - case 0: - frame->fixreg[FIRSTARG] = rval[0]; - frame->fixreg[FIRSTARG + 1] = rval[1]; - frame->cr &= ~0x10000000; - break; - case ERESTART: - /* - * Set user's pc back to redo the system call. - */ - frame->srr0 -= 4; - break; - case EJUSTRETURN: - /* nothing to do */ - break; - default: -syscall_bad: -#if 0 - if (p->p_emul->e_errno) - error = p->p_emul->e_errno[error]; -#endif - frame->fixreg[FIRSTARG] = error; - frame->cr |= 0x10000000; - break; - } + atomic_add_int(&cnt.v_syscall, 1); + + code = frame->fixreg[0]; + params = (caddr_t)(frame->fixreg + FIRSTARG); + n = NARGREG; + + if (p->p_sysent->sv_prepsyscall) { + /* + * The prep code is MP aware. + */ + (*p->p_sysent->sv_prepsyscall)(frame, args, &code, ¶ms); + } else if (code == SYS_syscall) { + /* + * code is first argument, + * followed by actual args. + */ + code = *params++; + n -= 1; + } else if (code == SYS___syscall) { + /* + * Like syscall, but code is a quad, + * so as to maintain quad alignment + * for the rest of the args. + */ + params++; + code = *params++; + n -= 2; + } + + if (p->p_sysent->sv_mask) + code &= p->p_sysent->sv_mask; + + if (code >= p->p_sysent->sv_size) + callp = &p->p_sysent->sv_table[0]; + else + callp = &p->p_sysent->sv_table[code]; + + narg = callp->sy_narg & SYF_ARGMASK; - /* - * Release Giant if we had to get it. Don't use - * mtx_owned(), we want to catch broken syscalls. - */ - if ((callp->sy_narg & SYF_MPSAFE) == 0) - mtx_unlock(&Giant); + if (narg > n * sizeof(register_t)) { + bcopy(params, args, n * sizeof(register_t)); + error = copyin(MOREARGS(frame->fixreg[1]), args + n, + narg - n * sizeof(register_t)); + if (error) { #ifdef KTRACE - if (KTRPOINT(p, KTR_SYSRET)) - ktrsysret(p, code, error, rval[0]); + /* Can't get all the arguments! */ + if (KTRPOINT(p, KTR_SYSCALL)) + ktrsyscall(p->p_tracep, code, narg, args); #endif + goto bad; } - break; + params = (caddr_t)args; + } - case EXC_FPU|EXC_USER: - if ((fputhread = PCPU_GET(fputhread)) != NULL) { - KASSERT(fputhread != td, - ("floating-point already enabled")); - save_fpu(fputhread); - } - PCPU_SET(fputhread, td); - td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid); - enable_fpu(td); - frame->srr1 |= PSL_FP; - break; + /* + * Try to run the syscall without Giant if the syscall is MP safe. + */ + if ((callp->sy_narg & SYF_MPSAFE) == 0) + mtx_lock(&Giant); -#ifdef ALTIVEC - case EXC_VEC|EXC_USER: - if (vecproc) { - save_vec(vecproc); - } - vecproc = p; - enable_vec(p); - break; +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSCALL)) + ktrsyscall(p->p_tracep, code, narg, params); #endif - -#if 0 - case EXC_AST|EXC_USER: - astpending = 0; /* we are about to do it */ - PROC_LOCK(p); - if (p->p_flag & P_OWEUPC) { - p->p_flag &= ~P_OWEUPC; - ADDUPROF(p); - } - /* Check whether we are being preempted. */ - if (want_resched) - mi_switch(); - PROC_UNLOCK(p); + td->td_retval[0] = 0; + td->td_retval[1] = frame->fixreg[FIRSTARG + 1]; + + STOPEVENT(p, S_SCE, narg); + + error = (*callp->sy_call)(td, params); + switch (error) { + case 0: + frame->fixreg[FIRSTARG] = td->td_retval[0]; + frame->fixreg[FIRSTARG + 1] = td->td_retval[1]; + /* XXX: Magic number */ + frame->cr &= ~0x10000000; break; -#endif - - case EXC_ALI|EXC_USER: - PROC_LOCK(p); - if (fix_unaligned(td, frame) != 0) { - printf("trap: pid %d (%s): user ALI trap @ %#x " - "(SSR1=%#x)\n", - p->p_pid, p->p_comm, frame->srr0, - frame->srr1); - trapsignal(p, SIGBUS, EXC_ALI); - } else - frame->srr0 += 4; - PROC_UNLOCK(p); + case ERESTART: + /* + * Set user's pc back to redo the system call. + */ + frame->srr0 -= 4; break; - - case EXC_PGM|EXC_USER: -/* XXX temporarily */ - PROC_LOCK(p); - printf("trap: pid %d (%s): user PGM trap @ %#x " - "(SSR1=%#x)\n", - p->p_pid, p->p_comm, frame->srr0, frame->srr1); - if (frame->srr1 & 0x00020000) /* Bit 14 is set if trap */ - trapsignal(p, SIGTRAP, EXC_PGM); - else - trapsignal(p, SIGILL, EXC_PGM); - PROC_UNLOCK(p); + case EJUSTRETURN: + /* nothing to do */ break; - - case EXC_MCHK: { - faultbuf *fb; - - if ((fb = td->td_pcb->pcb_onfault) != NULL) { - frame->srr0 = (*fb)[0]; - frame->fixreg[1] = (*fb)[1]; - frame->fixreg[2] = (*fb)[2]; - frame->fixreg[3] = EFAULT; - frame->cr = (*fb)[3]; - memcpy(&frame->fixreg[13], &(*fb)[4], - 19 * sizeof(register_t)); - return; + default: +bad: + if (p->p_sysent->sv_errsize) { + if (error >= p->p_sysent->sv_errsize) + error = -1; /* XXX */ + else + error = p->p_sysent->sv_errtbl[error]; } - goto brain_damage; + frame->fixreg[FIRSTARG] = error; + /* XXX: Magic number: Carry Flag Equivalent? */ + frame->cr |= 0x10000000; + break; } - default: -brain_damage: - printf("trap type %x at %x\n", type, frame->srr0); -brain_damage2: -#ifdef DDBX - if (kdb_trap(type, frame)) - return; -#endif -#ifdef TRAP_PANICWAIT - printf("Press a key to panic.\n"); - cnpollc(1); - cngetc(); - cnpollc(0); + +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSRET)) + ktrsysret(p->p_tracep, code, error, td->td_retval[0]); #endif - panic("trap"); - } -#if 0 - /* Take pending signals. */ - { - int sig; - - while ((sig = CURSIG(p)) != 0) - postsig(sig); - } -#endif + if ((callp->sy_narg & SYF_MPSAFE) == 0) + mtx_unlock(&Giant); /* - * If someone stole the fp or vector unit while we were away, - * disable it + * Does the comment in the i386 code about errno apply here? */ - if (td != PCPU_GET(fputhread) || - td->td_pcb->pcb_fpcpu != PCPU_GET(cpuid)) - frame->srr1 &= ~PSL_FP; -#ifdef ALTIVEC - if (p != vecproc) - frame->srr1 &= ~PSL_VEC; -#endif + STOPEVENT(p, S_SCX, code); -#if 0 - p->p_priority = p->p_usrpri; +#ifdef WITNESS + if (witness_list(td)) { + panic("system call %s returning with mutex(s) held\n", + syscallnames[code]); + } #endif + mtx_assert(&sched_lock, MA_NOTOWNED); + mtx_assert(&Giant, MA_NOTOWNED); } -void child_return(void *); - -void -child_return(void *arg) +static int +trap_pfault(struct trapframe *frame, int user) { - struct thread *td; - struct proc *p; - struct trapframe *tf; + vm_offset_t eva, va; + struct thread *td; + struct proc *p; + vm_map_t map; + vm_prot_t ftype; + int rv; + u_int user_sr; - td = (struct thread *)arg; + td = curthread; p = td->td_proc; - tf = trapframe(td); + if (frame->exc == EXC_ISI) { + eva = frame->srr0; + ftype = VM_PROT_READ | VM_PROT_EXECUTE; + } else { + eva = frame->dar; + if (frame->dsisr & DSISR_STORE) + ftype = VM_PROT_READ | VM_PROT_WRITE; + else + ftype = VM_PROT_READ; + } - PROC_UNLOCK(p); + if (user) { + map = &p->p_vmspace->vm_map; + } else { + if ((eva >> ADDR_SR_SHFT) == USER_SR) { + if (p->p_vmspace == NULL) + return (SIGSEGV); + + __asm ("mfsr %0, %1" + : "=r"(user_sr) + : "K"(USER_SR)); + eva &= ADDR_PIDX | ADDR_POFF; + eva |= user_sr << ADDR_SR_SHFT; + map = &p->p_vmspace->vm_map; + } else { + map = kernel_map; + } + } + va = trunc_page(eva); - tf->fixreg[FIRSTARG] = 0; - tf->fixreg[FIRSTARG + 1] = 1; - tf->cr &= ~0x10000000; - tf->srr1 &= ~(PSL_FP|PSL_VEC); /* Disable FP & AltiVec, as we can't - be them. */ - td->td_pcb->pcb_fpcpu = NULL; -#ifdef KTRACE - if (KTRPOINT(p, KTR_SYSRET)) { + mtx_lock(&Giant); + if (map != kernel_map) { + /* + * Keep swapout from messing with us during this + * critical time. + */ PROC_LOCK(p); - ktrsysret(p, SYS_fork, 0, 0); + ++p->p_lock; PROC_UNLOCK(p); + + /* Fault in the user page: */ + rv = vm_fault(map, va, ftype, + (ftype & VM_PROT_WRITE) ? VM_FAULT_DIRTY + : VM_FAULT_NORMAL); + + PROC_LOCK(p); + --p->p_lock; + PROC_UNLOCK(p); + } else { + /* + * Don't have to worry about process locking or stacks in the + * kernel. + */ + rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL); } -#endif + mtx_unlock(&Giant); + + if (rv == KERN_SUCCESS) + return (0); + + if (!user && handle_onfault(frame)) + return (0); + + return (SIGSEGV); } static __inline void -- cgit v1.1