void chain_to(void *func) { /* * If set, this pointer is jumped-to by exit * after carefully restoring the PROM stack. */ chain_to_func = func; ICIA(); exit(0); }
/*ARGSUSED*/ void trap(struct frame *fp, int type, unsigned code, unsigned v) { extern char fubail[], subail[]; struct lwp *l; struct proc *p; struct pcb *pcb; void *onfault; ksiginfo_t ksi; int s; int rv; u_quad_t sticks = 0 /* XXX initialiser works around compiler bug */; static int panicking __diagused; curcpu()->ci_data.cpu_ntrap++; l = curlwp; p = l->l_proc; pcb = lwp_getpcb(l); KSI_INIT_TRAP(&ksi); ksi.ksi_trap = type & ~T_USER; if (USERMODE(fp->f_sr)) { type |= T_USER; sticks = p->p_sticks; l->l_md.md_regs = fp->f_regs; LWP_CACHE_CREDS(l, p); } switch (type) { default: dopanic: /* * Let the kernel debugger see the trap frame that * caused us to panic. This is a convenience so * one can see registers at the point of failure. */ s = splhigh(); panicking = 1; printf("trap type %d, code = 0x%x, v = 0x%x\n", type, code, v); printf("%s program counter = 0x%x\n", (type & T_USER) ? "user" : "kernel", fp->f_pc); #ifdef KGDB /* If connected, step or cont returns 1 */ if (kgdb_trap(type, (db_regs_t *)fp)) goto kgdb_cont; #endif #ifdef DDB (void)kdb_trap(type, (db_regs_t *)fp); #endif #ifdef KGDB kgdb_cont: #endif splx(s); if (panicstr) { printf("trap during panic!\n"); #ifdef DEBUG /* XXX should be a machine-dependent hook */ printf("(press a key)\n"); (void)cngetc(); #endif } regdump((struct trapframe *)fp, 128); type &= ~T_USER; if ((u_int)type < trap_types) panic(trap_type[type]); panic("trap"); case T_BUSERR: /* kernel bus error */ onfault = pcb->pcb_onfault; if (onfault == NULL) goto dopanic; rv = EFAULT; /* FALLTHROUGH */ copyfault: /* * If we have arranged to catch this fault in any of the * copy to/from user space routines, set PC to return to * indicated location and set flag informing buserror code * that it may need to clean up stack frame. */ fp->f_stackadj = exframesize[fp->f_format]; fp->f_format = fp->f_vector = 0; fp->f_pc = (int)onfault; fp->f_regs[D0] = rv; return; case T_BUSERR|T_USER: /* bus error */ case T_ADDRERR|T_USER: /* address error */ ksi.ksi_addr = (void *)v; ksi.ksi_signo = SIGBUS; ksi.ksi_code = (type == (T_BUSERR|T_USER)) ? BUS_OBJERR : BUS_ADRERR; break; case T_COPERR: /* kernel coprocessor violation */ case T_FMTERR|T_USER: /* do all RTE errors come in as T_USER? */ case T_FMTERR: /* ...just in case... */ /* * The user has most likely trashed the RTE or FP state info * in the stack frame of a signal handler. */ printf("pid %d: kernel %s exception\n", p->p_pid, type==T_COPERR ? "coprocessor" : "format"); type |= T_USER; mutex_enter(p->p_lock); SIGACTION(p, SIGILL).sa_handler = SIG_DFL; sigdelset(&p->p_sigctx.ps_sigignore, SIGILL); sigdelset(&p->p_sigctx.ps_sigcatch, SIGILL); sigdelset(&l->l_sigmask, SIGILL); mutex_exit(p->p_lock); ksi.ksi_signo = SIGILL; ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was ILL_RESAD_FAULT */ ksi.ksi_code = (type == T_COPERR) ? ILL_COPROC : ILL_ILLOPC; break; case T_COPERR|T_USER: /* user coprocessor violation */ /* What is a proper response here? */ ksi.ksi_signo = SIGFPE; ksi.ksi_code = FPE_FLTINV; break; case T_FPERR|T_USER: /* 68881 exceptions */ /* * We pass along the 68881 status register which locore stashed * in code for us. */ ksi.ksi_signo = SIGFPE; ksi.ksi_code = fpsr2siginfocode(code); break; #ifdef M68040 case T_FPEMULI|T_USER: /* unimplemented FP instruction */ case T_FPEMULD|T_USER: /* unimplemented FP data type */ /* XXX need to FSAVE */ printf("pid %d(%s): unimplemented FP %s at %x (EA %x)\n", p->p_pid, p->p_comm, fp->f_format == 2 ? "instruction" : "data type", fp->f_pc, fp->f_fmt2.f_iaddr); /* XXX need to FRESTORE */ ksi.ksi_signo = SIGFPE; ksi.ksi_code = FPE_FLTINV; break; #endif case T_ILLINST|T_USER: /* illegal instruction fault */ case T_PRIVINST|T_USER: /* privileged instruction fault */ ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was ILL_PRIVIN_FAULT */ ksi.ksi_signo = SIGILL; ksi.ksi_code = (type == (T_PRIVINST|T_USER)) ? ILL_PRVOPC : ILL_ILLOPC; break; case T_ZERODIV|T_USER: /* Divide by zero */ ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was FPE_INTDIV_TRAP */ ksi.ksi_signo = SIGFPE; ksi.ksi_code = FPE_FLTDIV; break; case T_CHKINST|T_USER: /* CHK instruction trap */ ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was FPE_SUBRNG_TRAP */ ksi.ksi_signo = SIGFPE; break; case T_TRAPVINST|T_USER: /* TRAPV instruction trap */ ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was FPE_INTOVF_TRAP */ ksi.ksi_signo = SIGFPE; break; /* * XXX: Trace traps are a nightmare. * * HP-UX uses trap #1 for breakpoints, * NetBSD/m68k uses trap #2, * SUN 3.x uses trap #15, * DDB and KGDB uses trap #15 (for kernel breakpoints; * handled elsewhere). * * NetBSD and HP-UX traps both get mapped by locore.s into T_TRACE. * SUN 3.x traps get passed through as T_TRAP15 and are not really * supported yet. * * XXX: We should never get kernel-mode T_TRAP15 * XXX: because locore.s now gives them special treatment. */ case T_TRAP15: /* kernel breakpoint */ #ifdef DEBUG printf("unexpected kernel trace trap, type = %d\n", type); printf("program counter = 0x%x\n", fp->f_pc); #endif fp->f_sr &= ~PSL_T; return; case T_TRACE|T_USER: /* user trace trap */ #ifdef COMPAT_SUNOS /* * SunOS uses Trap #2 for a "CPU cache flush". * Just flush the on-chip caches and return. */ if (p->p_emul == &emul_sunos) { ICIA(); DCIU(); return; } #endif /* FALLTHROUGH */ case T_TRACE: /* tracing a trap instruction */ case T_TRAP15|T_USER: /* SUN user trace trap */ fp->f_sr &= ~PSL_T; ksi.ksi_signo = SIGTRAP; break; case T_ASTFLT: /* system async trap, cannot happen */ goto dopanic; case T_ASTFLT|T_USER: /* user async trap */ astpending = 0; /* * We check for software interrupts first. This is because * they are at a higher level than ASTs, and on a VAX would * interrupt the AST. We assume that if we are processing * an AST that we must be at IPL0 so we don't bother to * check. Note that we ensure that we are at least at SIR * IPL while processing the SIR. */ spl1(); /* fall into... */ case T_SSIR: /* software interrupt */ case T_SSIR|T_USER: /* * If this was not an AST trap, we are all done. */ if (type != (T_ASTFLT|T_USER)) { curcpu()->ci_data.cpu_ntrap--; return; } spl0(); if (l->l_pflag & LP_OWEUPC) { l->l_pflag &= ~LP_OWEUPC; ADDUPROF(l); } if (curcpu()->ci_want_resched) preempt(); goto out; case T_MMUFLT: /* kernel mode page fault */ /* * If we were doing profiling ticks or other user mode * stuff from interrupt code, Just Say No. */ onfault = pcb->pcb_onfault; if (onfault == fubail || onfault == subail) { rv = EFAULT; goto copyfault; } /* fall into ... */ case T_MMUFLT|T_USER: /* page fault */ { vaddr_t va; struct vmspace *vm = p->p_vmspace; struct vm_map *map; vm_prot_t ftype; extern struct vm_map *kernel_map; onfault = pcb->pcb_onfault; #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) printf("trap: T_MMUFLT pid=%d, code=%x, v=%x, pc=%x, sr=%x\n", p->p_pid, code, v, fp->f_pc, fp->f_sr); #endif /* * It is only a kernel address space fault iff: * 1. (type & T_USER) == 0 and * 2. pcb_onfault not set or * 3. pcb_onfault set but supervisor space data fault * The last can occur during an exec() copyin where the * argument space is lazy-allocated. */ if ((type & T_USER) == 0 && (onfault == NULL || KDFAULT(code))) map = kernel_map; else { map = vm ? &vm->vm_map : kernel_map; } if (WRFAULT(code)) ftype = VM_PROT_WRITE; else ftype = VM_PROT_READ; va = trunc_page((vaddr_t)v); if (map == kernel_map && va == 0) { printf("trap: bad kernel %s access at 0x%x\n", (ftype & VM_PROT_WRITE) ? "read/write" : "read", v); goto dopanic; } #ifdef DIAGNOSTIC if (interrupt_depth && !panicking) { printf("trap: calling uvm_fault() from interrupt!\n"); goto dopanic; } #endif pcb->pcb_onfault = NULL; rv = uvm_fault(map, va, ftype); pcb->pcb_onfault = onfault; #ifdef DEBUG if (rv && MDB_ISPID(p->p_pid)) printf("uvm_fault(%p, 0x%lx, 0x%x) -> 0x%x\n", map, va, ftype, rv); #endif /* * 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 (rv == 0) { if (map != kernel_map && (void *)va >= vm->vm_maxsaddr) uvm_grow(p, va); if (type == T_MMUFLT) { if (ucas_ras_check(&fp->F_t)) { return; } #ifdef M68040 if (cputype == CPU_68040) (void) writeback(fp, 1); #endif return; } goto out; } if (rv == EACCES) { ksi.ksi_code = SEGV_ACCERR; rv = EFAULT; } else ksi.ksi_code = SEGV_MAPERR; if (type == T_MMUFLT) { if (onfault) goto copyfault; printf("uvm_fault(%p, 0x%lx, 0x%x) -> 0x%x\n", map, va, ftype, rv); printf(" type %x, code [mmu,,ssw]: %x\n", type, code); goto dopanic; } ksi.ksi_addr = (void *)v; switch (rv) { case ENOMEM: 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); ksi.ksi_signo = SIGKILL; break; case EINVAL: ksi.ksi_signo = SIGBUS; ksi.ksi_code = BUS_ADRERR; break; case EACCES: ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_ACCERR; break; default: ksi.ksi_signo = SIGSEGV; ksi.ksi_code = SEGV_MAPERR; break; } break; } } trapsignal(l, &ksi); if ((type & T_USER) == 0) return; out: userret(l, fp, sticks, v, 1); }
/* * This function does all command procesing for interfacing to * a remote gdb. */ int kgdb_trap(int type, struct frame *frame) { u_long len; u_char *addr; u_char *cp; u_char out, in; int outlen; int inlen; u_long gdb_regs[NUM_REGS]; if ((int)kgdb_dev < 0) { /* not debugging */ return (0); } if (kgdb_active == 0) { if (type != T_TRAP15) { /* No debugger active -- let trap handle this. */ return (0); } kgdb_getc = 0; for (inlen = 0; constab[inlen].cn_probe; inlen++) if (major(constab[inlen].cn_dev) == major(kgdb_dev)) { kgdb_getc = constab[inlen].cn_getc; kgdb_putc = constab[inlen].cn_putc; break; } if (kgdb_getc == 0 || kgdb_putc == 0) return (0); /* * If the packet that woke us up isn't an exec packet, * ignore it since there is no active debugger. Also, * we check that it's not an ack to be sure that the * remote side doesn't send back a response after the * local gdb has exited. Otherwise, the local host * could trap into gdb if it's running a gdb kernel too. */ in = GETC; /* * If we came in asynchronously through the serial line, * the framing character is eaten by the receive interrupt, * but if we come in through a synchronous trap (i.e., via * kgdb_connect()), we will see the extra character. */ if (in == FRAME_START) in = GETC; /* * Check that this is a debugger exec message. If so, * slurp up the entire message then ack it, and fall * through to the recv loop. */ if (KGDB_CMD(in) != KGDB_EXEC || (in & KGDB_ACK) != 0) return (0); while (GETC != FRAME_END) ; /* * Do the printf *before* we ack the message. This way * we won't drop any inbound characters while we're * doing the polling printf. */ printf("kgdb started from device %x\n", kgdb_dev); kgdb_send(in | KGDB_ACK, (u_char *)0, 0); kgdb_active = 1; } /* * Stick frame regs into our reg cache then tell remote host * that an exception has occurred. */ regs_to_gdb(frame, gdb_regs); if (type != T_TRAP15) { /* * Only send an asynchronous SIGNAL message when we hit * a breakpoint. Otherwise, we will drop the incoming * packet while we output this one (and on entry the other * side isn't interested in the SIGNAL type -- if it is, * it will have used a signal packet.) */ outbuffer[0] = computeSignal(type); kgdb_send(KGDB_SIGNAL, outbuffer, 1); } while (1) { in = kgdb_recv(inbuffer, &inlen); if (in == 0 || (in & KGDB_ACK)) /* Ignore inbound acks and error conditions. */ continue; out = in | KGDB_ACK; switch (KGDB_CMD(in)) { case KGDB_SIGNAL: /* * if this command came from a running gdb, * answer it -- the other guy has no way of * knowing if we're in or out of this loop * when he issues a "remote-signal". (Note * that without the length check, we could * loop here forever if the ourput line is * looped back or the remote host is echoing.) */ if (inlen == 0) { outbuffer[0] = computeSignal(type); kgdb_send(KGDB_SIGNAL, outbuffer, 1); } continue; case KGDB_REG_R: case KGDB_REG_R | KGDB_DELTA: cp = outbuffer; outlen = 0; for (len = inbuffer[0]; len < NUM_REGS; ++len) { if (reg_cache[len] != gdb_regs[len] || (in & KGDB_DELTA) == 0) { if (outlen + 5 > SL_MAXDATA) { out |= KGDB_MORE; break; } cp[outlen] = len; kgdb_copy((u_char *)&gdb_regs[len], &cp[outlen + 1], 4); reg_cache[len] = gdb_regs[len]; outlen += 5; } } break; case KGDB_REG_W: case KGDB_REG_W | KGDB_DELTA: cp = inbuffer; for (len = 0; len < inlen; len += 5) { int j = cp[len]; kgdb_copy(&cp[len + 1], (u_char *)&gdb_regs[j], 4); reg_cache[j] = gdb_regs[j]; } gdb_to_regs(frame, gdb_regs); outlen = 0; break; case KGDB_MEM_R: len = inbuffer[0]; kgdb_copy(&inbuffer[1], (u_char *)&addr, 4); if (len > SL_MAXDATA) { outlen = 1; outbuffer[0] = E2BIG; } else if (!kgdb_acc(addr, len, B_READ)) { outlen = 1; outbuffer[0] = EFAULT; } else { outlen = len + 1; outbuffer[0] = 0; kgdb_copy(addr, &outbuffer[1], len); } break; case KGDB_MEM_W: len = inlen - 4; kgdb_copy(inbuffer, (u_char *)&addr, 4); outlen = 1; if (!kgdb_acc(addr, len, B_READ)) outbuffer[0] = EFAULT; else { outbuffer[0] = 0; if (!kgdb_acc(addr, len, B_WRITE)) chgkprot(addr, len, B_WRITE); kgdb_copy(&inbuffer[4], addr, len); ICIA(); } break; case KGDB_KILL: kgdb_active = 0; printf("kgdb detached\n"); /* fall through */ case KGDB_CONT: kgdb_send(out, 0, 0); frame->f_sr &=~ PSL_T; return (1); case KGDB_STEP: kgdb_send(out, 0, 0); frame->f_sr |= PSL_T; return (1); case KGDB_EXEC: default: /* Unknown command. Ack with a null message. */ outlen = 0; break; } /* Send the reply */ kgdb_send(out, outbuffer, outlen); } }
/*ARGSUSED1*/ int cachectl(struct proc *p, int req, vaddr_t addr, int len) { int error = 0; #if defined(M68040) || defined(M68060) if (mmutype <= MMU_68040) { int inc = 0; int doall = 0; paddr_t pa = 0; vaddr_t end = 0; #ifdef COMPAT_HPUX extern struct emul emul_hpux; if ((p->p_emul == &emul_hpux) && len != 16 && len != NBPG) doall = 1; #endif if (addr == 0 || ((req & ~CC_EXTPURGE) != CC_PURGE && len > 2*NBPG)) doall = 1; if (!doall) { end = addr + len; if (len <= 1024) { addr = addr & ~0xF; inc = 16; } else { addr = addr & ~PGOFSET; inc = NBPG; } } do { /* * Convert to physical address if needed. * If translation fails, we perform operation on * entire cache (XXX is this a rational thing to do?) */ if (!doall && (pa == 0 || ((int)addr & PGOFSET) == 0)) { if (pmap_extract( p->p_vmspace->vm_map.pmap, addr, &pa) == FALSE) doall = 1; } switch (req) { case CC_EXTPURGE|CC_IPURGE: case CC_IPURGE: if (doall) { DCFA(); ICPA(); } else if (inc == 16) { DCFL(pa); ICPL(pa); } else if (inc == NBPG) { DCFP(pa); ICPP(pa); } break; case CC_EXTPURGE|CC_PURGE: case CC_PURGE: if (doall) DCFA(); /* note: flush not purge */ else if (inc == 16) DCPL(pa); else if (inc == NBPG) DCPP(pa); break; case CC_EXTPURGE|CC_FLUSH: case CC_FLUSH: if (doall) DCFA(); else if (inc == 16) DCFL(pa); else if (inc == NBPG) DCFP(pa); break; default: error = EINVAL; break; } if (doall) break; pa += inc; addr += inc; } while (addr < end); return (error); } #endif switch (req) { case CC_EXTPURGE|CC_PURGE: case CC_EXTPURGE|CC_FLUSH: #if defined(CACHE_HAVE_PAC) if (ectype == EC_PHYS) PCIA(); /* FALLTHROUGH */ #endif case CC_PURGE: case CC_FLUSH: DCIU(); break; case CC_EXTPURGE|CC_IPURGE: #if defined(CACHE_HAVE_PAC) if (ectype == EC_PHYS) PCIA(); else #endif DCIU(); /* FALLTHROUGH */ case CC_IPURGE: ICIA(); break; default: error = EINVAL; break; } return (error); }
static void db_write_text(db_addr_t addr, size_t size, const char *data) { char *dst, *odst; pt_entry_t *pte, oldpte, tmppte; vaddr_t pgva; int limit; dst = (char *)addr; while (size > 0) { /* * Get the VA for the page. */ pgva = trunc_page((vaddr_t)dst); /* * Save this destination address, for TLB flush. */ odst = dst; /* * Compute number of bytes that can be written * with this mapping and subtract it from the total size. */ limit = round_page((vaddr_t)dst + 1) - (vaddr_t)dst; if (limit > size) limit = size; size -= limit; #ifdef M68K_MMU_HP /* * Flush the supervisor side of the VAC to * prevent a cache hit on the old, read-only PTE. */ if (ectype == EC_VIRT) DCIS(); #endif /* * Make the page writable. Note the mapping is * cache-inhibited to save hair. */ pte = kvtopte(pgva); oldpte = *pte; if ((oldpte & PG_V) == 0) { printf(" address %p not a valid page\n", dst); return; } tmppte = (oldpte & ~PG_RO) | PG_RW | PG_CI; *pte = tmppte; TBIS((vaddr_t)odst); /* * Page is now writable. Do as much access as we can. */ for (; limit > 0; limit--) *dst++ = *data++; /* * Restore the old PTE. */ *pte = oldpte; TBIS((vaddr_t)odst); } /* * Invalidate the instruction cache so our changes take effect. */ ICIA(); }
/*ARGSUSED*/ void trap(struct frame *fp, int type, u_int code, u_int v) { extern char fubail[], subail[]; struct lwp *l; struct proc *p; ksiginfo_t ksi; int s; u_quad_t sticks; uvmexp.traps++; l = curlwp; KSI_INIT_TRAP(&ksi); ksi.ksi_trap = type & ~T_USER; p = l->l_proc; if (USERMODE(fp->f_sr)) { type |= T_USER; sticks = p->p_sticks; l->l_md.md_regs = fp->f_regs; LWP_CACHE_CREDS(l, p); } else sticks = 0; #ifdef DIAGNOSTIC if (l->l_addr == NULL) panic("trap: type 0x%x, code 0x%x, v 0x%x -- no pcb", type, code, v); #endif switch (type) { default: dopanic: printf("trap type %d, code = 0x%x, v = 0x%x\n", type, code, v); printf("%s program counter = 0x%x\n", (type & T_USER) ? "user" : "kernel", fp->f_pc); /* * Let the kernel debugger see the trap frame that * caused us to panic. This is a convenience so * one can see registers at the point of failure. */ s = splhigh(); #ifdef KGDB /* If connected, step or cont returns 1 */ if (kgdb_trap(type, (db_regs_t *)fp)) goto kgdb_cont; #endif #ifdef DDB (void)kdb_trap(type, (db_regs_t *)fp); #endif #ifdef KGDB kgdb_cont: #endif splx(s); if (panicstr) { printf("trap during panic!\n"); #ifdef DEBUG /* XXX should be a machine-dependent hook */ printf("(press a key)\n"); (void)cngetc(); #endif } regdump((struct trapframe *)fp, 128); type &= ~T_USER; if ((u_int)type < trap_types) panic(trap_type[type]); panic("trap"); case T_BUSERR: /* Kernel bus error */ if (!l->l_addr->u_pcb.pcb_onfault) goto dopanic; /* * If we have arranged to catch this fault in any of the * copy to/from user space routines, set PC to return to * indicated location and set flag informing buserror code * that it may need to clean up stack frame. */ copyfault: fp->f_stackadj = exframesize[fp->f_format]; fp->f_format = fp->f_vector = 0; fp->f_pc = (int)l->l_addr->u_pcb.pcb_onfault; return; case T_BUSERR|T_USER: /* Bus error */ case T_ADDRERR|T_USER: /* Address error */ ksi.ksi_addr = (void *)v; ksi.ksi_signo = SIGBUS; ksi.ksi_code = (type == (T_BUSERR|T_USER)) ? BUS_OBJERR : BUS_ADRERR; break; case T_ILLINST|T_USER: /* Illegal instruction fault */ case T_PRIVINST|T_USER: /* Privileged instruction fault */ ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was ILL_PRIVIN_FAULT */ ksi.ksi_signo = SIGILL; ksi.ksi_code = (type == (T_PRIVINST|T_USER)) ? ILL_PRVOPC : ILL_ILLOPC; break; /* * divde by zero, CHK/TRAPV inst */ case T_ZERODIV|T_USER: /* Divide by zero trap */ ksi.ksi_code = FPE_FLTDIV; case T_CHKINST|T_USER: /* CHK instruction trap */ case T_TRAPVINST|T_USER: /* TRAPV instruction trap */ ksi.ksi_addr = (void *)(int)fp->f_format; ksi.ksi_signo = SIGFPE; break; /* * User coprocessor violation */ case T_COPERR|T_USER: /* XXX What is a proper response here? */ ksi.ksi_signo = SIGFPE; ksi.ksi_code = FPE_FLTINV; break; /* * 6888x exceptions */ case T_FPERR|T_USER: /* * We pass along the 68881 status register which locore * stashed in code for us. Note that there is a * possibility that the bit pattern of this register * will conflict with one of the FPE_* codes defined * in signal.h. Fortunately for us, the only such * codes we use are all in the range 1-7 and the low * 3 bits of the status register are defined as 0 so * there is no clash. */ ksi.ksi_signo = SIGFPE; ksi.ksi_addr = (void *)code; break; /* * FPU faults in supervisor mode. */ case T_ILLINST: /* fnop generates this, apparently. */ case T_FPEMULI: case T_FPEMULD: { extern label_t *nofault; if (nofault) /* If we're probing. */ longjmp(nofault); if (type == T_ILLINST) printf("Kernel Illegal Instruction trap.\n"); else printf("Kernel FPU trap.\n"); goto dopanic; } /* * Unimplemented FPU instructions/datatypes. */ case T_FPEMULI|T_USER: case T_FPEMULD|T_USER: #ifdef FPU_EMULATE if (fpu_emulate(fp, &l->l_addr->u_pcb.pcb_fpregs, &ksi) == 0) ; /* XXX - Deal with tracing? (fp->f_sr & PSL_T) */ #else uprintf("pid %d killed: no floating point support.\n", p->p_pid); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLOPC; #endif break; case T_COPERR: /* Kernel coprocessor violation */ case T_FMTERR: /* Kernel format error */ case T_FMTERR|T_USER: /* User format error */ /* * The user has most likely trashed the RTE or FP state info * in the stack frame of a signal handler. */ printf("pid %d: kernel %s exception\n", p->p_pid, type==T_COPERR ? "coprocessor" : "format"); type |= T_USER; mutex_enter(p->p_lock); SIGACTION(p, SIGILL).sa_handler = SIG_DFL; sigdelset(&p->p_sigctx.ps_sigignore, SIGILL); sigdelset(&p->p_sigctx.ps_sigcatch, SIGILL); sigdelset(&l->l_sigmask, SIGILL); mutex_exit(p->p_lock); ksi.ksi_signo = SIGILL; ksi.ksi_addr = (void *)(int)fp->f_format; /* XXX was ILL_RESAD_FAULT */ ksi.ksi_code = (type == T_COPERR) ? ILL_COPROC : ILL_ILLOPC; break; /* * XXX: Trace traps are a nightmare. * * HP-UX uses trap #1 for breakpoints, * NetBSD/m68k uses trap #2, * SUN 3.x uses trap #15, * DDB and KGDB uses trap #15 (for kernel breakpoints; * handled elsewhere). * * NetBSD and HP-UX traps both get mapped by locore.s into T_TRACE. * SUN 3.x traps get passed through as T_TRAP15 and are not really * supported yet. * * XXX: We should never get kernel-mode T_TRAP15 because * XXX: locore.s now gives it special treatment. */ case T_TRAP15: /* SUN trace trap */ #ifdef DEBUG printf("unexpected kernel trace trap, type = %d\n", type); printf("program counter = 0x%x\n", fp->f_pc); #endif fp->f_sr &= ~PSL_T; ksi.ksi_signo = SIGTRAP; break; case T_TRACE|T_USER: /* user trace trap */ #ifdef COMPAT_SUNOS /* * SunOS uses Trap #2 for a "CPU cache flush". * Just flush the on-chip caches and return. */ if (p->p_emul == &emul_sunos) { ICIA(); DCIU(); return; } #endif /* FALLTHROUGH */ case T_TRACE: /* tracing a trap instruction */ case T_TRAP15|T_USER: /* SUN user trace trap */ fp->f_sr &= ~PSL_T; ksi.ksi_signo = SIGTRAP; break; case T_ASTFLT: /* System async trap, cannot happen */ goto dopanic; case T_ASTFLT|T_USER: /* User async trap. */ astpending = 0; /* * We check for software interrupts first. This is because * they are at a higher level than ASTs, and on a VAX would * interrupt the AST. We assume that if we are processing * an AST that we must be at IPL0 so we don't bother to * check. Note that we ensure that we are at least at SIR * IPL while processing the SIR. */ spl1(); /* fall into... */ case T_SSIR: /* Software interrupt */ case T_SSIR|T_USER: /* * If this was not an AST trap, we are all done. */ if (type != (T_ASTFLT|T_USER)) { uvmexp.traps--; return; } spl0(); if (l->l_pflag & LP_OWEUPC) { l->l_pflag &= ~LP_OWEUPC; ADDUPROF(l); } if (curcpu()->ci_want_resched) preempt(); goto out; case T_MMUFLT: /* Kernel mode page fault */ /* * If we were doing profiling ticks or other user mode * stuff from interrupt code, Just Say No. */ if (l->l_addr->u_pcb.pcb_onfault == fubail || l->l_addr->u_pcb.pcb_onfault == subail) goto copyfault; /* fall into... */ case T_MMUFLT|T_USER: /* page fault */ { vaddr_t va; struct vmspace *vm = p->p_vmspace; struct vm_map *map; int rv; vm_prot_t ftype; extern struct vm_map *kernel_map; #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) printf("trap: T_MMUFLT pid=%d, code=%x, v=%x, pc=%x, sr=%x\n", p->p_pid, code, v, fp->f_pc, fp->f_sr); #endif /* * It is only a kernel address space fault iff: * 1. (type & T_USER) == 0 and * 2. pcb_onfault not set or * 3. pcb_onfault set but supervisor data fault * The last can occur during an exec() copyin where the * argument space is lazy-allocated. */ if (type == T_MMUFLT && (!l->l_addr->u_pcb.pcb_onfault || KDFAULT(code))) map = kernel_map; else { map = vm ? &vm->vm_map : kernel_map; if ((l->l_flag & LW_SA) && (~l->l_pflag & LP_SA_NOBLOCK)) { l->l_savp->savp_faultaddr = (vaddr_t)v; l->l_pflag |= LP_SA_PAGEFAULT; } } if (WRFAULT(code)) ftype = VM_PROT_WRITE; else ftype = VM_PROT_READ; va = trunc_page((vaddr_t)v); #ifdef DEBUG if (map == kernel_map && va == 0) { printf("trap: bad kernel access at %x\n", v); goto dopanic; } #endif rv = uvm_fault(map, va, ftype); #ifdef DEBUG if (rv && MDB_ISPID(p->p_pid)) printf("uvm_fault(%p, 0x%lx, 0x%x) -> 0x%x\n", map, va, ftype, rv); #endif /* * 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 (rv == 0) { if (map != kernel_map && (void *)va >= vm->vm_maxsaddr) uvm_grow(p, va); if (type == T_MMUFLT) { #if defined(M68040) if (mmutype == MMU_68040) (void)writeback(fp, 1); #endif return; } l->l_pflag &= ~LP_SA_PAGEFAULT; goto out; } if (rv == EACCES) { ksi.ksi_code = SEGV_ACCERR; rv = EFAULT; } else ksi.ksi_code = SEGV_MAPERR; if (type == T_MMUFLT) { if (l->l_addr->u_pcb.pcb_onfault) goto copyfault; printf("uvm_fault(%p, 0x%lx, 0x%x) -> 0x%x\n", map, va, ftype, rv); printf(" type %x, code [mmu,,ssw]: %x\n", type, code); goto dopanic; } l->l_pflag &= ~LP_SA_PAGEFAULT; ksi.ksi_addr = (void *)v; if (rv == ENOMEM) { 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); ksi.ksi_signo = SIGKILL; } else { ksi.ksi_signo = SIGSEGV; } break; } } if (ksi.ksi_signo) trapsignal(l, &ksi); if ((type & T_USER) == 0) return; out: userret(l, fp, sticks, v, 1); }