From 12ca33f44fc76890336f517ace402b47b9afb134 Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Mon, 13 Sep 2021 23:33:37 +0300 Subject: amd64: move signal handling and register structures manipulations into exec_machdep.c from machdep.c which is too large pile of unrelated things. Some ptrace functions are moved from machdep.c to ptrace_machdep.c. Now machdep.c contains code mostly related to the low level initialization and regular low level operation of the architecture, while signal MD code and registers handling is placed in exec_machdep.c. Reviewed by: jhb, markj Discussed with: jrtc27 Tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D31954 --- sys/amd64/amd64/exec_machdep.c | 987 +++++++++++++++++++++++++++++++++++++++ sys/amd64/amd64/machdep.c | 924 ------------------------------------ sys/amd64/amd64/ptrace_machdep.c | 33 ++ sys/conf/files.amd64 | 1 + 4 files changed, 1021 insertions(+), 924 deletions(-) create mode 100644 sys/amd64/amd64/exec_machdep.c diff --git a/sys/amd64/amd64/exec_machdep.c b/sys/amd64/amd64/exec_machdep.c new file mode 100644 index 000000000000..5cd905e1051b --- /dev/null +++ b/sys/amd64/amd64/exec_machdep.c @@ -0,0 +1,987 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 2003 Peter Wemm. + * Copyright (c) 1992 Terrence R. Lambert. + * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_cpu.h" +#include "opt_ddb.h" +#include "opt_kstack_pages.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SMP +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef DDB +#ifndef KDB +#error KDB must be enabled in order for DDB to work! +#endif +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#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. + * + * Stack is set up to allow sigcode stored at top to call routine, + * followed by call to sigreturn routine below. After sigreturn + * resets the signal mask, the stack, and the frame pointer, it + * returns to the user specified pc, psl. + */ +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); + + if (cpu_max_ext_state_size > sizeof(struct savefpu) && use_xsave) { + xfpusave_len = cpu_max_ext_state_size - sizeof(struct savefpu); + xfpusave = __builtin_alloca(xfpusave_len); + } else { + xfpusave_len = 0; + xfpusave = NULL; + } + + /* Save user context. */ + 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; + bcopy(regs, &sf.sf_uc.uc_mcontext.mc_rdi, sizeof(*regs)); + sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */ + get_fpcontext(td, &sf.sf_uc.uc_mcontext, xfpusave, xfpusave_len); + fpstate_drop(td); + 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)); + + /* Allocate space for the signal handler context. */ + 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 - 128; + 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); + /* Align to 16 bytes. */ + sfp = (struct sigframe *)((unsigned long)sp & ~0xFul); + + /* Build the argument list for the signal handler. */ + regs->tf_rdi = sig; /* arg 1 in %rdi */ + regs->tf_rdx = (register_t)&sfp->sf_uc; /* arg 3 in %rdx */ + bzero(&sf.sf_si, sizeof(sf.sf_si)); + if (SIGISMEMBER(psp->ps_siginfo, sig)) { + /* Signal handler installed with SA_SIGINFO. */ + regs->tf_rsi = (register_t)&sfp->sf_si; /* arg 2 in %rsi */ + sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; + + /* Fill in POSIX parts */ + sf.sf_si = ksi->ksi_info; + sf.sf_si.si_signo = sig; /* maybe a translated signal */ + regs->tf_rcx = (register_t)ksi->ksi_addr; /* arg 4 in %rcx */ + } else { + /* Old FreeBSD-style arguments. */ + regs->tf_rsi = ksi->ksi_code; /* arg 2 in %rsi */ + regs->tf_rcx = (register_t)ksi->ksi_addr; /* arg 4 in %rcx */ + sf.sf_ahu.sf_handler = catcher; + } + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(p); + + /* + * Copy the sigframe out to the user's stack. + */ + if (copyout(&sf, sfp, sizeof(*sfp)) != 0 || + (xfpusave != NULL && copyout(xfpusave, + (void *)sf.sf_uc.uc_mcontext.mc_xfpustate, xfpusave_len) + != 0)) { +#ifdef DEBUG + printf("process %ld has trashed its stack\n", (long)p->p_pid); +#endif + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + regs->tf_rsp = (long)sfp; + regs->tf_rip = p->p_sysent->sv_sigcode_base; + 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; + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); +} + +/* + * System call to cleanup state after a signal + * has been taken. Reset signal mask and + * stack state from context left by sendsig (above). + * Return to previous pc and psl as specified by + * context left by sendsig. Check carefully to + * make sure that the user has not modified the + * state to gain improper privileges. + */ +int +sys_sigreturn(td, uap) + struct thread *td; + struct sigreturn_args /* { + const struct __ucontext *sigcntxp; + } */ *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; + /* + * Don't allow users to change privileged or reserved flags. + */ + 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); + } + + /* + * Don't allow users to load a valid privileged %cs. Let the + * hardware check for invalid selectors, excess privilege in + * other selectors, invalid %eip's and invalid %esp's. + */ + 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 = __builtin_alloca(xfpustate_len); + error = copyin((const void *)uc.uc_mcontext.mc_xfpustate, + xfpustate, xfpustate_len); + if (error != 0) { + 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); + 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 + +/* + * Reset the hardware debug registers if they were in use. + * They won't have any meaning for the newly exec'd process. + */ +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) { + /* + * Clear the debug registers on the running CPU, + * otherwise they will end up affecting the next + * process we switch to. + */ + reset_dbregs(); + } + clear_pcb_flags(pcb, PCB_DBREGS); +} + +/* + * Reset registers to default values on exec. + */ +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 = 0; + pcb->pcb_gsbase = 0; + clear_pcb_flags(pcb, PCB_32BIT); + 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; /* argv */ + 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); + + /* + * Drop the FP state if we hold it, so that the process gets a + * clean FP state if it uses the FPU again. + */ + 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) { /* XXXKIB */ + 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); +} + +/* XXX check all this stuff! */ +/* externalize from sv_xmm */ +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; + + /* pcb -> fpregs */ + bzero(fpregs, sizeof(*fpregs)); + + /* FPU control/status */ + 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; + + /* FPU registers */ + for (i = 0; i < 8; ++i) + bcopy(sv_xmm->sv_fp[i].fp_acc.fp_bytes, fpregs->fpr_acc[i], 10); + + /* SSE registers */ + for (i = 0; i < 16; ++i) + bcopy(sv_xmm->sv_xmm[i].xmm_bytes, fpregs->fpr_xacc[i], 16); +} + +/* internalize from fpregs into sv_xmm */ +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; + + /* fpregs -> pcb */ + /* FPU control/status */ + 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; + + /* FPU registers */ + for (i = 0; i < 8; ++i) + bcopy(fpregs->fpr_acc[i], sv_xmm->sv_fp[i].fp_acc.fp_bytes, 10); + + /* SSE registers */ + for (i = 0; i < 16; ++i) + bcopy(fpregs->fpr_xacc[i], sv_xmm->sv_xmm[i].xmm_bytes, 16); +} + +/* externalize from td->pcb */ +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); +} + +/* internalize to td->pcb */ +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); +} + +/* + * Get machine context. + */ +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, 0); + 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; + bzero(mcp->mc_spare, sizeof(mcp->mc_spare)); + return (0); +} + +/* + * Set machine context. + * + * However, we don't set any but the user modifiable flags, and we won't + * touch the cs selector. + */ +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); + 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 = __builtin_alloca(mcp->mc_xfpustate_len); + ret = copyin((void *)mcp->mc_xfpustate, xfpustate, + mcp->mc_xfpustate_len); + if (ret != 0) + return (ret); + } else + xfpustate = NULL; + ret = set_fpcontext(td, mcp, xfpustate, mcp->mc_xfpustate_len); + 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; + } + return (0); +} + +static void +get_fpcontext(struct thread *td, mcontext_t *mcp, char *xfpusave, + size_t xfpusave_len) +{ + size_t max_len, 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 (!use_xsave || xfpusave_len == 0) + return; + max_len = cpu_max_ext_state_size - sizeof(struct savefpu); + len = xfpusave_len; + if (len > max_len) { + len = max_len; + bzero(xfpusave + max_len, len - max_len); + } + mcp->mc_flags |= _MC_HASFPXSTATE; + mcp->mc_xfpustate_len = len; + bcopy(get_pcb_user_save_td(td) + 1, xfpusave, len); +} + +static 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) { + /* We don't care what state is left in the FPU or PCB. */ + 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(); + /* + * XXX force a full drop of the fpu. The above only drops it if we + * owned it. + * + * XXX I don't much like fpugetuserregs()'s semantics of doing a full + * drop. Dropping only to the pcb matches fnsave's behaviour. + * We only need to drop to !PCB_INITDONE in sendsig(). But + * sendsig() is the only caller of fpugetuserregs()... perhaps we just + * have too many layers. + */ + 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 { + /* + * Don't let an illegal value for dr7 get set. Specifically, + * check for undefined settings. Setting these bit patterns + * result in undefined behaviour and can lead to an unexpected + * TRCTRAP or a general protection fault right here. + * Upper bits of dr6 and dr7 must not be set + */ + 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; + + /* + * Don't let a process set a breakpoint that is not within the + * process's address space. If a process could do this, it + * could halt the system by setting a breakpoint in the kernel + * (if ddb was enabled). Thus, we need to check to make sure + * that no breakpoints are being enabled for addresses outside + * process's address space. + * + * XXX - what about when the watched area of the user's + * address space is written into from within the kernel + * ... wouldn't that still cause a breakpoint to be generated + * from within kernel mode? + */ + + if (DBREG_DR7_ENABLED(dbregs->dr[7], 0)) { + /* dr0 is enabled */ + if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + if (DBREG_DR7_ENABLED(dbregs->dr[7], 1)) { + /* dr1 is enabled */ + if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + if (DBREG_DR7_ENABLED(dbregs->dr[7], 2)) { + /* dr2 is enabled */ + if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) + return (EINVAL); + } + if (DBREG_DR7_ENABLED(dbregs->dr[7], 3)) { + /* dr3 is enabled */ + 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); /* Turn off the control bits first */ + load_dr0(0); + load_dr1(0); + load_dr2(0); + load_dr3(0); + load_dr6(0); +} + +/* + * Return > 0 if a hardware breakpoint has been hit, and the + * breakpoint was in user space. Return 0, otherwise. + */ +int +user_dbreg_trap(register_t dr6) +{ + u_int64_t dr7; + u_int64_t bp; /* breakpoint bits extracted from dr6 */ + int nbp; /* number of breakpoints that triggered */ + caddr_t addr[4]; /* breakpoint addresses */ + int i; + + bp = dr6 & DBREG_DR6_BMASK; + if (bp == 0) { + /* + * None of the breakpoint bits are set meaning this + * trap was not caused by any of the debug registers + */ + return 0; + } + + dr7 = rdr7(); + if ((dr7 & 0x000000ff) == 0) { + /* + * all GE and LE bits in the dr7 register are zero, + * thus the trap couldn't have been caused by the + * hardware debug registers + */ + return 0; + } + + nbp = 0; + + /* + * at least one of the breakpoints were hit, check to see + * which ones and if any of them are user space addresses + */ + + 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) { + /* + * addr[i] is in user space + */ + return nbp; + } + } + + /* + * None of the breakpoints are in user space. + */ + return 0; +} diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 55d52a28b576..d4e2356a9ae1 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -167,10 +167,6 @@ CTASSERT(PC_PTI_STACK_SZ * sizeof(register_t) >= 2 * sizeof(struct pti_frame) - extern u_int64_t hammer_time(u_int64_t, u_int64_t); static void cpu_startup(void *); -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); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL); /* Preload data parse function */ @@ -322,331 +318,6 @@ late_ifunc_resolve(void *dummy __unused) } SYSINIT(late_ifunc_resolve, SI_SUB_CPU, SI_ORDER_ANY, late_ifunc_resolve, NULL); -/* - * Send an interrupt to process. - * - * Stack is set up to allow sigcode stored - * at top to call routine, followed by call - * to sigreturn routine below. After sigreturn - * resets the signal mask, the stack, and the - * frame pointer, it returns to the user - * specified pc, psl. - */ -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); - - if (cpu_max_ext_state_size > sizeof(struct savefpu) && use_xsave) { - xfpusave_len = cpu_max_ext_state_size - sizeof(struct savefpu); - xfpusave = __builtin_alloca(xfpusave_len); - } else { - xfpusave_len = 0; - xfpusave = NULL; - } - - /* Save user context. */ - 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; - bcopy(regs, &sf.sf_uc.uc_mcontext.mc_rdi, sizeof(*regs)); - sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */ - get_fpcontext(td, &sf.sf_uc.uc_mcontext, xfpusave, xfpusave_len); - fpstate_drop(td); - 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)); - - /* Allocate space for the signal handler context. */ - 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 - 128; - 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); - /* Align to 16 bytes. */ - sfp = (struct sigframe *)((unsigned long)sp & ~0xFul); - - /* Build the argument list for the signal handler. */ - regs->tf_rdi = sig; /* arg 1 in %rdi */ - regs->tf_rdx = (register_t)&sfp->sf_uc; /* arg 3 in %rdx */ - bzero(&sf.sf_si, sizeof(sf.sf_si)); - if (SIGISMEMBER(psp->ps_siginfo, sig)) { - /* Signal handler installed with SA_SIGINFO. */ - regs->tf_rsi = (register_t)&sfp->sf_si; /* arg 2 in %rsi */ - sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; - - /* Fill in POSIX parts */ - sf.sf_si = ksi->ksi_info; - sf.sf_si.si_signo = sig; /* maybe a translated signal */ - regs->tf_rcx = (register_t)ksi->ksi_addr; /* arg 4 in %rcx */ - } else { - /* Old FreeBSD-style arguments. */ - regs->tf_rsi = ksi->ksi_code; /* arg 2 in %rsi */ - regs->tf_rcx = (register_t)ksi->ksi_addr; /* arg 4 in %rcx */ - sf.sf_ahu.sf_handler = catcher; - } - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(p); - - /* - * Copy the sigframe out to the user's stack. - */ - if (copyout(&sf, sfp, sizeof(*sfp)) != 0 || - (xfpusave != NULL && copyout(xfpusave, - (void *)sf.sf_uc.uc_mcontext.mc_xfpustate, xfpusave_len) - != 0)) { -#ifdef DEBUG - printf("process %ld has trashed its stack\n", (long)p->p_pid); -#endif - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - regs->tf_rsp = (long)sfp; - regs->tf_rip = p->p_sysent->sv_sigcode_base; - 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; - PROC_LOCK(p); - mtx_lock(&psp->ps_mtx); -} - -/* - * System call to cleanup state after a signal - * has been taken. Reset signal mask and - * stack state from context left by sendsig (above). - * Return to previous pc and psl as specified by - * context left by sendsig. Check carefully to - * make sure that the user has not modified the - * state to gain improper privileges. - * - * MPSAFE - */ -int -sys_sigreturn(td, uap) - struct thread *td; - struct sigreturn_args /* { - const struct __ucontext *sigcntxp; - } */ *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; - /* - * Don't allow users to change privileged or reserved flags. - */ - 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); - } - - /* - * Don't allow users to load a valid privileged %cs. Let the - * hardware check for invalid selectors, excess privilege in - * other selectors, invalid %eip's and invalid %esp's. - */ - 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 = __builtin_alloca(xfpustate_len); - error = copyin((const void *)uc.uc_mcontext.mc_xfpustate, - xfpustate, xfpustate_len); - if (error != 0) { - 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); - 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 - -/* - * Reset the hardware debug registers if they were in use. - * They won't have any meaning for the newly exec'd process. - */ -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) { - /* - * Clear the debug registers on the running CPU, - * otherwise they will end up affecting the next - * process we switch to. - */ - reset_dbregs(); - } - clear_pcb_flags(pcb, PCB_DBREGS); -} - -/* - * Reset registers to default values on exec. - */ -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 = 0; - pcb->pcb_gsbase = 0; - clear_pcb_flags(pcb, PCB_32BIT); - 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; /* argv */ - 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); - - /* - * Drop the FP state if we hold it, so that the process gets a - * clean FP state if it uses the FPU again. - */ - fpstate_drop(td); -} void cpu_setregs(void) @@ -2068,601 +1739,6 @@ makectx(struct trapframe *tf, struct pcb *pcb) pcb->pcb_rsp = tf->tf_rsp; } -int -ptrace_set_pc(struct thread *td, unsigned long addr) -{ - - td->td_frame->tf_rip = addr; - set_pcb_flags(td->td_pcb, PCB_FULL_IRET); - return (0); -} - -int -ptrace_single_step(struct thread *td) -{ - - PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); - if ((td->td_frame->tf_rflags & PSL_T) == 0) { - td->td_frame->tf_rflags |= PSL_T; - td->td_dbgflags |= TDB_STEP; - } - return (0); -} - -int -ptrace_clear_single_step(struct thread *td) -{ - - PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); - td->td_frame->tf_rflags &= ~PSL_T; - td->td_dbgflags &= ~TDB_STEP; - return (0); -} - -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) { /* XXXKIB */ - 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); -} - -/* XXX check all this stuff! */ -/* externalize from sv_xmm */ -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; - - /* pcb -> fpregs */ - bzero(fpregs, sizeof(*fpregs)); - - /* FPU control/status */ - 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; - - /* FPU registers */ - for (i = 0; i < 8; ++i) - bcopy(sv_xmm->sv_fp[i].fp_acc.fp_bytes, fpregs->fpr_acc[i], 10); - - /* SSE registers */ - for (i = 0; i < 16; ++i) - bcopy(sv_xmm->sv_xmm[i].xmm_bytes, fpregs->fpr_xacc[i], 16); -} - -/* internalize from fpregs into sv_xmm */ -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; - - /* fpregs -> pcb */ - /* FPU control/status */ - 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; - - /* FPU registers */ - for (i = 0; i < 8; ++i) - bcopy(fpregs->fpr_acc[i], sv_xmm->sv_fp[i].fp_acc.fp_bytes, 10); - - /* SSE registers */ - for (i = 0; i < 16; ++i) - bcopy(fpregs->fpr_xacc[i], sv_xmm->sv_xmm[i].xmm_bytes, 16); -} - -/* externalize from td->pcb */ -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); -} - -/* internalize to td->pcb */ -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); -} - -/* - * Get machine context. - */ -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, 0); - 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; - bzero(mcp->mc_spare, sizeof(mcp->mc_spare)); - return (0); -} - -/* - * Set machine context. - * - * However, we don't set any but the user modifiable flags, and we won't - * touch the cs selector. - */ -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); - 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 = __builtin_alloca(mcp->mc_xfpustate_len); - ret = copyin((void *)mcp->mc_xfpustate, xfpustate, - mcp->mc_xfpustate_len); - if (ret != 0) - return (ret); - } else - xfpustate = NULL; - ret = set_fpcontext(td, mcp, xfpustate, mcp->mc_xfpustate_len); - 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; - } - return (0); -} - -static void -get_fpcontext(struct thread *td, mcontext_t *mcp, char *xfpusave, - size_t xfpusave_len) -{ - size_t max_len, 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 (!use_xsave || xfpusave_len == 0) - return; - max_len = cpu_max_ext_state_size - sizeof(struct savefpu); - len = xfpusave_len; - if (len > max_len) { - len = max_len; - bzero(xfpusave + max_len, len - max_len); - } - mcp->mc_flags |= _MC_HASFPXSTATE; - mcp->mc_xfpustate_len = len; - bcopy(get_pcb_user_save_td(td) + 1, xfpusave, len); -} - -static 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) { - /* We don't care what state is left in the FPU or PCB. */ - 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(); - /* - * XXX force a full drop of the fpu. The above only drops it if we - * owned it. - * - * XXX I don't much like fpugetuserregs()'s semantics of doing a full - * drop. Dropping only to the pcb matches fnsave's behaviour. - * We only need to drop to !PCB_INITDONE in sendsig(). But - * sendsig() is the only caller of fpugetuserregs()... perhaps we just - * have too many layers. - */ - 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 { - /* - * Don't let an illegal value for dr7 get set. Specifically, - * check for undefined settings. Setting these bit patterns - * result in undefined behaviour and can lead to an unexpected - * TRCTRAP or a general protection fault right here. - * Upper bits of dr6 and dr7 must not be set - */ - 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; - - /* - * Don't let a process set a breakpoint that is not within the - * process's address space. If a process could do this, it - * could halt the system by setting a breakpoint in the kernel - * (if ddb was enabled). Thus, we need to check to make sure - * that no breakpoints are being enabled for addresses outside - * process's address space. - * - * XXX - what about when the watched area of the user's - * address space is written into from within the kernel - * ... wouldn't that still cause a breakpoint to be generated - * from within kernel mode? - */ - - if (DBREG_DR7_ENABLED(dbregs->dr[7], 0)) { - /* dr0 is enabled */ - if (dbregs->dr[0] >= VM_MAXUSER_ADDRESS) - return (EINVAL); - } - if (DBREG_DR7_ENABLED(dbregs->dr[7], 1)) { - /* dr1 is enabled */ - if (dbregs->dr[1] >= VM_MAXUSER_ADDRESS) - return (EINVAL); - } - if (DBREG_DR7_ENABLED(dbregs->dr[7], 2)) { - /* dr2 is enabled */ - if (dbregs->dr[2] >= VM_MAXUSER_ADDRESS) - return (EINVAL); - } - if (DBREG_DR7_ENABLED(dbregs->dr[7], 3)) { - /* dr3 is enabled */ - 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); /* Turn off the control bits first */ - load_dr0(0); - load_dr1(0); - load_dr2(0); - load_dr3(0); - load_dr6(0); -} - -/* - * Return > 0 if a hardware breakpoint has been hit, and the - * breakpoint was in user space. Return 0, otherwise. - */ -int -user_dbreg_trap(register_t dr6) -{ - u_int64_t dr7; - u_int64_t bp; /* breakpoint bits extracted from dr6 */ - int nbp; /* number of breakpoints that triggered */ - caddr_t addr[4]; /* breakpoint addresses */ - int i; - - bp = dr6 & DBREG_DR6_BMASK; - if (bp == 0) { - /* - * None of the breakpoint bits are set meaning this - * trap was not caused by any of the debug registers - */ - return 0; - } - - dr7 = rdr7(); - if ((dr7 & 0x000000ff) == 0) { - /* - * all GE and LE bits in the dr7 register are zero, - * thus the trap couldn't have been caused by the - * hardware debug registers - */ - return 0; - } - - nbp = 0; - - /* - * at least one of the breakpoints were hit, check to see - * which ones and if any of them are user space addresses - */ - - 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) { - /* - * addr[i] is in user space - */ - return nbp; - } - } - - /* - * None of the breakpoints are in user space. - */ - return 0; -} - /* * The pcb_flags is only modified by current thread, or by other threads * when current thread is stopped. However, current thread may change it diff --git a/sys/amd64/amd64/ptrace_machdep.c b/sys/amd64/amd64/ptrace_machdep.c index 5a552b03c90c..74fd48c68492 100644 --- a/sys/amd64/amd64/ptrace_machdep.c +++ b/sys/amd64/amd64/ptrace_machdep.c @@ -32,7 +32,9 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include +#include #include #include #include @@ -278,3 +280,34 @@ cpu_ptrace(struct thread *td, int req, void *addr, int data) return (error); } + +int +ptrace_set_pc(struct thread *td, unsigned long addr) +{ + + td->td_frame->tf_rip = addr; + set_pcb_flags(td->td_pcb, PCB_FULL_IRET); + return (0); +} + +int +ptrace_single_step(struct thread *td) +{ + + PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); + if ((td->td_frame->tf_rflags & PSL_T) == 0) { + td->td_frame->tf_rflags |= PSL_T; + td->td_dbgflags |= TDB_STEP; + } + return (0); +} + +int +ptrace_clear_single_step(struct thread *td) +{ + + PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); + td->td_frame->tf_rflags &= ~PSL_T; + td->td_dbgflags &= ~TDB_STEP; + return (0); +} diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index b50034f38d96..c17730b37409 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -83,6 +83,7 @@ amd64/amd64/efirt_machdep.c optional efirt amd64/amd64/efirt_support.S optional efirt amd64/amd64/elf_machdep.c standard amd64/amd64/exception.S standard +amd64/amd64/exec_machdep.c standard amd64/amd64/fpu.c standard amd64/amd64/gdb_machdep.c optional gdb amd64/amd64/in_cksum.c optional inet | inet6 -- cgit v1.2.3