#include "use_isa.h"
#include "opt_ddb.h"
#include "opt_ktrace.h"
#include <machine/frame.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kerneldump.h>
#include <sys/proc.h>
#include <sys/pioctl.h>
#include <sys/types.h>
#include <sys/signal2.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <sys/ktr.h>
#include <sys/sysmsg.h>
#include <vm/pmap.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/vm_param.h>
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/smp.h>
#include <machine/specialreg.h>
#include <machine/thread.h>
#include <machine/clock.h>
#include <machine/vmparam.h>
#include <machine/md_var.h>
#include <machine_base/isa/isa_intr.h>
#include <machine_base/apic/lapic.h>
#include <ddb/ddb.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
extern void Xbpt(void);
extern void Xfast_syscall(void);
#define IDTVEC(vec) X##vec
extern void trap(struct trapframe *frame);
static int trap_pfault(struct trapframe *, int);
static void trap_fatal(struct trapframe *, vm_offset_t);
void dblfault_handler(struct trapframe *frame);
#define MAX_TRAP_MSG 30
static char *trap_msg[] = {
"",
"privileged instruction fault",
"",
"breakpoint instruction fault",
"",
"",
"arithmetic trap",
"system forced exception",
"",
"general protection fault",
"trace trap",
"",
"page fault",
"",
"alignment fault",
"",
"",
"",
"integer divide fault",
"non-maskable interrupt trap",
"overflow trap",
"FPU bounds check fault",
"FPU device not available",
"double fault",
"FPU operand fetch fault",
"invalid TSS fault",
"segment not present fault",
"stack fault",
"machine check trap",
"SIMD floating-point exception",
"reserved (unknown) fault",
};
#ifdef DDB
static int ddb_on_nmi = 1;
SYSCTL_INT(_machdep, OID_AUTO, ddb_on_nmi, CTLFLAG_RW,
&ddb_on_nmi, 0, "Go to DDB on NMI");
static int ddb_on_seg_fault = 0;
SYSCTL_INT(_machdep, OID_AUTO, ddb_on_seg_fault, CTLFLAG_RW,
&ddb_on_seg_fault, 0, "Go to DDB on user seg-fault");
__read_mostly static int freeze_on_seg_fault = 0;
SYSCTL_INT(_machdep, OID_AUTO, freeze_on_seg_fault, CTLFLAG_RW,
&freeze_on_seg_fault, 0, "Go to DDB on user seg-fault");
#endif
static int panic_on_nmi = 1;
SYSCTL_INT(_machdep, OID_AUTO, panic_on_nmi, CTLFLAG_RW,
&panic_on_nmi, 0, "Panic on NMI");
#ifdef SYSCALL_DEBUG
#define SCWC_MAXT 30
struct syscallwc {
uint32_t idx;
uint32_t dummy;
uint64_t tot[SYS_MAXSYSCALL];
uint64_t timings[SYS_MAXSYSCALL][SCWC_MAXT];
} __cachealign;
struct syscallwc SysCallsWorstCase[MAXCPU];
#endif
static __inline void
userenter(struct thread *curtd, struct proc *curp)
{
struct ucred *ocred;
struct ucred *ncred;
curtd->td_release = lwkt_passive_release;
if (__predict_false(curtd->td_ucred != curp->p_ucred)) {
spin_lock(&curp->p_spin);
ncred = crhold(curp->p_ucred);
spin_unlock(&curp->p_spin);
ocred = curtd->td_ucred;
curtd->td_ucred = ncred;
if (ocred)
crfree(ocred);
}
}
static void
userret(struct lwp *lp, struct trapframe *frame, int sticks)
{
struct proc *p = lp->lwp_proc;
int sig;
int ptok;
if (__predict_false(p->p_flags & P_PROFIL)) {
addupc_task(p, frame->tf_rip,
(u_int)((int)lp->lwp_thread->td_sticks - sticks));
}
recheck:
if (lp->lwp_mpflags & LWP_MP_URETMASK)
lwpuserret(lp);
if (__predict_false(STOPLWP(p, lp))) {
lwkt_gettoken(&p->p_token);
tstop();
lwkt_reltoken(&p->p_token);
goto recheck;
}
while (__predict_false(dump_stop_usertds)) {
tsleep(&dump_stop_usertds, 0, "dumpstp", 0);
}
if (__predict_false(p->p_flags & (P_SIGVTALRM | P_SIGPROF))) {
lwkt_gettoken(&p->p_token);
if (p->p_flags & P_SIGVTALRM) {
p->p_flags &= ~P_SIGVTALRM;
ksignal(p, SIGVTALRM);
}
if (p->p_flags & P_SIGPROF) {
p->p_flags &= ~P_SIGPROF;
ksignal(p, SIGPROF);
}
lwkt_reltoken(&p->p_token);
goto recheck;
}
if (__predict_false((sig = CURSIG_LCK_TRACE(lp, &ptok)) != 0)) {
postsig(sig, ptok);
goto recheck;
}
if (__predict_false(lp->lwp_flags & LWP_OLDMASK)) {
lp->lwp_flags &= ~LWP_OLDMASK;
lp->lwp_sigmask = lp->lwp_oldsigmask;
goto recheck;
}
}
static __inline void
userexit(struct lwp *lp)
{
struct thread *td = lp->lwp_thread;
while (__predict_false(STOPLWP(lp->lwp_proc, lp))) {
lwkt_gettoken(&lp->lwp_proc->p_token);
tstop();
lwkt_reltoken(&lp->lwp_proc->p_token);
}
lwkt_passive_recover(td);
if (lp->lwp_proc->p_usched == &usched_dfly)
dfly_acquire_curproc(lp);
else
lp->lwp_proc->p_usched->acquire_curproc(lp);
}
static int
trap_is_smap(struct trapframe *frame)
{
if ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 &&
(frame->tf_err & (PGEX_P | PGEX_U | PGEX_I | PGEX_RSV)) == PGEX_P &&
(frame->tf_rflags & PSL_AC) == 0) {
return 1;
} else {
return 0;
}
}
#if !defined(KTR_KERNENTRY)
#define KTR_KERNENTRY KTR_ALL
#endif
KTR_INFO_MASTER(kernentry);
KTR_INFO(KTR_KERNENTRY, kernentry, trap, 0,
"TRAP(pid %d, tid %d, trapno %ld, eva %lu)",
pid_t pid, lwpid_t tid, register_t trapno, vm_offset_t eva);
KTR_INFO(KTR_KERNENTRY, kernentry, trap_ret, 0, "TRAP_RET(pid %d, tid %d)",
pid_t pid, lwpid_t tid);
KTR_INFO(KTR_KERNENTRY, kernentry, syscall, 0, "SYSC(pid %d, tid %d, nr %ld)",
pid_t pid, lwpid_t tid, register_t trapno);
KTR_INFO(KTR_KERNENTRY, kernentry, syscall_ret, 0, "SYSRET(pid %d, tid %d, err %d)",
pid_t pid, lwpid_t tid, int err);
KTR_INFO(KTR_KERNENTRY, kernentry, fork_ret, 0, "FORKRET(pid %d, tid %d)",
pid_t pid, lwpid_t tid);
void
trap(struct trapframe *frame)
{
static struct krate sscpubugrate = { 1 };
struct globaldata *gd = mycpu;
struct thread *td = gd->gd_curthread;
struct lwp *lp = td->td_lwp;
struct proc *p;
int sticks = 0;
int i = 0, ucode = 0, type, code;
#ifdef INVARIANTS
int crit_count = td->td_critcount;
lwkt_tokref_t curstop = td->td_toks_stop;
#endif
vm_offset_t eva;
p = td->td_proc;
clear_quickret();
#ifdef DDB
if (db_active && frame->tf_trapno != T_DNA) {
eva = (frame->tf_trapno == T_PAGEFLT ? frame->tf_addr : 0);
++gd->gd_trap_nesting_level;
trap_fatal(frame, eva);
--gd->gd_trap_nesting_level;
goto out2;
}
#endif
eva = 0;
if ((frame->tf_rflags & PSL_I) == 0) {
type = frame->tf_trapno;
if (ISPL(frame->tf_cs) == SEL_UPL) {
kprintf(
"pid %ld (%s): trap %d with interrupts disabled\n",
(long)curproc->p_pid, curproc->p_comm, type);
} else if ((type == T_STKFLT || type == T_PROTFLT ||
type == T_SEGNPFLT) &&
frame->tf_rip == (long)doreti_iret) {
} else if (type != T_NMI && type != T_BPTFLT &&
type != T_TRCTRAP) {
kprintf("kernel trap %d (%s @ 0x%016jx) with "
"interrupts disabled\n",
type,
td->td_comm,
frame->tf_rip);
}
cpu_enable_intr();
}
type = frame->tf_trapno;
code = frame->tf_err;
if (ISPL(frame->tf_cs) == SEL_UPL) {
KTR_LOG(kernentry_trap, p->p_pid, lp->lwp_tid,
frame->tf_trapno, eva);
userenter(td, p);
sticks = (int)td->td_sticks;
KASSERT(lp->lwp_md.md_regs == frame,
("Frame mismatch %p %p", lp->lwp_md.md_regs, frame));
switch (type) {
case T_PRIVINFLT:
i = SIGILL;
ucode = ILL_PRVOPC;
break;
case T_BPTFLT:
case T_TRCTRAP:
frame->tf_rflags &= ~PSL_T;
i = SIGTRAP;
ucode = (type == T_TRCTRAP ? TRAP_TRACE : TRAP_BRKPT);
break;
case T_ARITHTRAP:
ucode = code;
i = SIGFPE;
break;
case T_ASTFLT:
mycpu->gd_cnt.v_soft++;
if (mycpu->gd_reqflags & RQF_AST_OWEUPC) {
atomic_clear_int(&mycpu->gd_reqflags,
RQF_AST_OWEUPC);
addupc_task(p, p->p_prof.pr_addr,
p->p_prof.pr_ticks);
}
goto out;
case T_PROTFLT:
i = SIGBUS;
ucode = BUS_OBJERR;
break;
case T_STKFLT:
case T_SEGNPFLT:
i = SIGBUS;
ucode = BUS_ADRERR;
break;
case T_TSSFLT:
case T_DOUBLEFLT:
default:
i = SIGBUS;
ucode = BUS_OBJERR;
break;
case T_PAGEFLT:
i = trap_pfault(frame, TRUE);
#ifdef DDB
if (frame->tf_rip == 0) {
while (freeze_on_seg_fault)
tsleep(p, 0, "freeze", hz * 20);
}
#endif
if (i == -1 || i == 0)
goto out;
if (i == SIGSEGV) {
ucode = SEGV_MAPERR;
} else {
i = SIGSEGV;
ucode = SEGV_ACCERR;
}
break;
case T_DIVIDE:
ucode = FPE_INTDIV;
i = SIGFPE;
break;
#if NISA > 0
case T_NMI:
if (isa_nmi(code) == 0) {
#ifdef DDB
if (ddb_on_nmi) {
kprintf ("NMI ... going to debugger\n");
kdb_trap(type, 0, frame);
}
#endif
goto out2;
} else if (panic_on_nmi)
panic("NMI indicates hardware failure");
break;
#endif
case T_OFLOW:
ucode = FPE_INTOVF;
i = SIGFPE;
break;
case T_BOUND:
ucode = FPE_FLTSUB;
i = SIGFPE;
break;
case T_DNA:
if (lp->lwp_vkernel && lp->lwp_vkernel->ve &&
(td->td_pcb->pcb_flags & FP_VIRTFP)
) {
npxdna();
break;
}
if (npxdna()) {
gd->gd_cnt.v_trap++;
goto out;
}
i = SIGFPE;
ucode = FPE_FPU_NP_TRAP;
break;
case T_FPOPFLT:
ucode = ILL_COPROC;
i = SIGILL;
break;
case T_XMMFLT:
ucode = 0;
i = SIGFPE;
break;
}
} else {
switch (type) {
case T_PAGEFLT:
trap_pfault(frame, FALSE);
goto out2;
case T_DNA:
if (npxdna()) {
gd->gd_cnt.v_trap++;
goto out2;
}
break;
case T_STKFLT:
case T_PROTFLT:
case T_SEGNPFLT:
if (mycpu->gd_intr_nesting_level == 0) {
if (td->td_pcb->pcb_onfault &&
td->td_pcb->pcb_onfault_sp ==
frame->tf_rsp) {
frame->tf_rip = (register_t)
td->td_pcb->pcb_onfault;
goto out2;
}
if (frame->tf_rip == (long)doreti_iret) {
frame->tf_rip = (long)doreti_iret_fault;
goto out2;
}
}
break;
case T_TSSFLT:
if (frame->tf_rflags & PSL_NT) {
frame->tf_rflags &= ~PSL_NT;
#if 0
if (frame->tf_rip == (long)doreti_iret)
frame->tf_rip = (long)doreti_iret_fault;
#endif
goto out2;
}
break;
case T_TRCTRAP:
gd->gd_cnt.v_trap++;
if (frame->tf_rip == (register_t)IDTVEC(fast_syscall)) {
krateprintf(&sscpubugrate,
"Caught #DB at syscall cpu artifact\n");
goto out2;
}
if (frame->tf_rip == (register_t)IDTVEC(bpt)) {
krateprintf(&sscpubugrate,
"Caught #DB at int $N cpu artifact\n");
goto out2;
}
if (user_dbreg_trap()) {
load_dr6(rdr6() & ~0xf);
goto out2;
}
case T_BPTFLT:
ucode = TRAP_BRKPT;
#ifdef DDB
if (kdb_trap(type, 0, frame))
goto out2;
#endif
break;
#if NISA > 0
case T_NMI:
if (isa_nmi(code) == 0) {
#ifdef DDB
if (ddb_on_nmi) {
kprintf ("NMI ... going to debugger\n");
kdb_trap(type, 0, frame);
}
#endif
goto out2;
} else if (panic_on_nmi == 0)
goto out2;
#endif
break;
default:
if (type >= T_RESERVED && type < T_RESERVED + 256) {
kprintf("Ignoring spurious unknown "
"cpu trap T_RESERVED+%d\n",
type - T_RESERVED);
gd->gd_cnt.v_trap++;
goto out2;
}
break;
}
trap_fatal(frame, 0);
goto out2;
}
if (lp->lwp_vkernel && lp->lwp_vkernel->ve) {
vkernel_trap(lp, frame);
goto out;
}
if (*p->p_sysent->sv_transtrap)
i = (*p->p_sysent->sv_transtrap)(i, type);
gd->gd_cnt.v_trap++;
trapsignal(lp, i, ucode);
#ifdef DEBUG
if (type <= MAX_TRAP_MSG) {
uprintf("fatal process exception: %s",
trap_msg[type]);
if ((type == T_PAGEFLT) || (type == T_PROTFLT))
uprintf(", fault VA = 0x%lx", frame->tf_addr);
uprintf("\n");
}
#endif
out:
userret(lp, frame, sticks);
userexit(lp);
out2: ;
if (p != NULL && lp != NULL)
KTR_LOG(kernentry_trap_ret, p->p_pid, lp->lwp_tid);
#ifdef INVARIANTS
KASSERT(crit_count == td->td_critcount,
("trap: critical section count mismatch! %d/%d",
crit_count, td->td_critcount));
KASSERT(curstop == td->td_toks_stop,
("trap: extra tokens held after trap! %ld/%ld (%s)",
curstop - &td->td_toks_base,
td->td_toks_stop - &td->td_toks_base,
td->td_toks_stop[-1].tr_tok->t_desc));
#endif
}
void
trap_handle_userenter(struct thread *td)
{
userenter(td, td->td_proc);
}
void
trap_handle_userexit(struct trapframe *frame, int sticks)
{
struct lwp *lp = curthread->td_lwp;
if (lp) {
userret(lp, frame, sticks);
userexit(lp);
}
}
static int
trap_pfault(struct trapframe *frame, int usermode)
{
vm_offset_t va;
struct vmspace *vm = NULL;
vm_map_t map;
int rv = 0;
int fault_flags;
vm_prot_t ftype;
thread_t td = curthread;
struct lwp *lp = td->td_lwp;
struct proc *p;
va = trunc_page(frame->tf_addr);
if (va >= VM_MIN_KERNEL_ADDRESS) {
if (usermode) {
fault_flags = -1;
ftype = -1;
goto nogo;
}
map = kernel_map;
} else {
if (lp != NULL)
vm = lp->lwp_vmspace;
if (vm == NULL) {
fault_flags = -1;
ftype = -1;
goto nogo;
}
if (usermode == 0) {
#ifdef DDB
if (td->td_pcb == NULL ||
td->td_pcb->pcb_onfault == NULL) {
if (freeze_on_seg_fault) {
kprintf("trap_pfault: user address "
"fault from kernel mode "
"%016lx\n",
(long)frame->tf_addr);
while (freeze_on_seg_fault) {
tsleep(&freeze_on_seg_fault,
0,
"frzseg",
hz * 20);
}
}
}
#endif
if (td->td_gd->gd_intr_nesting_level ||
trap_is_smap(frame) ||
td->td_pcb == NULL ||
td->td_pcb->pcb_onfault == NULL) {
kprintf("Fatal user address access "
"from kernel mode from %s at %016jx\n",
td->td_comm, frame->tf_rip);
trap_fatal(frame, frame->tf_addr);
return (-1);
}
}
map = &vm->vm_map;
}
if (frame->tf_err & PGEX_W)
ftype = VM_PROT_WRITE;
else if (frame->tf_err & PGEX_I)
ftype = VM_PROT_EXECUTE;
else
ftype = VM_PROT_READ;
lwkt_tokref_t stop = td->td_toks_stop;
if (map != kernel_map) {
PHOLD(lp->lwp_proc);
fault_flags = 0;
if (usermode)
fault_flags |= VM_FAULT_BURST | VM_FAULT_USERMODE;
if (ftype & VM_PROT_WRITE)
fault_flags |= VM_FAULT_DIRTY;
else
fault_flags |= VM_FAULT_NORMAL;
rv = vm_fault(map, va, ftype, fault_flags);
if (td->td_toks_stop != stop) {
stop = td->td_toks_stop - 1;
kprintf("A-HELD TOKENS DURING PFAULT td=%p(%s) map=%p va=%p ftype=%d fault_flags=%d\n", td, td->td_comm, map, (void *)va, ftype, fault_flags);
panic("held tokens");
}
PRELE(lp->lwp_proc);
} else {
fault_flags = VM_FAULT_NORMAL;
rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
if (td->td_toks_stop != stop) {
stop = td->td_toks_stop - 1;
kprintf("B-HELD TOKENS DURING PFAULT td=%p(%s) map=%p va=%p ftype=%d fault_flags=%d\n", td, td->td_comm, map, (void *)va, ftype, VM_FAULT_NORMAL);
panic("held tokens");
}
}
if (rv == KERN_SUCCESS)
return (0);
nogo:
if (!usermode) {
if (td->td_pcb->pcb_onfault &&
td->td_pcb->pcb_onfault_sp == frame->tf_rsp &&
td->td_gd->gd_intr_nesting_level == 0) {
frame->tf_rip = (register_t)td->td_pcb->pcb_onfault;
return (0);
}
trap_fatal(frame, frame->tf_addr);
return (-1);
}
p = td->td_proc;
#ifdef DDB
if (td->td_lwp->lwp_vkernel == NULL) {
while (freeze_on_seg_fault) {
tsleep(p, 0, "freeze", hz * 20);
}
if (ddb_on_seg_fault)
Debugger("ddb_on_seg_fault");
}
#endif
return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
}
static void
trap_fatal(struct trapframe *frame, vm_offset_t eva)
{
int code, ss;
u_int type;
long rsp;
struct soft_segment_descriptor softseg;
code = frame->tf_err;
type = frame->tf_trapno;
sdtossd(&mdcpu->gd_gdt[IDXSEL(frame->tf_cs & 0xffff)], &softseg);
kprintf("\n\nFatal trap %d: ", type);
if (type <= MAX_TRAP_MSG)
kprintf("%s ", trap_msg[type]);
else
kprintf("rsvd(%d) ", type - T_RESERVED);
kprintf("while in %s mode\n",
ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel");
kprintf("cpuid = %d; ", mycpu->gd_cpuid);
if (lapic_usable)
kprintf("lapic id = %u\n", LAPIC_READID);
if (type == T_PAGEFLT) {
kprintf("fault virtual address = 0x%lx\n", eva);
kprintf("fault code = %s %s %s, %s\n",
code & PGEX_U ? "user" : "supervisor",
code & PGEX_W ? "write" : "read",
code & PGEX_I ? "instruction" : "data",
code & PGEX_P ? "protection violation" : "page not present");
}
kprintf("instruction pointer = 0x%lx:0x%lx\n",
frame->tf_cs & 0xffff, frame->tf_rip);
if (ISPL(frame->tf_cs) == SEL_UPL) {
ss = frame->tf_ss & 0xffff;
rsp = frame->tf_rsp;
} else {
ss = GSEL(GDATA_SEL, SEL_KPL);
rsp = frame->tf_rsp;
}
kprintf("stack pointer = 0x%x:0x%lx\n", ss, rsp);
kprintf("frame pointer = 0x%x:0x%lx\n", ss, frame->tf_rbp);
kprintf("code segment = base 0x%lx, limit 0x%lx, type 0x%x\n",
softseg.ssd_base, softseg.ssd_limit, softseg.ssd_type);
kprintf(" = DPL %d, pres %d, long %d, def32 %d, gran %d\n",
softseg.ssd_dpl, softseg.ssd_p, softseg.ssd_long, softseg.ssd_def32,
softseg.ssd_gran);
kprintf("processor eflags = ");
if (frame->tf_rflags & PSL_T)
kprintf("trace trap, ");
if (frame->tf_rflags & PSL_I)
kprintf("interrupt enabled, ");
if (frame->tf_rflags & PSL_NT)
kprintf("nested task, ");
if (frame->tf_rflags & PSL_RF)
kprintf("resume, ");
if (frame->tf_rflags & PSL_AC)
kprintf("smap_open, ");
kprintf("IOPL = %ld\n", (frame->tf_rflags & PSL_IOPL) >> 12);
kprintf("current process = ");
if (curproc) {
kprintf("%lu\n",
(u_long)curproc->p_pid);
} else {
kprintf("Idle\n");
}
kprintf("current thread = pri %d ", curthread->td_pri);
if (curthread->td_critcount)
kprintf("(CRIT)");
kprintf("\n");
#ifdef DDB
if ((debugger_on_panic || db_active) && kdb_trap(type, code, frame))
return;
#endif
kprintf("trap number = %d\n", type);
if (type <= MAX_TRAP_MSG)
panic("%s", trap_msg[type]);
else
panic("unknown/reserved trap");
}
static __inline
int
in_kstack_guard(register_t rptr)
{
thread_t td = curthread;
if ((char *)rptr >= td->td_kstack &&
(char *)rptr < td->td_kstack + PAGE_SIZE) {
return 1;
}
return 0;
}
void
dblfault_handler(struct trapframe *frame)
{
thread_t td = curthread;
if (in_kstack_guard(frame->tf_rsp) || in_kstack_guard(frame->tf_rbp)) {
kprintf("DOUBLE FAULT - KERNEL STACK GUARD HIT!\n");
if (in_kstack_guard(frame->tf_rsp))
frame->tf_rsp = (register_t)(td->td_kstack + PAGE_SIZE);
if (in_kstack_guard(frame->tf_rbp))
frame->tf_rbp = (register_t)(td->td_kstack + PAGE_SIZE);
} else {
kprintf("DOUBLE FAULT\n");
}
kprintf("\nFatal double fault\n");
kprintf("rip = 0x%lx\n", frame->tf_rip);
kprintf("rsp = 0x%lx\n", frame->tf_rsp);
kprintf("rbp = 0x%lx\n", frame->tf_rbp);
kprintf("cpuid = %d; ", mycpu->gd_cpuid);
if (lapic_usable)
kprintf("lapic id = %u\n", LAPIC_READID);
panic("double fault");
}
void
syscall2(struct trapframe *frame)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct lwp *lp = td->td_lwp;
struct sysent *callp;
register_t orig_tf_rflags;
int sticks;
int error;
int narg;
#ifdef INVARIANTS
int crit_count = td->td_critcount;
#endif
struct sysmsg sysmsg;
union sysunion *argp;
u_int code;
const int regcnt = 6;
mycpu->gd_cnt.v_syscall++;
#ifdef DIAGNOSTIC
if (__predict_false(ISPL(frame->tf_cs) != SEL_UPL)) {
panic("syscall");
}
#endif
KTR_LOG(kernentry_syscall, p->p_pid, lp->lwp_tid,
frame->tf_rax);
userenter(td, p);
sticks = (int)td->td_sticks;
orig_tf_rflags = frame->tf_rflags;
if (__predict_false(lp->lwp_vkernel && lp->lwp_vkernel->ve)) {
vkernel_trap(lp, frame);
error = EJUSTRETURN;
callp = NULL;
code = 0;
goto out;
}
#ifdef DIAGNOSTIC
KASSERT(lp->lwp_md.md_regs == frame,
("Frame mismatch %p %p", lp->lwp_md.md_regs, frame));
#endif
code = (u_int)frame->tf_rax;
if (code >= p->p_sysent->sv_size)
code = SYS___nosys;
argp = (union sysunion *)&frame->tf_rdi;
callp = &p->p_sysent->sv_table[code];
narg = callp->sy_narg;
if (__predict_false(narg > regcnt)) {
register_t *argsdst;
caddr_t params;
argsdst = (register_t *)&sysmsg.extargs;
bcopy(argp, argsdst, sizeof(register_t) * regcnt);
params = (caddr_t)frame->tf_rsp + sizeof(register_t);
error = copyin(params, &argsdst[regcnt],
(narg - regcnt) * sizeof(register_t));
argp = (void *)argsdst;
if (error) {
#ifdef KTRACE
if (KTRPOINTP(p, td, KTR_SYSCALL)) {
ktrsyscall(lp, code, narg, argp);
}
#endif
goto bad;
}
}
#ifdef KTRACE
if (KTRPOINTP(p, td, KTR_SYSCALL)) {
ktrsyscall(lp, code, narg, argp);
}
#endif
sysmsg.sysmsg_fds[0] = 0;
sysmsg.sysmsg_fds[1] = frame->tf_rdx;
sysmsg.sysmsg_frame = frame;
STOPEVENT(p, S_SCE, narg);
#ifdef SYSCALL_DEBUG
tsc_uclock_t tscval = rdtsc();
#endif
error = (*callp->sy_call)(&sysmsg, argp);
#ifdef SYSCALL_DEBUG
tscval = rdtsc() - tscval;
tscval = tscval * 1000000 / (tsc_frequency / 1000);
{
struct syscallwc *scwc = &SysCallsWorstCase[mycpu->gd_cpuid];
int idx = scwc->idx++ % SCWC_MAXT;
scwc->tot[code] += tscval - scwc->timings[code][idx];
scwc->timings[code][idx] = tscval;
}
#endif
out:
if (__predict_true(error == 0)) {
p = curproc;
lp = curthread->td_lwp;
frame->tf_rax = sysmsg.sysmsg_fds[0];
frame->tf_rdx = sysmsg.sysmsg_fds[1];
frame->tf_rflags &= ~PSL_C;
} else if (error == ERESTART) {
if (frame->tf_err != 0 && frame->tf_err != 2)
kprintf("lp %s:%d frame->tf_err is weird %ld\n",
td->td_comm, lp->lwp_proc->p_pid, frame->tf_err);
frame->tf_rip -= frame->tf_err;
frame->tf_r10 = frame->tf_rcx;
} else if (error == EJUSTRETURN) {
} else if (error == EASYNC) {
panic("Unexpected EASYNC return value (for now)");
} else {
bad:
if (p->p_sysent->sv_errsize) {
if (error >= p->p_sysent->sv_errsize)
error = -1;
else
error = p->p_sysent->sv_errtbl[error];
}
frame->tf_rax = error;
frame->tf_rflags |= PSL_C;
}
if (__predict_false(orig_tf_rflags & PSL_T)) {
frame->tf_rflags &= ~PSL_T;
trapsignal(lp, SIGTRAP, TRAP_TRACE);
}
userret(lp, frame, sticks);
#ifdef KTRACE
if (KTRPOINTP(p, td, KTR_SYSRET)) {
ktrsysret(lp, code, error, sysmsg.sysmsg_result);
}
#endif
STOPEVENT(p, S_SCX, code);
userexit(lp);
KTR_LOG(kernentry_syscall_ret, p->p_pid, lp->lwp_tid, error);
#ifdef INVARIANTS
KASSERT(crit_count == td->td_critcount,
("syscall: critical section count mismatch! "
"%d/%d in %s sysno=%d",
crit_count, td->td_critcount, td->td_comm, code));
KASSERT(&td->td_toks_base == td->td_toks_stop,
("syscall: %ld extra tokens held after trap! syscall %p",
td->td_toks_stop - &td->td_toks_base,
callp->sy_call));
#endif
}
void xsyscall(struct sysmsg *sysmsg, struct nosys_args *uap);
int
sys_xsyscall(struct sysmsg *sysmsg, const struct nosys_args *uap)
{
struct trapframe *frame;
struct sysent *callp;
union sysunion *argp;
struct thread *td;
struct proc *p;
const int regcnt = 5;
u_int code;
int error;
int narg;
td = curthread;
p = td->td_proc;
frame = sysmsg->sysmsg_frame;
code = (u_int)frame->tf_rdi;
if (code >= p->p_sysent->sv_size)
code = SYS___nosys;
argp = (union sysunion *)(&frame->tf_rdi + 1);
callp = &p->p_sysent->sv_table[code];
narg = callp->sy_narg;
if (__predict_false(narg > regcnt)) {
register_t *argsdst;
caddr_t params;
argsdst = (register_t *)&sysmsg->extargs;
bcopy(argp, argsdst, sizeof(register_t) * regcnt);
params = (caddr_t)frame->tf_rsp + sizeof(register_t);
error = copyin(params, &argsdst[regcnt],
(narg - regcnt) * sizeof(register_t));
argp = (void *)argsdst;
if (error) {
#ifdef KTRACE
if (KTRPOINTP(p, td, KTR_SYSCALL)) {
ktrsyscall(td->td_lwp, code, narg, argp);
}
if (KTRPOINTP(p, td, KTR_SYSRET)) {
ktrsysret(td->td_lwp, code, error,
sysmsg->sysmsg_result);
}
#endif
return error;
}
}
#ifdef KTRACE
if (KTRPOINTP(p, td, KTR_SYSCALL)) {
ktrsyscall(td->td_lwp, code, narg, argp);
}
#endif
error = (*callp->sy_call)(sysmsg, argp);
#ifdef KTRACE
if (KTRPOINTP(p, td, KTR_SYSRET)) {
register_t rval;
rval = (callp->sy_rsize <= 4) ? sysmsg->sysmsg_result :
sysmsg->sysmsg_lresult;
ktrsysret(td->td_lwp, code, error, rval);
}
#endif
return error;
}
void
fork_return(struct lwp *lp, struct trapframe *frame)
{
frame->tf_rax = 0;
frame->tf_rflags &= ~PSL_C;
frame->tf_rdx = 1;
generic_lwp_return(lp, frame);
KTR_LOG(kernentry_fork_ret, lp->lwp_proc->p_pid, lp->lwp_tid);
}
void
generic_lwp_return(struct lwp *lp, struct trapframe *frame)
{
struct proc *p = lp->lwp_proc;
if (p->p_flags & P_WEXIT) {
lwpsignal(p, lp, SIGKILL);
}
lwkt_setpri_self(TDPRI_USER_NORM);
userenter(lp->lwp_thread, p);
userret(lp, frame, 0);
#ifdef KTRACE
if (KTRPOINTP(p, lp->lwp_thread, KTR_SYSRET))
ktrsysret(lp, SYS_fork, 0, 0);
#endif
lp->lwp_flags |= LWP_PASSIVE_ACQ;
userexit(lp);
lp->lwp_flags &= ~LWP_PASSIVE_ACQ;
}
void
set_vkernel_fp(struct trapframe *frame)
{
struct thread *td = curthread;
if (frame->tf_xflags & PGEX_FPFAULT) {
td->td_pcb->pcb_flags |= FP_VIRTFP;
if (mdcpu->gd_npxthread == td)
npxexit();
} else {
td->td_pcb->pcb_flags &= ~FP_VIRTFP;
}
}
void
cpu_vkernel_trap(struct trapframe *frame, int error)
{
frame->tf_rax = error;
if (error)
frame->tf_rflags |= PSL_C;
else
frame->tf_rflags &= ~PSL_C;
}