/* Instruction pointers operate differently on mc88110 */ void m88110_syscall(register_t code, struct trapframe *tf) { int i, nsys, nap; struct sysent *callp; struct proc *p; int error; register_t args[11], rval[2], *ap; u_quad_t sticks; #ifdef DIAGNOSTIC extern struct pcb *curpcb; #endif uvmexp.syscalls++; p = curproc; callp = p->p_emul->e_sysent; nsys = p->p_emul->e_nsysent; #ifdef DIAGNOSTIC if (USERMODE(tf->tf_epsr) == 0) panic("syscall"); if (curpcb != &p->p_addr->u_pcb) panic("syscall curpcb/ppcb"); if (tf != (struct trapframe *)&curpcb->user_state) panic("syscall trapframe"); #endif sticks = p->p_sticks; p->p_md.md_tf = tf; /* * For 88k, all the arguments are passed in the registers (r2-r12) * For syscall (and __syscall), r2 (and r3) has the actual code. * __syscall takes a quad syscall number, so that other * arguments are at their natural alignments. */ ap = &tf->tf_r[2]; nap = 11; /* r2-r12 */ switch (code) { case SYS_syscall: code = *ap++; nap--; break; case SYS___syscall: if (callp != sysent) break; code = ap[_QUAD_LOWWORD]; ap += 2; nap -= 2; break; } /* Callp currently points to syscall, which returns ENOSYS. */ if (code < 0 || code >= nsys) callp += p->p_emul->e_nosys; else { callp += code; i = callp->sy_argsize / sizeof(register_t); if (i > nap) panic("syscall nargs"); /* * just copy them; syscall stub made sure all the * args are moved from user stack to registers. */ bcopy((caddr_t)ap, (caddr_t)args, i * sizeof(register_t)); } #ifdef SYSCALL_DEBUG scdebug_call(p, code, args); #endif #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) ktrsyscall(p, code, callp->sy_argsize, args); #endif rval[0] = 0; rval[1] = tf->tf_r[3]; #if NSYSTRACE > 0 if (ISSET(p->p_flag, P_SYSTRACE)) error = systrace_redirect(code, p, args, rval); else #endif error = (*callp->sy_call)(p, args, rval); /* * system call will look like: * ld r10, r31, 32; r10,r11,r12 might be garbage. * ld r11, r31, 36 * ld r12, r31, 40 * or r13, r0, <code> * tb0 0, r0, <128> <- exip * br err <- enip * jmp r1 * err: or.u r3, r0, hi16(errno) * st r2, r3, lo16(errno) * subu r2, r0, 1 * jmp r1 * * So, when we take syscall trap, exip/enip will be as * shown above. * Given this, * 1. If the system call returned 0, need to jmp r1. * exip += 8 * 2. If the system call returned an errno > 0, increment * exip += 4 and plug the value in r2. This will have us * executing "br err" on return to user space. * 3. If the system call code returned ERESTART, * we need to rexecute the trap instruction. leave exip as is. * 4. If the system call returned EJUSTRETURN, just return. * exip += 4 */ switch (error) { case 0: /* * If fork succeeded and we are the child, our stack * has moved and the pointer tf is no longer valid, * and p is wrong. Compute the new trapframe pointer. * (The trap frame invariably resides at the * tippity-top of the u. area.) */ p = curproc; tf = (struct trapframe *)USER_REGS(p); tf->tf_r[2] = rval[0]; tf->tf_r[3] = rval[1]; tf->tf_epsr &= ~PSR_C; /* skip two instructions */ if (tf->tf_exip & 1) tf->tf_exip = tf->tf_enip + 4; else tf->tf_exip += 4 + 4; break; case ERESTART: /* * Reexecute the trap. * exip is already at the trap instruction, so * there is nothing to do. */ tf->tf_epsr &= ~PSR_C; break; case EJUSTRETURN: tf->tf_epsr &= ~PSR_C; /* skip one instruction */ if (tf->tf_exip & 1) tf->tf_exip = tf->tf_enip; else tf->tf_exip += 4; break; default: if (p->p_emul->e_errno) error = p->p_emul->e_errno[error]; tf->tf_r[2] = error; tf->tf_epsr |= PSR_C; /* fail */ /* skip one instruction */ if (tf->tf_exip & 1) tf->tf_exip = tf->tf_enip; else tf->tf_exip += 4; break; } #ifdef SYSCALL_DEBUG scdebug_ret(p, code, error, rval); #endif userret(p, tf, sticks); #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) ktrsysret(p, code, error, rval[0]); #endif }
void m88100_syscall(register_t code, struct trapframe *tf) { int i, nsys, nap; struct sysent *callp; struct proc *p = curproc; int error; register_t args[8], rval[2], *ap; int nolock; uvmexp.syscalls++; callp = p->p_emul->e_sysent; nsys = p->p_emul->e_nsysent; p->p_md.md_tf = tf; /* * For 88k, all the arguments are passed in the registers (r2-r9), * and further arguments (if any) on stack. * For syscall (and __syscall), r2 (and r3) has the actual code. * __syscall takes a quad syscall number, so that other * arguments are at their natural alignments. */ ap = &tf->tf_r[2]; nap = 8; /* r2-r9 */ switch (code) { case SYS_syscall: code = *ap++; nap--; break; case SYS___syscall: if (callp != sysent) break; code = ap[_QUAD_LOWWORD]; ap += 2; nap -= 2; break; } if (code < 0 || code >= nsys) callp += p->p_emul->e_nosys; else callp += code; i = callp->sy_argsize / sizeof(register_t); if (i > sizeof(args) / sizeof(register_t)) panic("syscall nargs"); if (i > nap) { bcopy((caddr_t)ap, (caddr_t)args, nap * sizeof(register_t)); error = copyin((caddr_t)tf->tf_r[31], (caddr_t)(args + nap), (i - nap) * sizeof(register_t)); } else { bcopy((caddr_t)ap, (caddr_t)args, i * sizeof(register_t)); error = 0; } if (error != 0) goto bad; #ifdef SYSCALL_DEBUG KERNEL_LOCK(); scdebug_call(p, code, args); KERNEL_UNLOCK(); #endif #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) { KERNEL_LOCK(); ktrsyscall(p, code, callp->sy_argsize, args); KERNEL_UNLOCK(); } #endif rval[0] = 0; rval[1] = tf->tf_r[3]; #if NSYSTRACE > 0 if (ISSET(p->p_flag, P_SYSTRACE)) { KERNEL_LOCK(); error = systrace_redirect(code, p, args, rval); KERNEL_UNLOCK(); } else #endif { nolock = (callp->sy_flags & SY_NOLOCK); if (!nolock) KERNEL_LOCK(); error = (*callp->sy_call)(p, args, rval); if (!nolock) KERNEL_UNLOCK(); } /* * system call will look like: * or r13, r0, <code> * tb0 0, r0, <128> <- sxip * br err <- snip * jmp r1 <- sfip * err: or.u r3, r0, hi16(errno) * st r2, r3, lo16(errno) * subu r2, r0, 1 * jmp r1 * * So, when we take syscall trap, sxip/snip/sfip will be as * shown above. * Given this, * 1. If the system call returned 0, need to skip nip. * nip = fip, fip += 4 * (doesn't matter what fip + 4 will be but we will never * execute this since jmp r1 at nip will change the execution flow.) * 2. If the system call returned an errno > 0, plug the value * in r2, and leave nip and fip unchanged. This will have us * executing "br err" on return to user space. * 3. If the system call code returned ERESTART, * we need to rexecute the trap instruction. Back up the pipe * line. * fip = nip, nip = xip * 4. If the system call returned EJUSTRETURN, don't need to adjust * any pointers. */ switch (error) { case 0: tf->tf_r[2] = rval[0]; tf->tf_r[3] = rval[1]; tf->tf_epsr &= ~PSR_C; tf->tf_snip = tf->tf_sfip & ~NIP_E; tf->tf_sfip = tf->tf_snip + 4; break; case ERESTART: tf->tf_epsr &= ~PSR_C; tf->tf_sfip = tf->tf_snip & ~FIP_E; tf->tf_snip = tf->tf_sxip & ~NIP_E; break; case EJUSTRETURN: tf->tf_epsr &= ~PSR_C; break; default: bad: if (p->p_emul->e_errno) error = p->p_emul->e_errno[error]; tf->tf_r[2] = error; tf->tf_epsr |= PSR_C; /* fail */ tf->tf_snip = tf->tf_snip & ~NIP_E; tf->tf_sfip = tf->tf_sfip & ~FIP_E; break; } #ifdef SYSCALL_DEBUG KERNEL_LOCK(); scdebug_ret(p, code, error, rval); KERNEL_UNLOCK(); #endif userret(p); #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) { KERNEL_LOCK(); ktrsysret(p, code, error, rval[0]); KERNEL_UNLOCK(); } #endif }
/* Instruction pointers operate differently on mc88110 */ void m88110_syscall(register_t code, struct trapframe *tf) { int i, nsys, nap; struct sysent *callp; struct proc *p; int error; register_t args[8], rval[2], *ap; uvmexp.syscalls++; p = curproc; callp = p->p_emul->e_sysent; nsys = p->p_emul->e_nsysent; p->p_md.md_tf = tf; /* * For 88k, all the arguments are passed in the registers (r2-r9), * and further arguments (if any) on stack. * For syscall (and __syscall), r2 (and r3) has the actual code. * __syscall takes a quad syscall number, so that other * arguments are at their natural alignments. */ ap = &tf->tf_r[2]; nap = 8; /* r2-r9 */ switch (code) { case SYS_syscall: code = *ap++; nap--; break; case SYS___syscall: if (callp != sysent) break; code = ap[_QUAD_LOWWORD]; ap += 2; nap -= 2; break; } if (code < 0 || code >= nsys) callp += p->p_emul->e_nosys; else callp += code; i = callp->sy_argsize / sizeof(register_t); if (i > sizeof(args) > sizeof(register_t)) panic("syscall nargs"); if (i > nap) { bcopy((caddr_t)ap, (caddr_t)args, nap * sizeof(register_t)); error = copyin((caddr_t)tf->tf_r[31], (caddr_t)(args + nap), (i - nap) * sizeof(register_t)); } else { bcopy((caddr_t)ap, (caddr_t)args, i * sizeof(register_t)); error = 0; } if (error != 0) goto bad; KERNEL_PROC_LOCK(p); #ifdef SYSCALL_DEBUG scdebug_call(p, code, args); #endif #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) ktrsyscall(p, code, callp->sy_argsize, args); #endif rval[0] = 0; rval[1] = tf->tf_r[3]; #if NSYSTRACE > 0 if (ISSET(p->p_flag, P_SYSTRACE)) error = systrace_redirect(code, p, args, rval); else #endif error = (*callp->sy_call)(p, args, rval); /* * system call will look like: * or r13, r0, <code> * tb0 0, r0, <128> <- exip * br err <- enip * jmp r1 * err: or.u r3, r0, hi16(errno) * st r2, r3, lo16(errno) * subu r2, r0, 1 * jmp r1 * * So, when we take syscall trap, exip/enip will be as * shown above. * Given this, * 1. If the system call returned 0, need to jmp r1. * exip += 8 * 2. If the system call returned an errno > 0, increment * exip += 4 and plug the value in r2. This will have us * executing "br err" on return to user space. * 3. If the system call code returned ERESTART, * we need to rexecute the trap instruction. leave exip as is. * 4. If the system call returned EJUSTRETURN, just return. * exip += 4 */ KERNEL_PROC_UNLOCK(p); switch (error) { case 0: tf->tf_r[2] = rval[0]; tf->tf_r[3] = rval[1]; tf->tf_epsr &= ~PSR_C; /* skip two instructions */ if (tf->tf_exip & 1) tf->tf_exip = tf->tf_enip + 4; else tf->tf_exip += 4 + 4; break; case ERESTART: /* * Reexecute the trap. * exip is already at the trap instruction, so * there is nothing to do. */ tf->tf_epsr &= ~PSR_C; break; case EJUSTRETURN: tf->tf_epsr &= ~PSR_C; /* skip one instruction */ if (tf->tf_exip & 1) tf->tf_exip = tf->tf_enip; else tf->tf_exip += 4; break; default: bad: if (p->p_emul->e_errno) error = p->p_emul->e_errno[error]; tf->tf_r[2] = error; tf->tf_epsr |= PSR_C; /* fail */ /* skip one instruction */ if (tf->tf_exip & 1) tf->tf_exip = tf->tf_enip; else tf->tf_exip += 4; break; } #ifdef SYSCALL_DEBUG KERNEL_PROC_LOCK(p); scdebug_ret(p, code, error, rval); KERNEL_PROC_UNLOCK(p); #endif userret(p); #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) { KERNEL_PROC_LOCK(p); ktrsysret(p, code, error, rval[0]); KERNEL_PROC_UNLOCK(p); } #endif }
/* * Handle an exception. * In the case of a kernel trap, we return the pc where to resume if * pcb_onfault is set, otherwise, return old pc. */ void trap(struct trap_frame *trapframe) { struct cpu_info *ci = curcpu(); int type, i; unsigned ucode = 0; struct proc *p = ci->ci_curproc; vm_prot_t ftype; extern vaddr_t onfault_table[]; int onfault; int typ = 0; union sigval sv; trapdebug_enter(ci, trapframe, -1); type = (trapframe->cause & CR_EXC_CODE) >> CR_EXC_CODE_SHIFT; if (USERMODE(trapframe->sr)) { type |= T_USER; } /* * Enable hardware interrupts if they were on before the trap; * enable IPI interrupts only otherwise. */ if (type != T_BREAK) { if (ISSET(trapframe->sr, SR_INT_ENAB)) enableintr(); else { #ifdef MULTIPROCESSOR ENABLEIPI(); #endif } } switch (type) { case T_TLB_MOD: /* check for kernel address */ if (trapframe->badvaddr < 0) { pt_entry_t *pte, entry; paddr_t pa; vm_page_t pg; pte = kvtopte(trapframe->badvaddr); entry = *pte; #ifdef DIAGNOSTIC if (!(entry & PG_V) || (entry & PG_M)) panic("trap: ktlbmod: invalid pte"); #endif if (pmap_is_page_ro(pmap_kernel(), trunc_page(trapframe->badvaddr), entry)) { /* write to read only page in the kernel */ ftype = VM_PROT_WRITE; goto kernel_fault; } entry |= PG_M; *pte = entry; KERNEL_LOCK(); pmap_update_kernel_page(trapframe->badvaddr & ~PGOFSET, entry); pa = pfn_to_pad(entry); pg = PHYS_TO_VM_PAGE(pa); if (pg == NULL) panic("trap: ktlbmod: unmanaged page"); pmap_set_modify(pg); KERNEL_UNLOCK(); return; } /* FALLTHROUGH */ case T_TLB_MOD+T_USER: { pt_entry_t *pte, entry; paddr_t pa; vm_page_t pg; pmap_t pmap = p->p_vmspace->vm_map.pmap; if (!(pte = pmap_segmap(pmap, trapframe->badvaddr))) panic("trap: utlbmod: invalid segmap"); pte += uvtopte(trapframe->badvaddr); entry = *pte; #ifdef DIAGNOSTIC if (!(entry & PG_V) || (entry & PG_M)) panic("trap: utlbmod: invalid pte"); #endif if (pmap_is_page_ro(pmap, trunc_page(trapframe->badvaddr), entry)) { /* write to read only page */ ftype = VM_PROT_WRITE; goto fault_common; } entry |= PG_M; *pte = entry; KERNEL_LOCK(); pmap_update_user_page(pmap, (trapframe->badvaddr & ~PGOFSET), entry); pa = pfn_to_pad(entry); pg = PHYS_TO_VM_PAGE(pa); if (pg == NULL) panic("trap: utlbmod: unmanaged page"); pmap_set_modify(pg); KERNEL_UNLOCK(); if (!USERMODE(trapframe->sr)) return; goto out; } case T_TLB_LD_MISS: case T_TLB_ST_MISS: ftype = (type == T_TLB_ST_MISS) ? VM_PROT_WRITE : VM_PROT_READ; /* check for kernel address */ if (trapframe->badvaddr < 0) { vaddr_t va; int rv; kernel_fault: va = trunc_page((vaddr_t)trapframe->badvaddr); onfault = p->p_addr->u_pcb.pcb_onfault; p->p_addr->u_pcb.pcb_onfault = 0; KERNEL_LOCK(); rv = uvm_fault(kernel_map, trunc_page(va), 0, ftype); KERNEL_UNLOCK(); p->p_addr->u_pcb.pcb_onfault = onfault; if (rv == 0) return; if (onfault != 0) { p->p_addr->u_pcb.pcb_onfault = 0; trapframe->pc = onfault_table[onfault]; return; } goto err; } /* * It is an error for the kernel to access user space except * through the copyin/copyout routines. */ if (p->p_addr->u_pcb.pcb_onfault != 0) { /* * We want to resolve the TLB fault before invoking * pcb_onfault if necessary. */ goto fault_common; } else { goto err; } case T_TLB_LD_MISS+T_USER: ftype = VM_PROT_READ; goto fault_common; case T_TLB_ST_MISS+T_USER: ftype = VM_PROT_WRITE; fault_common: { vaddr_t va; struct vmspace *vm; vm_map_t map; int rv; vm = p->p_vmspace; map = &vm->vm_map; va = trunc_page((vaddr_t)trapframe->badvaddr); onfault = p->p_addr->u_pcb.pcb_onfault; p->p_addr->u_pcb.pcb_onfault = 0; KERNEL_LOCK(); rv = uvm_fault(map, trunc_page(va), 0, ftype); p->p_addr->u_pcb.pcb_onfault = onfault; /* * If this was a stack access we keep track of the maximum * accessed stack size. Also, if vm_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 ((caddr_t)va >= vm->vm_maxsaddr) { if (rv == 0) uvm_grow(p, va); else if (rv == EACCES) rv = EFAULT; } KERNEL_UNLOCK(); if (rv == 0) { if (!USERMODE(trapframe->sr)) return; goto out; } if (!USERMODE(trapframe->sr)) { if (onfault != 0) { p->p_addr->u_pcb.pcb_onfault = 0; trapframe->pc = onfault_table[onfault]; return; } goto err; } #ifdef ADEBUG printf("SIG-SEGV @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapframe->ra); #endif ucode = ftype; i = SIGSEGV; typ = SEGV_MAPERR; break; } case T_ADDR_ERR_LD+T_USER: /* misaligned or kseg access */ case T_ADDR_ERR_ST+T_USER: /* misaligned or kseg access */ ucode = 0; /* XXX should be VM_PROT_something */ i = SIGBUS; typ = BUS_ADRALN; #ifdef ADEBUG printf("SIG-BUSA @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapframe->ra); #endif break; case T_BUS_ERR_IFETCH+T_USER: /* BERR asserted to cpu */ case T_BUS_ERR_LD_ST+T_USER: /* BERR asserted to cpu */ ucode = 0; /* XXX should be VM_PROT_something */ i = SIGBUS; typ = BUS_OBJERR; #ifdef ADEBUG printf("SIG-BUSB @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapframe->ra); #endif break; case T_SYSCALL+T_USER: { struct trap_frame *locr0 = p->p_md.md_regs; struct sysent *callp; unsigned int code; unsigned long tpc; int numsys; struct args { register_t i[8]; } args; register_t rval[2]; uvmexp.syscalls++; /* compute next PC after syscall instruction */ tpc = trapframe->pc; /* Remember if restart */ if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; callp = p->p_emul->e_sysent; numsys = p->p_emul->e_nsysent; code = locr0->v0; switch (code) { case SYS_syscall: /* * Code is first argument, followed by actual args. */ code = locr0->a0; if (code >= numsys) callp += p->p_emul->e_nosys; /* (illegal) */ else callp += code; i = callp->sy_argsize / sizeof(register_t); args.i[0] = locr0->a1; args.i[1] = locr0->a2; args.i[2] = locr0->a3; if (i > 3) { args.i[3] = locr0->a4; args.i[4] = locr0->a5; args.i[5] = locr0->a6; args.i[6] = locr0->a7; i = copyin((void *)locr0->sp, &args.i[7], sizeof(register_t)); } break; case SYS___syscall: /* * Like syscall, but code is a quad, so as to maintain * quad alignment for the rest of the arguments. */ code = locr0->a0; args.i[0] = locr0->a1; args.i[1] = locr0->a2; args.i[2] = locr0->a3; if (code >= numsys) callp += p->p_emul->e_nosys; /* (illegal) */ else callp += code; i = callp->sy_argsize / sizeof(int); if (i > 3) { args.i[3] = locr0->a4; args.i[4] = locr0->a5; args.i[5] = locr0->a6; args.i[6] = locr0->a7; i = copyin((void *)locr0->sp, &args.i[7], sizeof(register_t)); } break; default: if (code >= numsys) callp += p->p_emul->e_nosys; /* (illegal) */ else callp += code; i = callp->sy_narg; args.i[0] = locr0->a0; args.i[1] = locr0->a1; args.i[2] = locr0->a2; args.i[3] = locr0->a3; if (i > 4) { args.i[4] = locr0->a4; args.i[5] = locr0->a5; args.i[6] = locr0->a6; args.i[7] = locr0->a7; } } #ifdef SYSCALL_DEBUG KERNEL_LOCK(); scdebug_call(p, code, args.i); KERNEL_UNLOCK(); #endif #ifdef KTRACE if (KTRPOINT(p, KTR_SYSCALL)) { KERNEL_LOCK(); ktrsyscall(p, code, callp->sy_argsize, args.i); KERNEL_UNLOCK(); } #endif rval[0] = 0; rval[1] = locr0->v1; #if defined(DDB) || defined(DEBUG) trapdebug[TRAPSIZE * ci->ci_cpuid + (trppos[ci->ci_cpuid] == 0 ? TRAPSIZE : trppos[ci->ci_cpuid]) - 1].code = code; #endif #if NSYSTRACE > 0 if (ISSET(p->p_flag, P_SYSTRACE)) { KERNEL_LOCK(); i = systrace_redirect(code, p, args.i, rval); KERNEL_UNLOCK(); } else #endif { int nolock = (callp->sy_flags & SY_NOLOCK); if (!nolock) KERNEL_LOCK(); i = (*callp->sy_call)(p, &args, rval); if (!nolock) KERNEL_UNLOCK(); } switch (i) { case 0: locr0->v0 = rval[0]; locr0->v1 = rval[1]; locr0->a3 = 0; break; case ERESTART: locr0->pc = tpc; break; case EJUSTRETURN: break; /* nothing to do */ default: locr0->v0 = i; locr0->a3 = 1; } #ifdef SYSCALL_DEBUG KERNEL_LOCK(); scdebug_ret(p, code, i, rval); KERNEL_UNLOCK(); #endif #ifdef KTRACE if (KTRPOINT(p, KTR_SYSRET)) { KERNEL_LOCK(); ktrsysret(p, code, i, rval[0]); KERNEL_UNLOCK(); } #endif goto out; } case T_BREAK: #ifdef DDB kdb_trap(type, trapframe); #endif /* Reenable interrupts if necessary */ if (trapframe->sr & SR_INT_ENAB) { enableintr(); } return; case T_BREAK+T_USER: { caddr_t va; u_int32_t instr; struct trap_frame *locr0 = p->p_md.md_regs; /* compute address of break instruction */ va = (caddr_t)trapframe->pc; if (trapframe->cause & CR_BR_DELAY) va += 4; /* read break instruction */ copyin(va, &instr, sizeof(int32_t)); switch ((instr & BREAK_VAL_MASK) >> BREAK_VAL_SHIFT) { case 6: /* gcc range error */ i = SIGFPE; typ = FPE_FLTSUB; /* skip instruction */ if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; break; case 7: /* gcc3 divide by zero */ i = SIGFPE; typ = FPE_INTDIV; /* skip instruction */ if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; break; #ifdef PTRACE case BREAK_SSTEP_VAL: if (p->p_md.md_ss_addr == (long)va) { #ifdef DEBUG printf("trap: %s (%d): breakpoint at %p " "(insn %08x)\n", p->p_comm, p->p_pid, p->p_md.md_ss_addr, p->p_md.md_ss_instr); #endif /* Restore original instruction and clear BP */ process_sstep(p, 0); typ = TRAP_BRKPT; } else { typ = TRAP_TRACE; } i = SIGTRAP; break; #endif #ifdef FPUEMUL case BREAK_FPUEMUL_VAL: /* * If this is a genuine FP emulation break, * resume execution to our branch destination. */ if ((p->p_md.md_flags & MDP_FPUSED) != 0 && p->p_md.md_fppgva + 4 == (vaddr_t)va) { struct vm_map *map = &p->p_vmspace->vm_map; p->p_md.md_flags &= ~MDP_FPUSED; locr0->pc = p->p_md.md_fpbranchva; /* * Prevent access to the relocation page. * XXX needs to be fixed to work with rthreads */ uvm_fault_unwire(map, p->p_md.md_fppgva, p->p_md.md_fppgva + PAGE_SIZE); (void)uvm_map_protect(map, p->p_md.md_fppgva, p->p_md.md_fppgva + PAGE_SIZE, UVM_PROT_NONE, FALSE); goto out; } /* FALLTHROUGH */ #endif default: typ = TRAP_TRACE; i = SIGTRAP; break; } break; } case T_IWATCH+T_USER: case T_DWATCH+T_USER: { caddr_t va; /* compute address of trapped instruction */ va = (caddr_t)trapframe->pc; if (trapframe->cause & CR_BR_DELAY) va += 4; printf("watch exception @ %p\n", va); #ifdef RM7K_PERFCNTR if (rm7k_watchintr(trapframe)) { /* Return to user, don't add any more overhead */ goto out; } #endif i = SIGTRAP; typ = TRAP_BRKPT; break; } case T_TRAP+T_USER: { caddr_t va; u_int32_t instr; struct trap_frame *locr0 = p->p_md.md_regs; /* compute address of trap instruction */ va = (caddr_t)trapframe->pc; if (trapframe->cause & CR_BR_DELAY) va += 4; /* read break instruction */ copyin(va, &instr, sizeof(int32_t)); if (trapframe->cause & CR_BR_DELAY) locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0, 0); else locr0->pc += 4; #ifdef RM7K_PERFCNTR if (instr == 0x040c0000) { /* Performance cntr trap */ int result; result = rm7k_perfcntr(trapframe->a0, trapframe->a1, trapframe->a2, trapframe->a3); locr0->v0 = -result; /* Return to user, don't add any more overhead */ goto out; } else #endif /* * GCC 4 uses teq with code 7 to signal divide by * zero at runtime. This is one instruction shorter * than the BEQ + BREAK combination used by gcc 3. */ if ((instr & 0xfc00003f) == 0x00000034 /* teq */ && (instr & 0x001fffc0) == ((ZERO << 16) | (7 << 6))) { i = SIGFPE; typ = FPE_INTDIV; } else { i = SIGEMT; /* Stuff it with something for now */ typ = 0; } break; } case T_RES_INST+T_USER: i = SIGILL; typ = ILL_ILLOPC; break; case T_COP_UNUSABLE+T_USER: /* * Note MIPS IV COP1X instructions issued with FPU * disabled correctly report coprocessor 1 as the * unusable coprocessor number. */ if ((trapframe->cause & CR_COP_ERR) != 0x10000000) { i = SIGILL; /* only FPU instructions allowed */ typ = ILL_ILLOPC; break; } #ifdef FPUEMUL MipsFPTrap(trapframe); #else enable_fpu(p); #endif goto out; case T_FPE: printf("FPU Trap: PC %x CR %x SR %x\n", trapframe->pc, trapframe->cause, trapframe->sr); goto err; case T_FPE+T_USER: MipsFPTrap(trapframe); goto out; case T_OVFLOW+T_USER: i = SIGFPE; typ = FPE_FLTOVF; break; case T_ADDR_ERR_LD: /* misaligned access */ case T_ADDR_ERR_ST: /* misaligned access */ case T_BUS_ERR_LD_ST: /* BERR asserted to cpu */ if ((onfault = p->p_addr->u_pcb.pcb_onfault) != 0) { p->p_addr->u_pcb.pcb_onfault = 0; trapframe->pc = onfault_table[onfault]; return; } goto err; default: err: disableintr(); #if !defined(DDB) && defined(DEBUG) trapDump("trap"); #endif printf("\nTrap cause = %d Frame %p\n", type, trapframe); printf("Trap PC %p RA %p fault %p\n", trapframe->pc, trapframe->ra, trapframe->badvaddr); #ifdef DDB stacktrace(!USERMODE(trapframe->sr) ? trapframe : p->p_md.md_regs); kdb_trap(type, trapframe); #endif panic("trap"); } #ifdef FPUEMUL /* * If a relocated delay slot causes an exception, blame the * original delay slot address - userland is not supposed to * know anything about emulation bowels. */ if ((p->p_md.md_flags & MDP_FPUSED) != 0 && trapframe->badvaddr == p->p_md.md_fppgva) trapframe->badvaddr = p->p_md.md_fpslotva; #endif p->p_md.md_regs->pc = trapframe->pc; p->p_md.md_regs->cause = trapframe->cause; p->p_md.md_regs->badvaddr = trapframe->badvaddr; sv.sival_ptr = (void *)trapframe->badvaddr; KERNEL_LOCK(); trapsignal(p, i, ucode, typ, sv); KERNEL_UNLOCK(); out: /* * Note: we should only get here if returning to user mode. */ userret(p); }