#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/disp.h>
#include <sys/class.h>
#include <sys/core.h>
#include <sys/syscall.h>
#include <sys/cpuvar.h>
#include <sys/vm.h>
#include <sys/sysinfo.h>
#include <sys/fault.h>
#include <sys/stack.h>
#include <sys/psw.h>
#include <sys/regset.h>
#include <sys/fp.h>
#include <sys/trap.h>
#include <sys/kmem.h>
#include <sys/vtrace.h>
#include <sys/cmn_err.h>
#include <sys/prsystm.h>
#include <sys/mutex_impl.h>
#include <sys/machsystm.h>
#include <sys/archsystm.h>
#include <sys/sdt.h>
#include <sys/avintr.h>
#include <sys/kobj.h>
#include <vm/hat.h>
#include <vm/seg_kmem.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/hat_pte.h>
#include <vm/hat_i86.h>
#include <sys/procfs.h>
#include <sys/reboot.h>
#include <sys/debug.h>
#include <sys/debugreg.h>
#include <sys/modctl.h>
#include <sys/aio_impl.h>
#include <sys/cred.h>
#include <sys/mman.h>
#include <sys/x86_archext.h>
#include <sys/copyops.h>
#include <c2/audit.h>
#include <sys/ftrace.h>
#include <sys/panic.h>
#include <sys/traptrace.h>
#include <sys/ontrap.h>
#include <sys/cpc_impl.h>
#include <sys/bootconf.h>
#include <sys/bootinfo.h>
#include <sys/promif.h>
#include <sys/mach_mmu.h>
#if defined(__xpv)
#include <sys/hypervisor.h>
#endif
#include <sys/contract/process_impl.h>
#define USER 0x10000
static const char *trap_type_mnemonic[] = {
"de", "db", "2", "bp",
"of", "br", "ud", "nm",
"df", "9", "ts", "np",
"ss", "gp", "pf", "15",
"mf", "ac", "mc", "xf"
};
static const char *trap_type[] = {
"Divide error",
"Debug",
"NMI interrupt",
"Breakpoint",
"Overflow",
"BOUND range exceeded",
"Invalid opcode",
"Device not available",
"Double fault",
"Coprocessor segment overrun",
"Invalid TSS",
"Segment not present",
"Stack segment fault",
"General protection",
"Page fault",
"Reserved",
"x87 floating point error",
"Alignment check",
"Machine check",
"SIMD floating point exception",
};
#define TRAP_TYPES (sizeof (trap_type) / sizeof (trap_type[0]))
#define SLOW_SCALL_SIZE 2
#define FAST_SCALL_SIZE 2
int tudebug = 0;
int tudebugbpt = 0;
int tudebugfpe = 0;
int tudebugsse = 0;
#if defined(TRAPDEBUG) || defined(lint)
int tdebug = 0;
int lodebug = 0;
int faultdebug = 0;
#else
#define tdebug 0
#define lodebug 0
#define faultdebug 0
#endif
#if defined(TRAPTRACE)
static trap_trace_rec_t trap_tr0[TRAPTR_NENT];
trap_trace_ctl_t trap_trace_ctl[NCPU] = {
{
(uintptr_t)trap_tr0,
(uintptr_t)trap_tr0,
(uintptr_t)(trap_tr0 + TRAPTR_NENT),
(uintptr_t)0
},
};
size_t trap_trace_bufsize = TRAPTR_NENT * sizeof (trap_trace_rec_t);
int trap_trace_freeze = 0;
int trap_trace_off = 0;
trap_trace_rec_t trap_trace_postmort;
static void dump_ttrace(void);
#endif
static void dumpregs(struct regs *);
static void showregs(uint_t, struct regs *, caddr_t);
static int kern_gpfault(struct regs *);
static int
die(uint_t type, struct regs *rp, caddr_t addr, processorid_t cpuid)
{
struct panic_trap_info ti;
const char *trap_name, *trap_mnemonic;
if (type < TRAP_TYPES) {
trap_name = trap_type[type];
trap_mnemonic = trap_type_mnemonic[type];
} else {
trap_name = "trap";
trap_mnemonic = "-";
}
#ifdef TRAPTRACE
TRAPTRACE_FREEZE;
#endif
ti.trap_regs = rp;
ti.trap_type = type & ~USER;
ti.trap_addr = addr;
curthread->t_panic_trap = &ti;
if (type == T_PGFLT && addr < (caddr_t)kernelbase) {
panic("BAD TRAP: type=%x (#%s %s) rp=%p addr=%p "
"occurred in module \"%s\" due to %s",
type, trap_mnemonic, trap_name, (void *)rp, (void *)addr,
mod_containing_pc((caddr_t)rp->r_pc),
addr < (caddr_t)PAGESIZE ?
"a NULL pointer dereference" :
"an illegal access to a user address");
} else
panic("BAD TRAP: type=%x (#%s %s) rp=%p addr=%p",
type, trap_mnemonic, trap_name, (void *)rp, (void *)addr);
return (0);
}
static int
rewrite_syscall(caddr_t pc)
{
uchar_t instr[SLOW_SCALL_SIZE] = { 0xCD, T_SYSCALLINT };
if (uwrite(curthread->t_procp, instr, SLOW_SCALL_SIZE,
(uintptr_t)pc) != 0)
return (1);
return (0);
}
static int
instr_is_other_syscall(caddr_t pc, int which)
{
uchar_t instr[FAST_SCALL_SIZE];
ASSERT(which == X86FSET_SEP || which == X86FSET_ASYSC || which == 0xCD);
if (copyin_nowatch(pc, (caddr_t)instr, FAST_SCALL_SIZE) != 0)
return (0);
switch (which) {
case X86FSET_SEP:
if (instr[0] == 0x0F && instr[1] == 0x34)
return (1);
break;
case X86FSET_ASYSC:
if (instr[0] == 0x0F && instr[1] == 0x05)
return (1);
break;
case 0xCD:
if (instr[0] == 0xCD && instr[1] == T_SYSCALLINT)
return (1);
break;
}
return (0);
}
#ifdef DEBUG
static const char *
syscall_insn_string(int syscall_insn)
{
switch (syscall_insn) {
case X86FSET_SEP:
return ("sysenter");
case X86FSET_ASYSC:
return ("syscall");
case 0xCD:
return ("int");
default:
return ("Unknown");
}
}
#endif
static int
ldt_rewrite_syscall(struct regs *rp, proc_t *p, int syscall_insn)
{
caddr_t linearpc;
int return_code = 0;
mutex_enter(&p->p_ldtlock);
if (linear_pc(rp, p, &linearpc) == 0) {
if (instr_is_other_syscall(linearpc, 0xCD)) {
return_code = 1;
} else if (instr_is_other_syscall(linearpc, syscall_insn)) {
if (rewrite_syscall(linearpc) == 0) {
return_code = 1;
}
#ifdef DEBUG
else
cmn_err(CE_WARN, "failed to rewrite %s "
"instruction in process %d",
syscall_insn_string(syscall_insn),
p->p_pid);
#endif
}
}
mutex_exit(&p->p_ldtlock);
return (return_code);
}
#define LCALLSIZE 7
static int
instr_is_lcall_syscall(caddr_t pc)
{
uchar_t instr[LCALLSIZE];
if (copyin_nowatch(pc, (caddr_t)instr, LCALLSIZE) == 0 &&
instr[0] == 0x9a &&
instr[1] == 0 &&
instr[2] == 0 &&
instr[3] == 0 &&
instr[4] == 0 &&
(instr[5] == 0x7 || instr[5] == 0x27) &&
instr[6] == 0)
return (1);
return (0);
}
#define LSAHFSIZE 1
static int
instr_is_lsahf(caddr_t pc, uchar_t *instr)
{
if (copyin_nowatch(pc, (caddr_t)instr, LSAHFSIZE) == 0 &&
(*instr == 0x9e || *instr == 0x9f))
return (1);
return (0);
}
static void
emulate_lsahf(struct regs *rp, uchar_t instr)
{
if (instr == 0x9e) {
rp->r_ps = (rp->r_ps & ~0xff) |
((rp->r_rax >> 8) & PSL_LSAHFMASK) | PS_MB1;
} else {
rp->r_rax = (rp->r_rax & ~0xff00) |
(((rp->r_ps & PSL_LSAHFMASK) | PS_MB1) << 8);
}
rp->r_pc += LSAHFSIZE;
}
#ifdef OPTERON_ERRATUM_91
static int
cmp_to_prefetch(uchar_t *p)
{
#ifdef _LP64
if ((p[0] & 0xF0) == 0x40)
p++;
#endif
return ((p[0] == 0x0F && p[1] == 0x18 && ((p[2] >> 3) & 7) <= 3) ||
(p[0] == 0x0F && p[1] == 0x0D && ((p[2] >> 3) & 7) <= 1));
}
static int
instr_is_prefetch(caddr_t pc)
{
uchar_t instr[4];
return (copyin_nowatch(pc, instr, sizeof (instr)) == 0 &&
cmp_to_prefetch(instr));
}
#endif
void
trap(struct regs *rp, caddr_t addr, processorid_t cpuid)
{
kthread_t *ct = curthread;
enum seg_rw rw;
unsigned type;
proc_t *p = ttoproc(ct);
klwp_t *lwp = ttolwp(ct);
uintptr_t lofault;
label_t *onfault;
faultcode_t pagefault(), res, errcode;
enum fault_type fault_type;
k_siginfo_t siginfo;
uint_t fault = 0;
int mstate;
int sicode = 0;
int watchcode;
int watchpage;
caddr_t vaddr;
size_t sz;
int ta;
uchar_t instr;
ASSERT_STACK_ALIGNED();
errcode = 0;
mstate = 0;
rw = S_OTHER;
type = rp->r_trapno;
CPU_STATS_ADDQ(CPU, sys, trap, 1);
ASSERT(ct->t_schedflag & TS_DONT_SWAP);
if (type == T_PGFLT) {
errcode = rp->r_err;
if (errcode & PF_ERR_WRITE) {
rw = S_WRITE;
} else if ((caddr_t)rp->r_pc == addr ||
(mmu.pt_nx != 0 && (errcode & PF_ERR_EXEC))) {
rw = S_EXEC;
} else {
rw = S_READ;
}
} else if (type == T_SGLSTP && lwp != NULL) {
lwp->lwp_pcb.pcb_drstat = (uintptr_t)addr;
}
if (tdebug)
showregs(type, rp, addr);
if (USERMODE(rp->r_cs)) {
if (ct->t_cred != p->p_cred) {
cred_t *oldcred = ct->t_cred;
ct->t_cred = crgetcred();
crfree(oldcred);
}
ASSERT(lwp != NULL);
type |= USER;
ASSERT(lwptoregs(lwp) == rp);
lwp->lwp_state = LWP_SYS;
switch (type) {
case T_PGFLT + USER:
if ((caddr_t)rp->r_pc == addr)
mstate = LMS_TFAULT;
else
mstate = LMS_DFAULT;
break;
default:
mstate = LMS_TRAP;
break;
}
mstate = new_mstate(ct, mstate);
bzero(&siginfo, sizeof (siginfo));
}
switch (type) {
case T_PGFLT + USER:
case T_SGLSTP:
case T_SGLSTP + USER:
case T_BPTFLT + USER:
break;
default:
FTRACE_2("trap(): type=0x%lx, regs=0x%lx",
(ulong_t)type, (ulong_t)rp);
break;
}
switch (type) {
case T_SIMDFPE:
sti();
default:
if (type & USER) {
if (tudebug)
showregs(type, rp, (caddr_t)0);
printf("trap: Unknown trap type %d in user mode\n",
type & ~USER);
siginfo.si_signo = SIGILL;
siginfo.si_code = ILL_ILLTRP;
siginfo.si_addr = (caddr_t)rp->r_pc;
siginfo.si_trapno = type & ~USER;
fault = FLTILL;
} else {
(void) die(type, rp, addr, cpuid);
}
break;
case T_PGFLT:
if ((ct->t_ontrap != NULL) &&
(ct->t_ontrap->ot_prot & OT_DATA_ACCESS)) {
ct->t_ontrap->ot_trap |= OT_DATA_ACCESS;
rp->r_pc = ct->t_ontrap->ot_trampoline;
goto cleanup;
}
if (errcode & PF_ERR_EXEC) {
(void) die(type, rp, addr, cpuid);
}
if (addr < (caddr_t)kernelbase &&
is_x86_feature(x86_featureset, X86FSET_SMAP) == B_TRUE &&
(rp->r_ps & PS_ACHK) == 0) {
(void) die(type, rp, addr, cpuid);
}
lofault = ct->t_lofault;
onfault = ct->t_onfault;
ct->t_lofault = 0;
mstate = new_mstate(ct, LMS_KFAULT);
if (addr < (caddr_t)kernelbase) {
res = pagefault(addr,
(errcode & PF_ERR_PROT)? F_PROT: F_INVAL, rw, 0);
if (res == FC_NOMAP &&
addr < p->p_usrstack &&
grow(addr))
res = 0;
} else {
res = pagefault(addr,
(errcode & PF_ERR_PROT)? F_PROT: F_INVAL, rw, 1);
}
(void) new_mstate(ct, mstate);
ct->t_lofault = lofault;
ct->t_onfault = onfault;
if (res == 0)
goto cleanup;
#if defined(OPTERON_ERRATUM_93) && defined(_LP64)
if (lofault == 0 && opteron_erratum_93) {
uintptr_t rip = rp->r_pc;
if ((rip & 0xfffffffful) == rip) {
rip |= 0xfffffffful << 32;
if (hat_getpfnum(kas.a_hat, (caddr_t)rip) !=
PFN_INVALID &&
(*(uchar_t *)rip == 0xf4 ||
*(uchar_t *)(rip - 1) == 0xf4)) {
rp->r_pc = rip;
goto cleanup;
}
}
}
#endif
#ifdef OPTERON_ERRATUM_91
if (lofault == 0 && opteron_erratum_91) {
caddr_t pc = (caddr_t)rp->r_pc;
if (hat_getpfnum(kas.a_hat, pc) != PFN_INVALID) {
if (cmp_to_prefetch((uchar_t *)pc)) {
#ifdef DEBUG
cmn_err(CE_WARN, "Opteron erratum 91 "
"occurred: kernel prefetch"
" at %p generated a page fault!",
(void *)rp->r_pc);
#endif
goto cleanup;
}
}
(void) die(type, rp, addr, cpuid);
}
#endif
if (lofault == 0)
(void) die(type, rp, addr, cpuid);
if (lodebug) {
showregs(type, rp, addr);
traceregs(rp);
}
if (FC_CODE(res) == FC_OBJERR)
res = FC_ERRNO(res);
else
res = EFAULT;
rp->r_r0 = res;
rp->r_pc = ct->t_lofault;
goto cleanup;
case T_PGFLT + USER:
if (faultdebug) {
char *fault_str;
switch (rw) {
case S_READ:
fault_str = "read";
break;
case S_WRITE:
fault_str = "write";
break;
case S_EXEC:
fault_str = "exec";
break;
default:
fault_str = "";
break;
}
printf("user %s fault: addr=0x%lx errcode=0x%x\n",
fault_str, (uintptr_t)addr, errcode);
}
#if defined(OPTERON_ERRATUM_100) && defined(_LP64)
if (p->p_model == DATAMODEL_ILP32 &&
(0xffffffff80000000 <= (uintptr_t)addr ||
(0x100000000 <= (uintptr_t)addr &&
(uintptr_t)addr <= 0x17fffffff))) {
if (!opteron_erratum_100)
panic("unexpected erratum #100");
if (rp->r_pc <= 0xffffffff)
goto out;
}
#endif
ASSERT(!(curthread->t_flag & T_WATCHPT));
watchpage = (pr_watch_active(p) && pr_is_watchpage(addr, rw));
vaddr = addr;
if (!watchpage || (sz = instr_size(rp, &vaddr, rw)) <= 0)
fault_type = (errcode & PF_ERR_PROT)? F_PROT: F_INVAL;
else if ((watchcode = pr_is_watchpoint(&vaddr, &ta,
sz, NULL, rw)) != 0) {
if (ta) {
do_watch_step(vaddr, sz, rw,
watchcode, rp->r_pc);
fault_type = F_INVAL;
} else {
bzero(&siginfo, sizeof (siginfo));
siginfo.si_signo = SIGTRAP;
siginfo.si_code = watchcode;
siginfo.si_addr = vaddr;
siginfo.si_trapafter = 0;
siginfo.si_pc = (caddr_t)rp->r_pc;
fault = FLTWATCH;
break;
}
} else {
if (rw != S_EXEC && pr_watch_emul(rp, vaddr, rw))
goto out;
do_watch_step(vaddr, sz, rw, 0, 0);
fault_type = F_INVAL;
}
res = pagefault(addr, fault_type, rw, 0);
if (res == 0 ||
(res == FC_NOMAP &&
addr < p->p_usrstack &&
grow(addr))) {
lwp->lwp_lastfault = FLTPAGE;
lwp->lwp_lastfaddr = addr;
if (prismember(&p->p_fltmask, FLTPAGE)) {
bzero(&siginfo, sizeof (siginfo));
siginfo.si_addr = addr;
(void) stop_on_fault(FLTPAGE, &siginfo);
}
goto out;
} else if (res == FC_PROT && addr < p->p_usrstack &&
(mmu.pt_nx != 0 && (errcode & PF_ERR_EXEC))) {
report_stack_exec(p, addr);
}
#ifdef OPTERON_ERRATUM_91
if (opteron_erratum_91 &&
addr != (caddr_t)rp->r_pc &&
instr_is_prefetch((caddr_t)rp->r_pc)) {
#ifdef DEBUG
cmn_err(CE_WARN, "Opteron erratum 91 occurred: "
"prefetch at %p in pid %d generated a trap!",
(void *)rp->r_pc, p->p_pid);
#endif
goto out;
}
#endif
if (tudebug)
showregs(type, rp, addr);
bzero(&siginfo, sizeof (siginfo));
siginfo.si_addr = addr;
switch (FC_CODE(res)) {
case FC_HWERR:
case FC_NOSUPPORT:
siginfo.si_signo = SIGBUS;
siginfo.si_code = BUS_ADRERR;
fault = FLTACCESS;
break;
case FC_ALIGN:
siginfo.si_signo = SIGBUS;
siginfo.si_code = BUS_ADRALN;
fault = FLTACCESS;
break;
case FC_OBJERR:
if ((siginfo.si_errno = FC_ERRNO(res)) != EINTR) {
siginfo.si_signo = SIGBUS;
siginfo.si_code = BUS_OBJERR;
fault = FLTACCESS;
}
break;
default:
siginfo.si_signo = SIGSEGV;
siginfo.si_code =
(res == FC_NOMAP)? SEGV_MAPERR : SEGV_ACCERR;
fault = FLTBOUNDS;
break;
}
break;
case T_ILLINST + USER:
if (p->p_ldt != NULL &&
ldt_rewrite_syscall(rp, p, X86FSET_ASYSC))
goto out;
if (p->p_model == DATAMODEL_LP64 &&
instr_is_lsahf((caddr_t)rp->r_pc, &instr)) {
emulate_lsahf(rp, instr);
goto out;
}
if (tudebug)
showregs(type, rp, (caddr_t)0);
siginfo.si_signo = SIGILL;
siginfo.si_code = ILL_ILLOPC;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTILL;
break;
case T_ZERODIV + USER:
if (tudebug && tudebugfpe)
showregs(type, rp, (caddr_t)0);
siginfo.si_signo = SIGFPE;
siginfo.si_code = FPE_INTDIV;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTIZDIV;
break;
case T_OVFLW + USER:
if (tudebug && tudebugfpe)
showregs(type, rp, (caddr_t)0);
siginfo.si_signo = SIGFPE;
siginfo.si_code = FPE_INTOVF;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTIOVF;
break;
case T_NOEXTFLT + USER:
case T_NOEXTFLT:
(void) die(type, rp, addr, cpuid);
break;
case T_EXTERRFLT:
sti();
(void) die(type, rp, addr, cpuid);
break;
case T_EXTERRFLT + USER:
if (tudebug && tudebugfpe)
showregs(type, rp, addr);
if ((sicode = fpexterrflt(rp)) != 0) {
siginfo.si_signo = SIGFPE;
siginfo.si_code = sicode;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTFPE;
}
break;
case T_SIMDFPE + USER:
if (tudebug && tudebugsse)
showregs(type, rp, addr);
if (!is_x86_feature(x86_featureset, X86FSET_SSE) &&
!is_x86_feature(x86_featureset, X86FSET_SSE2)) {
siginfo.si_signo = SIGILL;
siginfo.si_code = ILL_ILLTRP;
siginfo.si_addr = (caddr_t)rp->r_pc;
siginfo.si_trapno = type & ~USER;
fault = FLTILL;
} else if ((sicode = fpsimderrflt(rp)) != 0) {
siginfo.si_signo = SIGFPE;
siginfo.si_code = sicode;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTFPE;
}
sti();
break;
case T_BPTFLT:
if (tudebug && tudebugbpt)
showregs(type, rp, (caddr_t)0);
(void) die(type, rp, addr, cpuid);
break;
case T_SGLSTP:
#if !defined(__xpv)
if (lwp != NULL && (lwp->lwp_pcb.pcb_drstat & DR_SINGLESTEP)) {
if (rp->r_pc == (greg_t)brand_sys_sysenter ||
rp->r_pc == (greg_t)sys_sysenter ||
rp->r_pc == (greg_t)tr_brand_sys_sysenter ||
rp->r_pc == (greg_t)tr_sys_sysenter) {
rp->r_pc += 0x3;
rp->r_ps &= ~PS_T;
lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
ct->t_post_sys = 1;
aston(curthread);
goto cleanup;
} else {
if (tudebug && tudebugbpt)
showregs(type, rp, (caddr_t)0);
}
}
#endif
if (boothowto & RB_DEBUG)
debug_enter((char *)NULL);
else
(void) die(type, rp, addr, cpuid);
break;
case T_NMIFLT:
printf("Unexpected NMI in system mode\n");
goto cleanup;
case T_NMIFLT + USER:
printf("Unexpected NMI in user mode\n");
break;
case T_GPFLT:
if (ct->t_ontrap != NULL) {
int ttype = ct->t_ontrap->ot_prot &
(OT_DATA_ACCESS | OT_SEGMENT_ACCESS);
if (ttype != 0) {
ct->t_ontrap->ot_trap |= ttype;
if (tudebug)
showregs(type, rp, (caddr_t)0);
rp->r_pc = ct->t_ontrap->ot_trampoline;
goto cleanup;
}
}
if (ct->t_lofault) {
if (lodebug) {
showregs(type, rp, addr);
traceregs(rp);
}
rp->r_r0 = EFAULT;
rp->r_pc = ct->t_lofault;
goto cleanup;
}
case T_SEGFLT:
if (ct->t_ontrap != NULL &&
ct->t_ontrap->ot_prot & OT_SEGMENT_ACCESS) {
ct->t_ontrap->ot_trap |= OT_SEGMENT_ACCESS;
if (tudebug)
showregs(type, rp, (caddr_t)0);
rp->r_pc = ct->t_ontrap->ot_trampoline;
goto cleanup;
}
case T_STKFLT:
case T_TSSFLT:
if (tudebug)
showregs(type, rp, (caddr_t)0);
if (kern_gpfault(rp))
(void) die(type, rp, addr, cpuid);
goto cleanup;
case T_SEGFLT + USER:
case T_GPFLT + USER:
#ifdef _SYSCALL32_IMPL
if (p->p_model != DATAMODEL_NATIVE) {
#endif
if (instr_is_lcall_syscall((caddr_t)rp->r_pc)) {
if (type == T_SEGFLT + USER)
ASSERT(p->p_ldt != NULL);
if ((p->p_ldt == NULL && type == T_GPFLT + USER) ||
type == T_SEGFLT + USER) {
rp->r_pc += LCALLSIZE;
lwp->lwp_regs = rp;
mstate = LMS_USER;
dosyscall();
goto out;
}
}
#ifdef _SYSCALL32_IMPL
}
#endif
if (p->p_ldt != NULL &&
ldt_rewrite_syscall(rp, p, X86FSET_SEP))
goto out;
case T_BOUNDFLT + USER:
case T_STKFLT + USER:
case T_TSSFLT + USER:
if (tudebug)
showregs(type, rp, (caddr_t)0);
siginfo.si_signo = SIGSEGV;
siginfo.si_code = SEGV_MAPERR;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTBOUNDS;
break;
case T_ALIGNMENT + USER:
if (tudebug)
showregs(type, rp, (caddr_t)0);
bzero(&siginfo, sizeof (siginfo));
siginfo.si_signo = SIGBUS;
siginfo.si_code = BUS_ADRALN;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTACCESS;
break;
case T_SGLSTP + USER:
if (tudebug && tudebugbpt)
showregs(type, rp, (caddr_t)0);
if (lwp->lwp_pcb.pcb_drstat & DR_SINGLESTEP) {
pcb_t *pcb = &lwp->lwp_pcb;
rp->r_ps &= ~PS_T;
if ((fault = undo_watch_step(&siginfo)) == 0 &&
((pcb->pcb_flags & NORMAL_STEP) ||
!(pcb->pcb_flags & WATCH_STEP))) {
siginfo.si_signo = SIGTRAP;
siginfo.si_code = TRAP_TRACE;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTTRACE;
}
pcb->pcb_flags &= ~(NORMAL_STEP|WATCH_STEP);
}
break;
case T_BPTFLT + USER:
if (tudebug && tudebugbpt)
showregs(type, rp, (caddr_t)0);
if (p->p_proc_flag & P_PR_BPTADJ)
rp->r_pc--;
siginfo.si_signo = SIGTRAP;
siginfo.si_code = TRAP_BRKPT;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTBPT;
break;
case T_AST:
goto cleanup;
case T_AST + USER:
if (lwp->lwp_pcb.pcb_flags & ASYNC_HWERR) {
proc_t *p = ttoproc(curthread);
extern void print_msg_hwerr(ctid_t ct_id, proc_t *p);
lwp->lwp_pcb.pcb_flags &= ~ASYNC_HWERR;
print_msg_hwerr(p->p_ct_process->conp_contract.ct_id,
p);
contract_process_hwerr(p->p_ct_process, p);
siginfo.si_signo = SIGKILL;
siginfo.si_code = SI_NOINFO;
} else if (lwp->lwp_pcb.pcb_flags & CPC_OVERFLOW) {
lwp->lwp_pcb.pcb_flags &= ~CPC_OVERFLOW;
if (kcpc_overflow_ast()) {
if (tudebug)
showregs(type, rp, (caddr_t)0);
bzero(&siginfo, sizeof (siginfo));
siginfo.si_signo = SIGEMT;
siginfo.si_code = EMT_CPCOVF;
siginfo.si_addr = (caddr_t)rp->r_pc;
fault = FLTCPCOVF;
}
}
break;
}
ASSERT(type & USER);
if (fault) {
lwp->lwp_pcb.pcb_flags &= ~(NORMAL_STEP|WATCH_STEP);
lwp->lwp_lastfault = fault;
lwp->lwp_lastfaddr = siginfo.si_addr;
DTRACE_PROC2(fault, int, fault, ksiginfo_t *, &siginfo);
if (siginfo.si_signo != SIGKILL &&
prismember(&p->p_fltmask, fault) &&
stop_on_fault(fault, &siginfo) == 0)
siginfo.si_signo = 0;
}
if (siginfo.si_signo)
trapsig(&siginfo, (fault != FLTFPE && fault != FLTCPCOVF));
if (lwp->lwp_oweupc)
profil_tick(rp->r_pc);
if (ct->t_astflag | ct->t_sig_check) {
astoff(ct);
if (lwp->lwp_pcb.pcb_flags & DEBUG_PENDING)
deferred_singlestep_trap((caddr_t)rp->r_pc);
ct->t_sig_check = 0;
if (curthread->t_proc_flag & TP_CHANGEBIND) {
mutex_enter(&p->p_lock);
if (curthread->t_proc_flag & TP_CHANGEBIND) {
timer_lwpbind();
curthread->t_proc_flag &= ~TP_CHANGEBIND;
}
mutex_exit(&p->p_lock);
}
if (p->p_aio)
aio_cleanup(0);
if (ISHOLD(p))
holdlwp();
if (ISSIG_PENDING(ct, lwp, p)) {
if (issig(FORREAL))
psig();
ct->t_sig_check = 1;
}
if (ct->t_rprof != NULL) {
realsigprof(0, 0, 0);
ct->t_sig_check = 1;
}
if (lwp->lwp_pcb.pcb_flags & REQUEST_STEP) {
lwp->lwp_pcb.pcb_flags &= ~REQUEST_STEP;
rp->r_ps |= PS_T;
}
if (lwp->lwp_pcb.pcb_flags & REQUEST_NOSTEP) {
lwp->lwp_pcb.pcb_flags &= ~REQUEST_NOSTEP;
rp->r_ps &= ~PS_T;
}
}
out:
ASSERT(type & USER);
if (ISHOLD(p))
holdlwp();
lwp->lwp_state = LWP_USER;
if (ct->t_trapret) {
ct->t_trapret = 0;
thread_lock(ct);
CL_TRAPRET(ct);
thread_unlock(ct);
}
if (CPU->cpu_runrun || curthread->t_schedflag & TS_ANYWAITQ)
preempt();
prunstop();
(void) new_mstate(ct, mstate);
return;
cleanup:
ASSERT(!(type & USER));
}
int IGNORE_KERNEL_PREEMPTION = 0;
struct kpreempt_cnts {
int kpc_idle;
int kpc_intr;
int kpc_clock;
int kpc_blocked;
int kpc_notonproc;
int kpc_inswtch;
int kpc_prilevel;
int kpc_apreempt;
int kpc_spreempt;
} kpreempt_cnts;
void
kpreempt(int asyncspl)
{
kthread_t *ct = curthread;
if (IGNORE_KERNEL_PREEMPTION) {
aston(CPU->cpu_dispthread);
return;
}
do {
if (ct->t_preempt) {
if (ct->t_pri < 0) {
kpreempt_cnts.kpc_idle++;
if (CPU->cpu_dispthread != CPU->cpu_thread)
siron();
} else if (ct->t_flag & T_INTR_THREAD) {
kpreempt_cnts.kpc_intr++;
if (ct->t_pil == CLOCK_LEVEL)
kpreempt_cnts.kpc_clock++;
} else {
kpreempt_cnts.kpc_blocked++;
if (CPU->cpu_dispthread != CPU->cpu_thread)
siron();
}
aston(CPU->cpu_dispthread);
return;
}
if (ct->t_state != TS_ONPROC ||
ct->t_disp_queue != CPU->cpu_disp) {
kpreempt_cnts.kpc_notonproc++;
if (CPU->cpu_thread != CPU->cpu_dispthread) {
kpreempt_cnts.kpc_inswtch++;
siron();
}
return;
}
if (getpil() >= DISP_LEVEL) {
siron();
kpreempt_cnts.kpc_prilevel++;
return;
}
if (!interrupts_enabled()) {
kpreempt_cnts.kpc_prilevel++;
return;
}
if (asyncspl != KPREEMPT_SYNC)
kpreempt_cnts.kpc_apreempt++;
else
kpreempt_cnts.kpc_spreempt++;
ct->t_preempt++;
preempt();
ct->t_preempt--;
} while (CPU->cpu_kprunrun);
}
static void
showregs(uint_t type, struct regs *rp, caddr_t addr)
{
int s;
s = spl7();
type &= ~USER;
if (PTOU(curproc)->u_comm[0])
printf("%s: ", PTOU(curproc)->u_comm);
if (type < TRAP_TYPES)
printf("#%s %s\n", trap_type_mnemonic[type], trap_type[type]);
else
switch (type) {
case T_SYSCALL:
printf("Syscall Trap:\n");
break;
case T_AST:
printf("AST\n");
break;
default:
printf("Bad Trap = %d\n", type);
break;
}
if (type == T_PGFLT) {
printf("Bad %s fault at addr=0x%lx\n",
USERMODE(rp->r_cs) ? "user": "kernel", (uintptr_t)addr);
} else if (addr) {
printf("addr=0x%lx\n", (uintptr_t)addr);
}
printf("pid=%d, pc=0x%lx, sp=0x%lx, eflags=0x%lx\n",
(ttoproc(curthread) && ttoproc(curthread)->p_pidp) ?
ttoproc(curthread)->p_pid : 0, rp->r_pc, rp->r_sp, rp->r_ps);
#if defined(__lint)
printf("cr0: %x cr4: %b\n",
(uint_t)getcr0(), (uint_t)getcr4(), FMT_CR4);
#else
printf("cr0: %b cr4: %b\n",
(uint_t)getcr0(), FMT_CR0, (uint_t)getcr4(), FMT_CR4);
#endif
printf("cr2: %lx ", getcr2());
#if !defined(__xpv)
printf("cr3: %lx ", getcr3());
printf("cr8: %lx\n", getcr8());
#endif
printf("\n");
dumpregs(rp);
splx(s);
}
static void
dumpregs(struct regs *rp)
{
const char fmt[] = "\t%3s: %16lx %3s: %16lx %3s: %16lx\n";
printf(fmt, "rdi", rp->r_rdi, "rsi", rp->r_rsi, "rdx", rp->r_rdx);
printf(fmt, "rcx", rp->r_rcx, " r8", rp->r_r8, " r9", rp->r_r9);
printf(fmt, "rax", rp->r_rax, "rbx", rp->r_rbx, "rbp", rp->r_rbp);
printf(fmt, "r10", rp->r_r10, "r11", rp->r_r11, "r12", rp->r_r12);
printf(fmt, "r13", rp->r_r13, "r14", rp->r_r14, "r15", rp->r_r15);
printf(fmt, "fsb", rdmsr(MSR_AMD_FSBASE), "gsb", rdmsr(MSR_AMD_GSBASE),
" ds", rp->r_ds);
printf(fmt, " es", rp->r_es, " fs", rp->r_fs, " gs", rp->r_gs);
printf(fmt, "trp", rp->r_trapno, "err", rp->r_err, "rip", rp->r_rip);
printf(fmt, " cs", rp->r_cs, "rfl", rp->r_rfl, "rsp", rp->r_rsp);
printf("\t%3s: %16lx\n", " ss", rp->r_ss);
}
static int
instr_is_iret(caddr_t pc)
{
#if defined(__xpv)
extern void nopop_sys_rtt_syscall(void);
return ((pc == (caddr_t)nopop_sys_rtt_syscall) ? 1 : 0);
#else
static const uint8_t iret_insn[2] = { 0x48, 0xcf };
return (bcmp(pc, iret_insn, sizeof (iret_insn)) == 0);
#endif
}
static int
instr_is_sys_rtt(caddr_t pc)
{
extern void _sys_rtt(), _sys_rtt_end();
#if !defined(__xpv)
extern void tr_sysc_ret_start(), tr_sysc_ret_end();
extern void tr_intr_ret_start(), tr_intr_ret_end();
if ((uintptr_t)pc >= (uintptr_t)tr_sysc_ret_start &&
(uintptr_t)pc <= (uintptr_t)tr_sysc_ret_end)
return (1);
if ((uintptr_t)pc >= (uintptr_t)tr_intr_ret_start &&
(uintptr_t)pc <= (uintptr_t)tr_intr_ret_end)
return (1);
#endif
if ((uintptr_t)pc < (uintptr_t)_sys_rtt ||
(uintptr_t)pc > (uintptr_t)_sys_rtt_end)
return (0);
return (1);
}
static int
kern_gpfault(struct regs *rp)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
klwp_t *lwp = ttolwp(t);
struct regs tmpregs, *trp = NULL;
caddr_t pc = (caddr_t)rp->r_pc;
int v;
uint32_t auditing = AU_AUDITING();
if (lwp == NULL || !instr_is_sys_rtt(pc))
return (1);
if (instr_is_iret(pc)) {
trp = &tmpregs;
trp->r_ss = lwptoregs(lwp)->r_ss;
trp->r_sp = lwptoregs(lwp)->r_sp;
trp->r_ps = lwptoregs(lwp)->r_ps;
trp->r_cs = lwptoregs(lwp)->r_cs;
trp->r_pc = lwptoregs(lwp)->r_pc;
bcopy(rp, trp, offsetof(struct regs, r_pc));
ASSERT(trp->r_pc == lwptoregs(lwp)->r_pc);
ASSERT(trp->r_err == rp->r_err);
}
if (trp == NULL && PCB_NEED_UPDATE_SEGS(&lwp->lwp_pcb)) {
trp = lwptoregs(lwp);
ASSERT((caddr_t)trp == (caddr_t)rp->r_sp);
}
if (trp == NULL)
return (1);
trp->r_trapno = rp->r_trapno;
trp->r_err = rp->r_err;
if ((caddr_t)trp != (caddr_t)lwptoregs(lwp))
bcopy(trp, lwptoregs(lwp), sizeof (*trp));
mutex_enter(&p->p_lock);
lwp->lwp_cursig = SIGSEGV;
mutex_exit(&p->p_lock);
proc_is_exiting(p);
if (exitlwps(1) != 0) {
mutex_enter(&p->p_lock);
lwp_exit();
}
if (auditing)
audit_core_start(SIGSEGV);
v = core(SIGSEGV, B_FALSE);
if (auditing)
audit_core_finish(v ? CLD_KILLED : CLD_DUMPED);
exit(v ? CLD_KILLED : CLD_DUMPED, SIGSEGV);
return (0);
}
#if !defined(__xpv)
static void
dump_tss(void)
{
const char tss_fmt[] = "tss.%s:\t0x%p\n";
tss_t *tss = CPU->cpu_tss;
printf(tss_fmt, "tss_rsp0", (void *)tss->tss_rsp0);
printf(tss_fmt, "tss_rsp1", (void *)tss->tss_rsp1);
printf(tss_fmt, "tss_rsp2", (void *)tss->tss_rsp2);
printf(tss_fmt, "tss_ist1", (void *)tss->tss_ist1);
printf(tss_fmt, "tss_ist2", (void *)tss->tss_ist2);
printf(tss_fmt, "tss_ist3", (void *)tss->tss_ist3);
printf(tss_fmt, "tss_ist4", (void *)tss->tss_ist4);
printf(tss_fmt, "tss_ist5", (void *)tss->tss_ist5);
printf(tss_fmt, "tss_ist6", (void *)tss->tss_ist6);
printf(tss_fmt, "tss_ist7", (void *)tss->tss_ist7);
}
#endif
#if defined(TRAPTRACE)
int ttrace_nrec = 10;
int ttrace_dump_nregs = 0;
static void
dump_ttrace(void)
{
trap_trace_ctl_t *ttc;
trap_trace_rec_t *rec;
uintptr_t current;
int i, j;
int n = NCPU;
const char banner[] =
"CPU ADDRESS TIMESTAMP TYPE VC HANDLER PC\n";
const char fmt1[] = "%3d %016lx %12llx";
char data1[34];
const char fmt2[] = "%4s %3x";
const char fmt2s[] = "%4s %3s";
char data2[9];
const char fmt3h[] = "#%-15s";
const char fmt3p[] = "%-16p";
const char fmt3s[] = "%-16s";
char data3[17];
if (ttrace_nrec == 0)
return;
printf("\n");
printf(banner);
for (i = 0; i < n; i++) {
ttc = &trap_trace_ctl[i];
if (ttc->ttc_first == (uintptr_t)NULL)
continue;
current = ttc->ttc_next - sizeof (trap_trace_rec_t);
for (j = 0; j < ttrace_nrec; j++) {
struct sysent *sys;
struct autovec *vec;
extern struct av_head autovect[];
int type;
ulong_t off;
char *sym, *stype;
if (current < ttc->ttc_first)
current =
ttc->ttc_limit - sizeof (trap_trace_rec_t);
if (current == (uintptr_t)NULL)
continue;
rec = (trap_trace_rec_t *)current;
if (rec->ttr_stamp == 0)
break;
(void) snprintf(data1, sizeof (data1), fmt1, i,
(uintptr_t)rec, rec->ttr_stamp);
switch (rec->ttr_marker) {
case TT_SYSCALL:
case TT_SYSENTER:
case TT_SYSC:
case TT_SYSC64:
sys = &sysent32[rec->ttr_sysnum];
switch (rec->ttr_marker) {
case TT_SYSC64:
sys = &sysent[rec->ttr_sysnum];
case TT_SYSC:
stype = "sysc";
break;
case TT_SYSCALL:
stype = "lcal";
break;
case TT_SYSENTER:
stype = "syse";
break;
default:
stype = "";
break;
}
(void) snprintf(data2, sizeof (data2), fmt2,
stype, rec->ttr_sysnum);
if (sys != NULL) {
sym = kobj_getsymname(
(uintptr_t)sys->sy_callc,
&off);
if (sym != NULL) {
(void) snprintf(data3,
sizeof (data3), fmt3s, sym);
} else {
(void) snprintf(data3,
sizeof (data3), fmt3p,
sys->sy_callc);
}
} else {
(void) snprintf(data3, sizeof (data3),
fmt3s, "unknown");
}
break;
case TT_INTERRUPT:
if (rec->ttr_regs.r_trapno == T_SOFTINT) {
(void) snprintf(data2, sizeof (data2),
fmt2s, "intr", "-");
(void) snprintf(data3, sizeof (data3),
fmt3s, "(fakesoftint)");
break;
}
(void) snprintf(data2, sizeof (data2), fmt2,
"intr", rec->ttr_vector);
if (get_intr_handler != NULL)
vec = (struct autovec *)
(*get_intr_handler)
(rec->ttr_cpuid, rec->ttr_vector);
else
vec =
autovect[rec->ttr_vector].avh_link;
if (vec != NULL) {
sym = kobj_getsymname(
(uintptr_t)vec->av_vector, &off);
if (sym != NULL) {
(void) snprintf(data3,
sizeof (data3), fmt3s, sym);
} else {
(void) snprintf(data3,
sizeof (data3), fmt3p,
vec->av_vector);
}
} else {
(void) snprintf(data3, sizeof (data3),
fmt3s, "unknown");
}
break;
case TT_TRAP:
case TT_EVENT:
type = rec->ttr_regs.r_trapno;
(void) snprintf(data2, sizeof (data2), fmt2,
"trap", type);
if (type < TRAP_TYPES) {
(void) snprintf(data3, sizeof (data3),
fmt3h, trap_type_mnemonic[type]);
} else {
switch (type) {
case T_AST:
(void) snprintf(data3,
sizeof (data3), fmt3s,
"ast");
break;
default:
(void) snprintf(data3,
sizeof (data3), fmt3s, "");
break;
}
}
break;
default:
break;
}
sym = kobj_getsymname(rec->ttr_regs.r_pc, &off);
if (sym != NULL) {
printf("%s %s %s %s+%lx\n", data1, data2, data3,
sym, off);
} else {
printf("%s %s %s %lx\n", data1, data2, data3,
rec->ttr_regs.r_pc);
}
if (ttrace_dump_nregs-- > 0) {
int s;
if (rec->ttr_marker == TT_INTERRUPT)
printf(
"\t\tipl %x spl %x pri %x\n",
rec->ttr_ipl,
rec->ttr_spl,
rec->ttr_pri);
dumpregs(&rec->ttr_regs);
printf("\t%3s: %p\n\n", " ct",
(void *)rec->ttr_curthread);
for (s = 0; s < rec->ttr_sdepth; s++) {
uintptr_t fullpc;
if (s >= TTR_STACK_DEPTH) {
printf("ttr_sdepth corrupt\n");
break;
}
fullpc = (uintptr_t)rec->ttr_stack[s];
sym = kobj_getsymname(fullpc, &off);
if (sym != NULL)
printf("-> %s+0x%lx()\n",
sym, off);
else
printf("-> 0x%lx()\n", fullpc);
}
printf("\n");
}
current -= sizeof (trap_trace_rec_t);
}
}
}
#endif
void
panic_showtrap(struct panic_trap_info *tip)
{
showregs(tip->trap_type, tip->trap_regs, tip->trap_addr);
#if defined(TRAPTRACE)
dump_ttrace();
#endif
#if !defined(__xpv)
if (tip->trap_type == T_DBLFLT)
dump_tss();
#endif
}
void
panic_savetrap(panic_data_t *pdp, struct panic_trap_info *tip)
{
panic_saveregs(pdp, tip->trap_regs);
}