#include <sys/cdefs.h>
#include "opt_cpu.h"
#include "opt_ddb.h"
#include "opt_kstack_pages.h"
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/pcpu.h>
#include <sys/reg.h>
#include <sys/rwlock.h>
#include <sys/signalvar.h>
#include <sys/smp.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/ucontext.h>
#include <sys/vmmeter.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_extern.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#ifdef DDB
#ifndef KDB
#error KDB must be enabled in order for DDB to work!
#endif
#include <ddb/ddb.h>
#include <ddb/db_sym.h>
#endif
#include <machine/vmparam.h>
#include <machine/frame.h>
#include <machine/md_var.h>
#include <machine/pcb.h>
#include <machine/proc.h>
#include <machine/sigframe.h>
#include <machine/specialreg.h>
#include <machine/trap.h>
_Static_assert(sizeof(mcontext_t) == 800, "mcontext_t size incorrect");
_Static_assert(sizeof(ucontext_t) == 880, "ucontext_t size incorrect");
_Static_assert(sizeof(siginfo_t) == 80, "siginfo_t size incorrect");
static bool
is_seg_val(register_t r)
{
return ((uint64_t)r <= 0xffff);
}
void
sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
{
struct sigframe sf, *sfp;
struct pcb *pcb;
struct proc *p;
struct thread *td;
struct sigacts *psp;
char *sp;
struct trapframe *regs;
char *xfpusave;
size_t xfpusave_len;
int sig;
int oonstack;
td = curthread;
pcb = td->td_pcb;
p = td->td_proc;
PROC_LOCK_ASSERT(p, MA_OWNED);
sig = ksi->ksi_signo;
psp = p->p_sigacts;
mtx_assert(&psp->ps_mtx, MA_OWNED);
regs = td->td_frame;
oonstack = sigonstack(regs->tf_rsp);
bzero(&sf, sizeof(sf));
sf.sf_uc.uc_sigmask = *mask;
sf.sf_uc.uc_stack = td->td_sigstk;
sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK)
? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE;
sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0;
sf.sf_uc.uc_mcontext.mc_rdi = regs->tf_rdi;
sf.sf_uc.uc_mcontext.mc_rsi = regs->tf_rsi;
sf.sf_uc.uc_mcontext.mc_rdx = regs->tf_rdx;
sf.sf_uc.uc_mcontext.mc_rcx = regs->tf_rcx;
sf.sf_uc.uc_mcontext.mc_r8 = regs->tf_r8;
sf.sf_uc.uc_mcontext.mc_r9 = regs->tf_r9;
sf.sf_uc.uc_mcontext.mc_rax = regs->tf_rax;
sf.sf_uc.uc_mcontext.mc_rbx = regs->tf_rbx;
sf.sf_uc.uc_mcontext.mc_rbp = regs->tf_rbp;
sf.sf_uc.uc_mcontext.mc_r10 = regs->tf_r10;
sf.sf_uc.uc_mcontext.mc_r11 = regs->tf_r11;
sf.sf_uc.uc_mcontext.mc_r12 = regs->tf_r12;
sf.sf_uc.uc_mcontext.mc_r13 = regs->tf_r13;
sf.sf_uc.uc_mcontext.mc_r14 = regs->tf_r14;
sf.sf_uc.uc_mcontext.mc_r15 = regs->tf_r15;
sf.sf_uc.uc_mcontext.mc_trapno = regs->tf_trapno;
sf.sf_uc.uc_mcontext.mc_fs = regs->tf_fs;
sf.sf_uc.uc_mcontext.mc_gs = regs->tf_gs;
sf.sf_uc.uc_mcontext.mc_addr = regs->tf_addr;
sf.sf_uc.uc_mcontext.mc_flags = regs->tf_flags;
sf.sf_uc.uc_mcontext.mc_es = regs->tf_es;
sf.sf_uc.uc_mcontext.mc_ds = regs->tf_ds;
sf.sf_uc.uc_mcontext.mc_err = regs->tf_err;
sf.sf_uc.uc_mcontext.mc_rip = regs->tf_rip;
sf.sf_uc.uc_mcontext.mc_cs = regs->tf_cs;
sf.sf_uc.uc_mcontext.mc_rflags = regs->tf_rflags;
sf.sf_uc.uc_mcontext.mc_rsp = regs->tf_rsp;
sf.sf_uc.uc_mcontext.mc_ss = regs->tf_ss;
sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext);
get_fpcontext(td, &sf.sf_uc.uc_mcontext, &xfpusave, &xfpusave_len);
update_pcb_bases(pcb);
sf.sf_uc.uc_mcontext.mc_fsbase = pcb->pcb_fsbase;
sf.sf_uc.uc_mcontext.mc_gsbase = pcb->pcb_gsbase;
bzero(sf.sf_uc.uc_mcontext.mc_spare,
sizeof(sf.sf_uc.uc_mcontext.mc_spare));
if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack &&
SIGISMEMBER(psp->ps_sigonstack, sig)) {
sp = (char *)td->td_sigstk.ss_sp + td->td_sigstk.ss_size;
#if defined(COMPAT_43)
td->td_sigstk.ss_flags |= SS_ONSTACK;
#endif
} else
sp = (char *)regs->tf_rsp - REDZONE_SZ;
if (xfpusave != NULL) {
sp -= xfpusave_len;
sp = (char *)((unsigned long)sp & ~0x3Ful);
sf.sf_uc.uc_mcontext.mc_xfpustate = (register_t)sp;
}
sp -= sizeof(struct sigframe);
sfp = (struct sigframe *)STACKALIGN(sp);
regs->tf_rdi = sig;
regs->tf_rdx = (register_t)&sfp->sf_uc;
bzero(&sf.sf_si, sizeof(sf.sf_si));
if (SIGISMEMBER(psp->ps_siginfo, sig)) {
regs->tf_rsi = (register_t)&sfp->sf_si;
sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher;
sf.sf_si = ksi->ksi_info;
sf.sf_si.si_signo = sig;
regs->tf_rcx = (register_t)ksi->ksi_addr;
} else {
regs->tf_rsi = ksi->ksi_code;
regs->tf_rcx = (register_t)ksi->ksi_addr;
sf.sf_ahu.sf_handler = catcher;
}
mtx_unlock(&psp->ps_mtx);
PROC_UNLOCK(p);
if (copyout(&sf, sfp, sizeof(*sfp)) != 0 ||
(xfpusave != NULL && copyout(xfpusave,
(void *)sf.sf_uc.uc_mcontext.mc_xfpustate, xfpusave_len)
!= 0)) {
uprintf("pid %d comm %s has trashed its stack, killing\n",
p->p_pid, p->p_comm);
PROC_LOCK(p);
sigexit(td, SIGILL);
}
fpstate_drop(td);
regs->tf_rsp = (long)sfp;
regs->tf_rip = PROC_SIGCODE(p);
regs->tf_rflags &= ~(PSL_T | PSL_D);
regs->tf_cs = _ucodesel;
regs->tf_ds = _udatasel;
regs->tf_ss = _udatasel;
regs->tf_es = _udatasel;
regs->tf_fs = _ufssel;
regs->tf_gs = _ugssel;
regs->tf_flags = TF_HASSEGS;
if ((pcb->pcb_flags & PCB_TLSBASE) != 0)
pcb->pcb_fsbase = pcb->pcb_tlsbase;
PROC_LOCK(p);
mtx_lock(&psp->ps_mtx);
}
int
sys_sigreturn(struct thread *td, struct sigreturn_args *uap)
{
ucontext_t uc;
struct pcb *pcb;
struct proc *p;
struct trapframe *regs;
ucontext_t *ucp;
char *xfpustate;
size_t xfpustate_len;
long rflags;
int cs, error, ret;
ksiginfo_t ksi;
pcb = td->td_pcb;
p = td->td_proc;
error = copyin(uap->sigcntxp, &uc, sizeof(uc));
if (error != 0) {
uprintf("pid %d (%s): sigreturn copyin failed\n",
p->p_pid, td->td_name);
return (error);
}
ucp = &uc;
if ((ucp->uc_mcontext.mc_flags & ~_MC_FLAG_MASK) != 0) {
uprintf("pid %d (%s): sigreturn mc_flags %x\n", p->p_pid,
td->td_name, ucp->uc_mcontext.mc_flags);
return (EINVAL);
}
regs = td->td_frame;
rflags = ucp->uc_mcontext.mc_rflags;
if (!EFL_SECURE(rflags, regs->tf_rflags)) {
uprintf("pid %d (%s): sigreturn rflags = 0x%lx\n", p->p_pid,
td->td_name, rflags);
return (EINVAL);
}
if (!is_seg_val(ucp->uc_mcontext.mc_ss) ||
!is_seg_val(ucp->uc_mcontext.mc_cs)) {
uprintf("pid %d (%s): sigreturn cs = %#lx ss = %#lx\n",
p->p_pid, td->td_name, ucp->uc_mcontext.mc_cs,
ucp->uc_mcontext.mc_ss);
return (EINVAL);
}
cs = ucp->uc_mcontext.mc_cs;
if (!CS_SECURE(cs)) {
uprintf("pid %d (%s): sigreturn cs = 0x%x\n", p->p_pid,
td->td_name, cs);
ksiginfo_init_trap(&ksi);
ksi.ksi_signo = SIGBUS;
ksi.ksi_code = BUS_OBJERR;
ksi.ksi_trapno = T_PROTFLT;
ksi.ksi_addr = (void *)regs->tf_rip;
trapsignal(td, &ksi);
return (EINVAL);
}
if ((uc.uc_mcontext.mc_flags & _MC_HASFPXSTATE) != 0) {
xfpustate_len = uc.uc_mcontext.mc_xfpustate_len;
if (xfpustate_len > cpu_max_ext_state_size -
sizeof(struct savefpu)) {
uprintf("pid %d (%s): sigreturn xfpusave_len = 0x%zx\n",
p->p_pid, td->td_name, xfpustate_len);
return (EINVAL);
}
xfpustate = (char *)fpu_save_area_alloc();
error = copyin((const void *)uc.uc_mcontext.mc_xfpustate,
xfpustate, xfpustate_len);
if (error != 0) {
fpu_save_area_free((struct savefpu *)xfpustate);
uprintf(
"pid %d (%s): sigreturn copying xfpustate failed\n",
p->p_pid, td->td_name);
return (error);
}
} else {
xfpustate = NULL;
xfpustate_len = 0;
}
ret = set_fpcontext(td, &ucp->uc_mcontext, xfpustate, xfpustate_len);
fpu_save_area_free((struct savefpu *)xfpustate);
if (ret != 0) {
uprintf("pid %d (%s): sigreturn set_fpcontext err %d\n",
p->p_pid, td->td_name, ret);
return (ret);
}
bcopy(&ucp->uc_mcontext.mc_rdi, regs, sizeof(*regs));
update_pcb_bases(pcb);
pcb->pcb_fsbase = ucp->uc_mcontext.mc_fsbase;
pcb->pcb_gsbase = ucp->uc_mcontext.mc_gsbase;
#if defined(COMPAT_43)
if (ucp->uc_mcontext.mc_onstack & 1)
td->td_sigstk.ss_flags |= SS_ONSTACK;
else
td->td_sigstk.ss_flags &= ~SS_ONSTACK;
#endif
kern_sigprocmask(td, SIG_SETMASK, &ucp->uc_sigmask, NULL, 0);
return (EJUSTRETURN);
}
#ifdef COMPAT_FREEBSD4
int
freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap)
{
return sys_sigreturn(td, (struct sigreturn_args *)uap);
}
#endif
void
x86_clear_dbregs(struct pcb *pcb)
{
if ((pcb->pcb_flags & PCB_DBREGS) == 0)
return;
pcb->pcb_dr0 = 0;
pcb->pcb_dr1 = 0;
pcb->pcb_dr2 = 0;
pcb->pcb_dr3 = 0;
pcb->pcb_dr6 = 0;
pcb->pcb_dr7 = 0;
if (pcb == curpcb) {
reset_dbregs();
}
clear_pcb_flags(pcb, PCB_DBREGS);
}
void
exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
{
struct trapframe *regs;
struct pcb *pcb;
register_t saved_rflags;
regs = td->td_frame;
pcb = td->td_pcb;
if (td->td_proc->p_md.md_ldt != NULL)
user_ldt_free(td);
update_pcb_bases(pcb);
pcb->pcb_fsbase = pcb->pcb_tlsbase = 0;
pcb->pcb_gsbase = 0;
clear_pcb_flags(pcb, PCB_32BIT | PCB_TLSBASE);
pcb->pcb_initial_fpucw = __INITIAL_FPUCW__;
saved_rflags = regs->tf_rflags & PSL_T;
bzero((char *)regs, sizeof(struct trapframe));
regs->tf_rip = imgp->entry_addr;
regs->tf_rsp = ((stack - 8) & ~0xFul) + 8;
regs->tf_rdi = stack;
regs->tf_rflags = PSL_USER | saved_rflags;
regs->tf_ss = _udatasel;
regs->tf_cs = _ucodesel;
regs->tf_ds = _udatasel;
regs->tf_es = _udatasel;
regs->tf_fs = _ufssel;
regs->tf_gs = _ugssel;
regs->tf_flags = TF_HASSEGS;
x86_clear_dbregs(pcb);
fpstate_drop(td);
}
int
fill_regs(struct thread *td, struct reg *regs)
{
struct trapframe *tp;
tp = td->td_frame;
return (fill_frame_regs(tp, regs));
}
int
fill_frame_regs(struct trapframe *tp, struct reg *regs)
{
regs->r_r15 = tp->tf_r15;
regs->r_r14 = tp->tf_r14;
regs->r_r13 = tp->tf_r13;
regs->r_r12 = tp->tf_r12;
regs->r_r11 = tp->tf_r11;
regs->r_r10 = tp->tf_r10;
regs->r_r9 = tp->tf_r9;
regs->r_r8 = tp->tf_r8;
regs->r_rdi = tp->tf_rdi;
regs->r_rsi = tp->tf_rsi;
regs->r_rbp = tp->tf_rbp;
regs->r_rbx = tp->tf_rbx;
regs->r_rdx = tp->tf_rdx;
regs->r_rcx = tp->tf_rcx;
regs->r_rax = tp->tf_rax;
regs->r_rip = tp->tf_rip;
regs->r_cs = tp->tf_cs;
regs->r_rflags = tp->tf_rflags;
regs->r_rsp = tp->tf_rsp;
regs->r_ss = tp->tf_ss;
if (tp->tf_flags & TF_HASSEGS) {
regs->r_ds = tp->tf_ds;
regs->r_es = tp->tf_es;
regs->r_fs = tp->tf_fs;
regs->r_gs = tp->tf_gs;
} else {
regs->r_ds = 0;
regs->r_es = 0;
regs->r_fs = 0;
regs->r_gs = 0;
}
regs->r_err = 0;
regs->r_trapno = 0;
return (0);
}
int
set_regs(struct thread *td, struct reg *regs)
{
struct trapframe *tp;
register_t rflags;
tp = td->td_frame;
rflags = regs->r_rflags & 0xffffffff;
if (!EFL_SECURE(rflags, tp->tf_rflags) || !CS_SECURE(regs->r_cs))
return (EINVAL);
tp->tf_r15 = regs->r_r15;
tp->tf_r14 = regs->r_r14;
tp->tf_r13 = regs->r_r13;
tp->tf_r12 = regs->r_r12;
tp->tf_r11 = regs->r_r11;
tp->tf_r10 = regs->r_r10;
tp->tf_r9 = regs->r_r9;
tp->tf_r8 = regs->r_r8;
tp->tf_rdi = regs->r_rdi;
tp->tf_rsi = regs->r_rsi;
tp->tf_rbp = regs->r_rbp;
tp->tf_rbx = regs->r_rbx;
tp->tf_rdx = regs->r_rdx;
tp->tf_rcx = regs->r_rcx;
tp->tf_rax = regs->r_rax;
tp->tf_rip = regs->r_rip;
tp->tf_cs = regs->r_cs;
tp->tf_rflags = rflags;
tp->tf_rsp = regs->r_rsp;
tp->tf_ss = regs->r_ss;
if (0) {
tp->tf_ds = regs->r_ds;
tp->tf_es = regs->r_es;
tp->tf_fs = regs->r_fs;
tp->tf_gs = regs->r_gs;
tp->tf_flags = TF_HASSEGS;
}
set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
return (0);
}
static void
fill_fpregs_xmm(struct savefpu *sv_xmm, struct fpreg *fpregs)
{
struct envxmm *penv_fpreg = (struct envxmm *)&fpregs->fpr_env;
struct envxmm *penv_xmm = &sv_xmm->sv_env;
int i;
bzero(fpregs, sizeof(*fpregs));
penv_fpreg->en_cw = penv_xmm->en_cw;
penv_fpreg->en_sw = penv_xmm->en_sw;
penv_fpreg->en_tw = penv_xmm->en_tw;
penv_fpreg->en_opcode = penv_xmm->en_opcode;
penv_fpreg->en_rip = penv_xmm->en_rip;
penv_fpreg->en_rdp = penv_xmm->en_rdp;
penv_fpreg->en_mxcsr = penv_xmm->en_mxcsr;
penv_fpreg->en_mxcsr_mask = penv_xmm->en_mxcsr_mask;
for (i = 0; i < 8; ++i)
bcopy(sv_xmm->sv_fp[i].fp_acc.fp_bytes, fpregs->fpr_acc[i], 10);
for (i = 0; i < 16; ++i)
bcopy(sv_xmm->sv_xmm[i].xmm_bytes, fpregs->fpr_xacc[i], 16);
}
static void
set_fpregs_xmm(struct fpreg *fpregs, struct savefpu *sv_xmm)
{
struct envxmm *penv_xmm = &sv_xmm->sv_env;
struct envxmm *penv_fpreg = (struct envxmm *)&fpregs->fpr_env;
int i;
penv_xmm->en_cw = penv_fpreg->en_cw;
penv_xmm->en_sw = penv_fpreg->en_sw;
penv_xmm->en_tw = penv_fpreg->en_tw;
penv_xmm->en_opcode = penv_fpreg->en_opcode;
penv_xmm->en_rip = penv_fpreg->en_rip;
penv_xmm->en_rdp = penv_fpreg->en_rdp;
penv_xmm->en_mxcsr = penv_fpreg->en_mxcsr;
penv_xmm->en_mxcsr_mask = penv_fpreg->en_mxcsr_mask & cpu_mxcsr_mask;
for (i = 0; i < 8; ++i)
bcopy(fpregs->fpr_acc[i], sv_xmm->sv_fp[i].fp_acc.fp_bytes, 10);
for (i = 0; i < 16; ++i)
bcopy(fpregs->fpr_xacc[i], sv_xmm->sv_xmm[i].xmm_bytes, 16);
}
int
fill_fpregs(struct thread *td, struct fpreg *fpregs)
{
KASSERT(td == curthread || TD_IS_SUSPENDED(td) ||
P_SHOULDSTOP(td->td_proc),
("not suspended thread %p", td));
fpugetregs(td);
fill_fpregs_xmm(get_pcb_user_save_td(td), fpregs);
return (0);
}
int
set_fpregs(struct thread *td, struct fpreg *fpregs)
{
critical_enter();
set_fpregs_xmm(fpregs, get_pcb_user_save_td(td));
fpuuserinited(td);
critical_exit();
return (0);
}
int
get_mcontext(struct thread *td, mcontext_t *mcp, int flags)
{
struct pcb *pcb;
struct trapframe *tp;
pcb = td->td_pcb;
tp = td->td_frame;
PROC_LOCK(curthread->td_proc);
mcp->mc_onstack = sigonstack(tp->tf_rsp);
PROC_UNLOCK(curthread->td_proc);
mcp->mc_r15 = tp->tf_r15;
mcp->mc_r14 = tp->tf_r14;
mcp->mc_r13 = tp->tf_r13;
mcp->mc_r12 = tp->tf_r12;
mcp->mc_r11 = tp->tf_r11;
mcp->mc_r10 = tp->tf_r10;
mcp->mc_r9 = tp->tf_r9;
mcp->mc_r8 = tp->tf_r8;
mcp->mc_rdi = tp->tf_rdi;
mcp->mc_rsi = tp->tf_rsi;
mcp->mc_rbp = tp->tf_rbp;
mcp->mc_rbx = tp->tf_rbx;
mcp->mc_rcx = tp->tf_rcx;
mcp->mc_rflags = tp->tf_rflags;
if (flags & GET_MC_CLEAR_RET) {
mcp->mc_rax = 0;
mcp->mc_rdx = 0;
mcp->mc_rflags &= ~PSL_C;
} else {
mcp->mc_rax = tp->tf_rax;
mcp->mc_rdx = tp->tf_rdx;
}
mcp->mc_rip = tp->tf_rip;
mcp->mc_cs = tp->tf_cs;
mcp->mc_rsp = tp->tf_rsp;
mcp->mc_ss = tp->tf_ss;
mcp->mc_ds = tp->tf_ds;
mcp->mc_es = tp->tf_es;
mcp->mc_fs = tp->tf_fs;
mcp->mc_gs = tp->tf_gs;
mcp->mc_flags = tp->tf_flags;
mcp->mc_len = sizeof(*mcp);
get_fpcontext(td, mcp, NULL, NULL);
update_pcb_bases(pcb);
mcp->mc_fsbase = pcb->pcb_fsbase;
mcp->mc_gsbase = pcb->pcb_gsbase;
mcp->mc_xfpustate = 0;
mcp->mc_xfpustate_len = 0;
mcp->mc_tlsbase = (pcb->pcb_flags & PCB_TLSBASE) != 0 ?
pcb->pcb_tlsbase : 0;
bzero(mcp->mc_spare, sizeof(mcp->mc_spare));
return (0);
}
int
set_mcontext(struct thread *td, mcontext_t *mcp)
{
struct pcb *pcb;
struct trapframe *tp;
char *xfpustate;
long rflags;
int ret;
pcb = td->td_pcb;
tp = td->td_frame;
if (mcp->mc_len != sizeof(*mcp) ||
(mcp->mc_flags & ~_MC_FLAG_MASK) != 0)
return (EINVAL);
if (!is_seg_val(mcp->mc_ss) || !is_seg_val(mcp->mc_cs))
return (EINVAL);
rflags = (mcp->mc_rflags & PSL_USERCHANGE) |
(tp->tf_rflags & ~PSL_USERCHANGE);
if (mcp->mc_flags & _MC_HASFPXSTATE) {
if (mcp->mc_xfpustate_len > cpu_max_ext_state_size -
sizeof(struct savefpu))
return (EINVAL);
xfpustate = (char *)fpu_save_area_alloc();
ret = copyin((void *)mcp->mc_xfpustate, xfpustate,
mcp->mc_xfpustate_len);
if (ret != 0) {
fpu_save_area_free((struct savefpu *)xfpustate);
return (ret);
}
} else
xfpustate = NULL;
ret = set_fpcontext(td, mcp, xfpustate, mcp->mc_xfpustate_len);
fpu_save_area_free((struct savefpu *)xfpustate);
if (ret != 0)
return (ret);
tp->tf_r15 = mcp->mc_r15;
tp->tf_r14 = mcp->mc_r14;
tp->tf_r13 = mcp->mc_r13;
tp->tf_r12 = mcp->mc_r12;
tp->tf_r11 = mcp->mc_r11;
tp->tf_r10 = mcp->mc_r10;
tp->tf_r9 = mcp->mc_r9;
tp->tf_r8 = mcp->mc_r8;
tp->tf_rdi = mcp->mc_rdi;
tp->tf_rsi = mcp->mc_rsi;
tp->tf_rbp = mcp->mc_rbp;
tp->tf_rbx = mcp->mc_rbx;
tp->tf_rdx = mcp->mc_rdx;
tp->tf_rcx = mcp->mc_rcx;
tp->tf_rax = mcp->mc_rax;
tp->tf_rip = mcp->mc_rip;
tp->tf_rflags = rflags;
tp->tf_rsp = mcp->mc_rsp;
tp->tf_ss = mcp->mc_ss;
tp->tf_flags = mcp->mc_flags;
if (tp->tf_flags & TF_HASSEGS) {
tp->tf_ds = mcp->mc_ds;
tp->tf_es = mcp->mc_es;
tp->tf_fs = mcp->mc_fs;
tp->tf_gs = mcp->mc_gs;
}
set_pcb_flags(pcb, PCB_FULL_IRET);
if (mcp->mc_flags & _MC_HASBASES) {
pcb->pcb_fsbase = mcp->mc_fsbase;
pcb->pcb_gsbase = mcp->mc_gsbase;
}
if ((mcp->mc_flags & _MC_HASTLSBASE) != 0) {
pcb->pcb_tlsbase = mcp->mc_tlsbase;
set_pcb_flags(pcb, PCB_TLSBASE);
}
return (0);
}
void
get_fpcontext(struct thread *td, mcontext_t *mcp, char **xfpusave,
size_t *xfpusave_len)
{
mcp->mc_ownedfp = fpugetregs(td);
bcopy(get_pcb_user_save_td(td), &mcp->mc_fpstate[0],
sizeof(mcp->mc_fpstate));
mcp->mc_fpformat = fpuformat();
if (xfpusave == NULL)
return;
if (!use_xsave || cpu_max_ext_state_size <= sizeof(struct savefpu)) {
*xfpusave_len = 0;
*xfpusave = NULL;
} else {
mcp->mc_flags |= _MC_HASFPXSTATE;
*xfpusave_len = mcp->mc_xfpustate_len =
cpu_max_ext_state_size - sizeof(struct savefpu);
*xfpusave = (char *)(get_pcb_user_save_td(td) + 1);
}
}
int
set_fpcontext(struct thread *td, mcontext_t *mcp, char *xfpustate,
size_t xfpustate_len)
{
int error;
if (mcp->mc_fpformat == _MC_FPFMT_NODEV)
return (0);
else if (mcp->mc_fpformat != _MC_FPFMT_XMM)
return (EINVAL);
else if (mcp->mc_ownedfp == _MC_FPOWNED_NONE) {
fpstate_drop(td);
error = 0;
} else if (mcp->mc_ownedfp == _MC_FPOWNED_FPU ||
mcp->mc_ownedfp == _MC_FPOWNED_PCB) {
error = fpusetregs(td, (struct savefpu *)&mcp->mc_fpstate,
xfpustate, xfpustate_len);
} else
return (EINVAL);
return (error);
}
void
fpstate_drop(struct thread *td)
{
KASSERT(PCB_USER_FPU(td->td_pcb), ("fpstate_drop: kernel-owned fpu"));
critical_enter();
if (PCPU_GET(fpcurthread) == td)
fpudrop();
clear_pcb_flags(curthread->td_pcb,
PCB_FPUINITDONE | PCB_USERFPUINITDONE);
critical_exit();
}
int
fill_dbregs(struct thread *td, struct dbreg *dbregs)
{
struct pcb *pcb;
if (td == NULL) {
dbregs->dr[0] = rdr0();
dbregs->dr[1] = rdr1();
dbregs->dr[2] = rdr2();
dbregs->dr[3] = rdr3();
dbregs->dr[6] = rdr6();
dbregs->dr[7] = rdr7();
} else {
pcb = td->td_pcb;
dbregs->dr[0] = pcb->pcb_dr0;
dbregs->dr[1] = pcb->pcb_dr1;
dbregs->dr[2] = pcb->pcb_dr2;
dbregs->dr[3] = pcb->pcb_dr3;
dbregs->dr[6] = pcb->pcb_dr6;
dbregs->dr[7] = pcb->pcb_dr7;
}
dbregs->dr[4] = 0;
dbregs->dr[5] = 0;
dbregs->dr[8] = 0;
dbregs->dr[9] = 0;
dbregs->dr[10] = 0;
dbregs->dr[11] = 0;
dbregs->dr[12] = 0;
dbregs->dr[13] = 0;
dbregs->dr[14] = 0;
dbregs->dr[15] = 0;
return (0);
}
int
set_dbregs(struct thread *td, struct dbreg *dbregs)
{
struct pcb *pcb;
int i;
if (td == NULL) {
load_dr0(dbregs->dr[0]);
load_dr1(dbregs->dr[1]);
load_dr2(dbregs->dr[2]);
load_dr3(dbregs->dr[3]);
load_dr6(dbregs->dr[6]);
load_dr7(dbregs->dr[7]);
} else {
for (i = 0; i < 4; i++) {
if (DBREG_DR7_ACCESS(dbregs->dr[7], i) == 0x02)
return (EINVAL);
if (td->td_frame->tf_cs == _ucode32sel &&
DBREG_DR7_LEN(dbregs->dr[7], i) == DBREG_DR7_LEN_8)
return (EINVAL);
}
if ((dbregs->dr[6] & 0xffffffff00000000ul) != 0 ||
(dbregs->dr[7] & 0xffffffff00000000ul) != 0)
return (EINVAL);
pcb = td->td_pcb;
if (DBREG_DR7_ENABLED(dbregs->dr[7], 0)) {
if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS)
return (EINVAL);
}
if (DBREG_DR7_ENABLED(dbregs->dr[7], 1)) {
if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS)
return (EINVAL);
}
if (DBREG_DR7_ENABLED(dbregs->dr[7], 2)) {
if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS)
return (EINVAL);
}
if (DBREG_DR7_ENABLED(dbregs->dr[7], 3)) {
if (dbregs->dr[3] >= VM_MAXUSER_ADDRESS)
return (EINVAL);
}
pcb->pcb_dr0 = dbregs->dr[0];
pcb->pcb_dr1 = dbregs->dr[1];
pcb->pcb_dr2 = dbregs->dr[2];
pcb->pcb_dr3 = dbregs->dr[3];
pcb->pcb_dr6 = dbregs->dr[6];
pcb->pcb_dr7 = dbregs->dr[7];
set_pcb_flags(pcb, PCB_DBREGS);
}
return (0);
}
void
reset_dbregs(void)
{
load_dr7(0);
load_dr0(0);
load_dr1(0);
load_dr2(0);
load_dr3(0);
load_dr6(0);
}
int
user_dbreg_trap(register_t dr6)
{
u_int64_t dr7;
u_int64_t bp;
int nbp;
caddr_t addr[4];
int i;
bp = dr6 & DBREG_DR6_BMASK;
if (bp == 0) {
return (0);
}
dr7 = rdr7();
if ((dr7 & 0x000000ff) == 0) {
return (0);
}
nbp = 0;
if (bp & 0x01) {
addr[nbp++] = (caddr_t)rdr0();
}
if (bp & 0x02) {
addr[nbp++] = (caddr_t)rdr1();
}
if (bp & 0x04) {
addr[nbp++] = (caddr_t)rdr2();
}
if (bp & 0x08) {
addr[nbp++] = (caddr_t)rdr3();
}
for (i = 0; i < nbp; i++) {
if (addr[i] < (caddr_t)VM_MAXUSER_ADDRESS) {
return (nbp);
}
}
return (0);
}