Index: sys/amd64/amd64/exec_machdep.c =================================================================== --- sys/amd64/amd64/exec_machdep.c +++ sys/amd64/amd64/exec_machdep.c @@ -95,11 +95,6 @@ #include #include -static void get_fpcontext(struct thread *td, mcontext_t *mcp, - char **xfpusave, size_t *xfpusave_len); -static int set_fpcontext(struct thread *td, mcontext_t *mcp, - char *xfpustate, size_t xfpustate_len); - /* * Send an interrupt to process. * @@ -714,7 +709,7 @@ return (0); } -static void +void get_fpcontext(struct thread *td, mcontext_t *mcp, char **xfpusave, size_t *xfpusave_len) { @@ -735,7 +730,7 @@ } } -static int +int set_fpcontext(struct thread *td, mcontext_t *mcp, char *xfpustate, size_t xfpustate_len) { Index: sys/amd64/include/md_var.h =================================================================== --- sys/amd64/include/md_var.h +++ sys/amd64/include/md_var.h @@ -54,6 +54,7 @@ extern bool efi_boot; +struct __mcontext; struct savefpu; struct sysentvec; @@ -88,5 +89,9 @@ struct savefpu *get_pcb_user_save_td(struct thread *td); struct savefpu *get_pcb_user_save_pcb(struct pcb *pcb); void pci_early_quirks(void); +void get_fpcontext(struct thread *td, struct __mcontext *mcp, + char **xfpusave, size_t *xfpusave_len); +int set_fpcontext(struct thread *td, struct __mcontext *mcp, + char *xfpustate, size_t xfpustate_len); #endif /* !_MACHINE_MD_VAR_H_ */ Index: sys/amd64/linux/linux.h =================================================================== --- sys/amd64/linux/linux.h +++ sys/amd64/linux/linux.h @@ -227,12 +227,17 @@ l_ulong sc_reserved1[8]; }; +#define LINUX_UC_FLAGS_XSTATE 0x1 + struct l_ucontext { l_ulong uc_flags; l_uintptr_t uc_link; l_stack_t uc_stack; struct l_sigcontext uc_mcontext; l_sigset_t uc_sigmask; + struct l_fpstate uc_fpregs; + void *uc_xsave; // XXX + size_t uc_xsavelen; // XXX }; #define LINUX_SI_PREAMBLE_SIZE (4 * sizeof(int)) Index: sys/amd64/linux/linux_sysvec.c =================================================================== --- sys/amd64/linux/linux_sysvec.c +++ sys/amd64/linux/linux_sysvec.c @@ -518,9 +518,7 @@ } /* - * Copied from amd64/amd64/machdep.c - * - * XXX fpu state need? don't think so + * Based on amd64/amd64/exec_machdep.c */ int linux_rt_sigreturn(struct thread *td, struct linux_rt_sigreturn_args *args) @@ -529,8 +527,12 @@ struct l_ucontext uc; struct l_sigcontext *context; struct trapframe *regs; + mcontext_t mcontext; + struct savefpu *savefpu; + char *xfpustate; + size_t xfpustate_len; unsigned long rflags; - int error; + int error, ret; ksiginfo_t ksi; regs = td->td_frame; @@ -578,6 +580,62 @@ return (EINVAL); } + if (uc.uc_mcontext.sc_fpstate != NULL) { + //linux_msg(td, "%s: got fpformat", __func__); + + memset(&mcontext, 0, sizeof(mcontext)); + mcontext.mc_flags |= _MC_HASFPXSTATE; + mcontext.mc_fpformat = _MC_FPFMT_XMM; + mcontext.mc_ownedfp = _MC_FPOWNED_FPU; + + savefpu = (struct savefpu *)mcontext.mc_fpstate; + + savefpu->sv_env.en_cw = uc.uc_fpregs.cwd; + savefpu->sv_env.en_sw = uc.uc_fpregs.swd; + savefpu->sv_env.en_tw = uc.uc_fpregs.twd; + savefpu->sv_env.en_opcode = uc.uc_fpregs.fop; + savefpu->sv_env.en_rip = uc.uc_fpregs.rip; + savefpu->sv_env.en_rdp = uc.uc_fpregs.rdp; + savefpu->sv_env.en_mxcsr = uc.uc_fpregs.mxcsr; + savefpu->sv_env.en_mxcsr_mask = uc.uc_fpregs.mxcsr_mask; + CTASSERT(sizeof(uc.uc_fpregs.st_space) == sizeof(savefpu->sv_fp)); + memcpy(savefpu->sv_fp, uc.uc_fpregs.st_space, sizeof(savefpu->sv_fp)); + CTASSERT(sizeof(uc.uc_fpregs.xmm_space) == sizeof(savefpu->sv_xmm)); + memcpy(savefpu->sv_xmm, uc.uc_fpregs.xmm_space, sizeof(savefpu->sv_xmm)); + } + + if (uc.uc_xsave != NULL) { + xfpustate_len = uc.uc_xsavelen; + //linux_msg(td, "%s: got xfpusave, len %zd", __func__, 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_xsave, + 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, &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); + } + PROC_LOCK(p); linux_to_bsd_sigset(&uc.uc_sigmask, &td->td_sigmask); SIG_CANTMASK(td->td_sigmask); @@ -610,7 +668,7 @@ } /* - * copied from amd64/amd64/machdep.c + * Based on amd64/amd64/exec_machdep.c * * Send an interrupt to process. */ @@ -623,6 +681,10 @@ struct sigacts *psp; caddr_t sp; struct trapframe *regs; + mcontext_t mcontext; + const struct savefpu *savefpu; + char *xfpusave; + size_t xfpusave_len; int sig, code; int oonstack; @@ -672,13 +734,43 @@ sf.sf_sc.uc_mcontext.sc_trapno = bsd_to_linux_trapcode(code); sf.sf_sc.uc_mcontext.sc_cr2 = (register_t)ksi->ksi_addr; + get_fpcontext(td, &mcontext, &xfpusave, &xfpusave_len); + if (mcontext.mc_fpformat == _MC_FPFMT_XMM) { + //linux_msg(td, "%s: got fpformat", __func__); + savefpu = (const struct savefpu *)mcontext.mc_fpstate; + + sf.sf_sc.uc_fpregs.cwd = savefpu->sv_env.en_cw; + sf.sf_sc.uc_fpregs.swd = savefpu->sv_env.en_sw; + sf.sf_sc.uc_fpregs.twd = savefpu->sv_env.en_tw; + sf.sf_sc.uc_fpregs.fop = savefpu->sv_env.en_opcode; + sf.sf_sc.uc_fpregs.rip = savefpu->sv_env.en_rip; + sf.sf_sc.uc_fpregs.rdp = savefpu->sv_env.en_rdp; + sf.sf_sc.uc_fpregs.mxcsr = savefpu->sv_env.en_mxcsr; + sf.sf_sc.uc_fpregs.mxcsr_mask = savefpu->sv_env.en_mxcsr_mask; + CTASSERT(sizeof(sf.sf_sc.uc_fpregs.st_space) == sizeof(savefpu->sv_fp)); + memcpy(sf.sf_sc.uc_fpregs.st_space, savefpu->sv_fp, sizeof(savefpu->sv_fp)); + CTASSERT(sizeof(sf.sf_sc.uc_fpregs.xmm_space) == sizeof(savefpu->sv_xmm)); + memcpy(sf.sf_sc.uc_fpregs.xmm_space, savefpu->sv_xmm, sizeof(savefpu->sv_xmm)); + + sf.sf_sc.uc_mcontext.sc_fpstate= (struct l_fpstate *)&sf.sf_sc.uc_fpregs; // XXX: Should be user addr + } + /* Allocate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { - sp = (caddr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size - - sizeof(struct l_rt_sigframe); + sp = (caddr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size; } else - sp = (caddr_t)regs->tf_rsp - sizeof(struct l_rt_sigframe) - 128; + sp = (caddr_t)regs->tf_rsp - 128; + if (xfpusave != NULL) { + KASSERT(mcontext.mc_fpformat == _MC_FPFMT_XMM, + ("%s: xfpusave, but no fpformat\n", __func__)); + sp -= xfpusave_len; + sp = (char *)((unsigned long)sp & ~0x3Ful); + sf.sf_sc.uc_xsave = sp; // XXX + sf.sf_sc.uc_xsavelen = xfpusave_len; // XXX + //linux_msg(td, "%s: got xsave at %p, len %zd", __func__, sf.sf_sc.uc_xsave, sf.sf_sc.uc_xsavelen); + } + sp -= sizeof(struct l_rt_sigframe); /* Align to 16 bytes. */ sfp = (struct l_rt_sigframe *)((unsigned long)sp & ~0xFul); @@ -699,7 +791,12 @@ PROC_UNLOCK(p); /* Copy the sigframe out to the user's stack. */ - if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { + if (copyout(&sf, sfp, sizeof(*sfp)) != 0 || + (xfpusave != NULL && copyout(xfpusave, + (void *)sf.sf_sc.uc_xsave, xfpusave_len) + != 0)) { + linux_msg(td, "pid %d comm %s has trashed its stack, killing\n", + p->p_pid, p->p_comm); PROC_LOCK(p); sigexit(td, SIGILL); }