diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -104,6 +104,7 @@ i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb +i386/i386/exec_machdep.c standard i386/i386/elan-mmcr.c optional cpu_elan | cpu_soekris i386/i386/elf_machdep.c standard i386/i386/exception.s standard diff --git a/sys/i386/i386/exec_machdep.c b/sys/i386/i386/exec_machdep.c new file mode 100644 --- /dev/null +++ b/sys/i386/i386/exec_machdep.c @@ -0,0 +1,1443 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 2018 The FreeBSD Foundation + * 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. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * 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 +#include +#include +#include +#include +#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 +#include +#include + +static void fpstate_drop(struct thread *td); +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); +#ifdef COMPAT_43 +static void osendsig(sig_t catcher, ksiginfo_t *, sigset_t *mask); +#endif +#ifdef COMPAT_FREEBSD4 +static void freebsd4_sendsig(sig_t catcher, ksiginfo_t *, sigset_t *mask); +#endif + +extern struct sysentvec elf32_freebsd_sysvec; + +/* + * 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. + */ +#ifdef COMPAT_43 +static void +osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) +{ + struct osigframe sf, *fp; + struct proc *p; + struct thread *td; + struct sigacts *psp; + struct trapframe *regs; + int sig; + int oonstack; + + td = curthread; + 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_esp); + + /* Allocate space for the signal handler context. */ + if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && + SIGISMEMBER(psp->ps_sigonstack, sig)) { + fp = (struct osigframe *)((uintptr_t)td->td_sigstk.ss_sp + + td->td_sigstk.ss_size - sizeof(struct osigframe)); +#if defined(COMPAT_43) + td->td_sigstk.ss_flags |= SS_ONSTACK; +#endif + } else + fp = (struct osigframe *)regs->tf_esp - 1; + + /* Build the argument list for the signal handler. */ + sf.sf_signum = sig; + sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; + bzero(&sf.sf_siginfo, sizeof(sf.sf_siginfo)); + if (SIGISMEMBER(psp->ps_siginfo, sig)) { + /* Signal handler installed with SA_SIGINFO. */ + sf.sf_arg2 = (register_t)&fp->sf_siginfo; + sf.sf_siginfo.si_signo = sig; + sf.sf_siginfo.si_code = ksi->ksi_code; + sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; + sf.sf_addr = 0; + } else { + /* Old FreeBSD-style arguments. */ + sf.sf_arg2 = ksi->ksi_code; + sf.sf_addr = (register_t)ksi->ksi_addr; + sf.sf_ahu.sf_handler = catcher; + } + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(p); + + /* Save most if not all of trap frame. */ + sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; + sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; + sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; + sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; + sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; + sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; + sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; + sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; + sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; + sf.sf_siginfo.si_sc.sc_es = regs->tf_es; + sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; + sf.sf_siginfo.si_sc.sc_gs = rgs(); + sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; + + /* Build the signal context to be used by osigreturn(). */ + sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; + SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); + sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; + sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; + sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; + sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; + sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; + sf.sf_siginfo.si_sc.sc_err = regs->tf_err; + + /* + * If we're a vm86 process, we want to save the segment registers. + * We also change eflags to be our emulated eflags, not the actual + * eflags. + */ + if (regs->tf_eflags & PSL_VM) { + /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ + struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; + struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; + + sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; + sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; + sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; + sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; + + if (vm86->vm86_has_vme == 0) + sf.sf_siginfo.si_sc.sc_ps = + (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | + (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); + + /* See sendsig() for comments. */ + tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); + } + + /* + * Copy the sigframe out to the user's stack. + */ + if (copyout(&sf, fp, sizeof(*fp)) != 0) { + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + regs->tf_esp = (int)fp; + if (p->p_sysent->sv_sigcode_base != 0) { + regs->tf_eip = p->p_sysent->sv_sigcode_base + szsigcode - + szosigcode; + } else { + /* a.out sysentvec does not use shared page */ + regs->tf_eip = p->p_sysent->sv_psstrings - szosigcode; + } + regs->tf_eflags &= ~(PSL_T | PSL_D); + regs->tf_cs = _ucodesel; + regs->tf_ds = _udatasel; + regs->tf_es = _udatasel; + regs->tf_fs = _udatasel; + load_gs(_udatasel); + regs->tf_ss = _udatasel; + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); +} +#endif /* COMPAT_43 */ + +#ifdef COMPAT_FREEBSD4 +static void +freebsd4_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) +{ + struct sigframe4 sf, *sfp; + struct proc *p; + struct thread *td; + struct sigacts *psp; + struct trapframe *regs; + int sig; + int oonstack; + + td = curthread; + 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_esp); + + /* 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; + sf.sf_uc.uc_mcontext.mc_gs = rgs(); + bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); + bzero(sf.sf_uc.uc_mcontext.mc_fpregs, + sizeof(sf.sf_uc.uc_mcontext.mc_fpregs)); + bzero(sf.sf_uc.uc_mcontext.__spare__, + sizeof(sf.sf_uc.uc_mcontext.__spare__)); + bzero(sf.sf_uc.__spare__, sizeof(sf.sf_uc.__spare__)); + + /* Allocate space for the signal handler context. */ + if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && + SIGISMEMBER(psp->ps_sigonstack, sig)) { + sfp = (struct sigframe4 *)((uintptr_t)td->td_sigstk.ss_sp + + td->td_sigstk.ss_size - sizeof(struct sigframe4)); +#if defined(COMPAT_43) + td->td_sigstk.ss_flags |= SS_ONSTACK; +#endif + } else + sfp = (struct sigframe4 *)regs->tf_esp - 1; + + /* Build the argument list for the signal handler. */ + sf.sf_signum = sig; + sf.sf_ucontext = (register_t)&sfp->sf_uc; + bzero(&sf.sf_si, sizeof(sf.sf_si)); + if (SIGISMEMBER(psp->ps_siginfo, sig)) { + /* Signal handler installed with SA_SIGINFO. */ + sf.sf_siginfo = (register_t)&sfp->sf_si; + sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; + + /* Fill in POSIX parts */ + sf.sf_si.si_signo = sig; + sf.sf_si.si_code = ksi->ksi_code; + sf.sf_si.si_addr = ksi->ksi_addr; + } else { + /* Old FreeBSD-style arguments. */ + sf.sf_siginfo = ksi->ksi_code; + sf.sf_addr = (register_t)ksi->ksi_addr; + sf.sf_ahu.sf_handler = catcher; + } + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(p); + + /* + * If we're a vm86 process, we want to save the segment registers. + * We also change eflags to be our emulated eflags, not the actual + * eflags. + */ + if (regs->tf_eflags & PSL_VM) { + struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; + struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; + + sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; + sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; + sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; + sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; + + if (vm86->vm86_has_vme == 0) + sf.sf_uc.uc_mcontext.mc_eflags = + (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | + (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); + + /* + * Clear PSL_NT to inhibit T_TSSFLT faults on return from + * syscalls made by the signal handler. This just avoids + * wasting time for our lazy fixup of such faults. PSL_NT + * does nothing in vm86 mode, but vm86 programs can set it + * almost legitimately in probes for old cpu types. + */ + tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); + } + + /* + * Copy the sigframe out to the user's stack. + */ + if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + regs->tf_esp = (int)sfp; + regs->tf_eip = p->p_sysent->sv_sigcode_base + szsigcode - + szfreebsd4_sigcode; + regs->tf_eflags &= ~(PSL_T | PSL_D); + regs->tf_cs = _ucodesel; + regs->tf_ds = _udatasel; + regs->tf_es = _udatasel; + regs->tf_fs = _udatasel; + regs->tf_ss = _udatasel; + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); +} +#endif /* COMPAT_FREEBSD4 */ + +void +sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) +{ + struct sigframe sf, *sfp; + struct proc *p; + struct thread *td; + struct sigacts *psp; + char *sp; + struct trapframe *regs; + struct segment_descriptor *sdp; + char *xfpusave; + size_t xfpusave_len; + int sig; + int oonstack; + + td = curthread; + 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); +#ifdef COMPAT_FREEBSD4 + if (SIGISMEMBER(psp->ps_freebsd4, sig)) { + freebsd4_sendsig(catcher, ksi, mask); + return; + } +#endif +#ifdef COMPAT_43 + if (SIGISMEMBER(psp->ps_osigset, sig)) { + osendsig(catcher, ksi, mask); + return; + } +#endif + regs = td->td_frame; + oonstack = sigonstack(regs->tf_esp); + + if (cpu_max_ext_state_size > sizeof(union savefpu) && use_xsave) { + xfpusave_len = cpu_max_ext_state_size - sizeof(union 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; + sf.sf_uc.uc_mcontext.mc_gs = rgs(); + bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, 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); + /* + * Unconditionally fill the fsbase and gsbase into the mcontext. + */ + sdp = &td->td_pcb->pcb_fsd; + sf.sf_uc.uc_mcontext.mc_fsbase = sdp->sd_hibase << 24 | + sdp->sd_lobase; + sdp = &td->td_pcb->pcb_gsd; + sf.sf_uc.uc_mcontext.mc_gsbase = sdp->sd_hibase << 24 | + sdp->sd_lobase; + bzero(sf.sf_uc.uc_mcontext.mc_spare2, + sizeof(sf.sf_uc.uc_mcontext.mc_spare2)); + + /* 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_esp - 128; + if (xfpusave != NULL) { + sp -= xfpusave_len; + sp = (char *)((unsigned int)sp & ~0x3F); + sf.sf_uc.uc_mcontext.mc_xfpustate = (register_t)sp; + } + sp -= sizeof(struct sigframe); + + /* Align to 16 bytes. */ + sfp = (struct sigframe *)((unsigned int)sp & ~0xF); + + /* Build the argument list for the signal handler. */ + sf.sf_signum = sig; + sf.sf_ucontext = (register_t)&sfp->sf_uc; + bzero(&sf.sf_si, sizeof(sf.sf_si)); + if (SIGISMEMBER(psp->ps_siginfo, sig)) { + /* Signal handler installed with SA_SIGINFO. */ + sf.sf_siginfo = (register_t)&sfp->sf_si; + 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 */ + } else { + /* Old FreeBSD-style arguments. */ + sf.sf_siginfo = ksi->ksi_code; + sf.sf_addr = (register_t)ksi->ksi_addr; + sf.sf_ahu.sf_handler = catcher; + } + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(p); + + /* + * If we're a vm86 process, we want to save the segment registers. + * We also change eflags to be our emulated eflags, not the actual + * eflags. + */ + if (regs->tf_eflags & PSL_VM) { + struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; + struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; + + sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; + sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; + sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; + sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; + + if (vm86->vm86_has_vme == 0) + sf.sf_uc.uc_mcontext.mc_eflags = + (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | + (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); + + /* + * Clear PSL_NT to inhibit T_TSSFLT faults on return from + * syscalls made by the signal handler. This just avoids + * wasting time for our lazy fixup of such faults. PSL_NT + * does nothing in vm86 mode, but vm86 programs can set it + * almost legitimately in probes for old cpu types. + */ + tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); + } + + /* + * 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)) { + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + regs->tf_esp = (int)sfp; + regs->tf_eip = p->p_sysent->sv_sigcode_base; + if (regs->tf_eip == 0) + regs->tf_eip = p->p_sysent->sv_psstrings - szsigcode; + regs->tf_eflags &= ~(PSL_T | PSL_D); + regs->tf_cs = _ucodesel; + regs->tf_ds = _udatasel; + regs->tf_es = _udatasel; + regs->tf_fs = _udatasel; + regs->tf_ss = _udatasel; + 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. + */ +#ifdef COMPAT_43 +int +osigreturn(struct thread *td, struct osigreturn_args *uap) +{ + struct osigcontext sc; + struct trapframe *regs; + struct osigcontext *scp; + int eflags, error; + ksiginfo_t ksi; + + regs = td->td_frame; + error = copyin(uap->sigcntxp, &sc, sizeof(sc)); + if (error != 0) + return (error); + scp = ≻ + eflags = scp->sc_ps; + if (eflags & PSL_VM) { + struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; + struct vm86_kernel *vm86; + + /* + * if pcb_ext == 0 or vm86_inited == 0, the user hasn't + * set up the vm86 area, and we can't enter vm86 mode. + */ + if (td->td_pcb->pcb_ext == 0) + return (EINVAL); + vm86 = &td->td_pcb->pcb_ext->ext_vm86; + if (vm86->vm86_inited == 0) + return (EINVAL); + + /* Go back to user mode if both flags are set. */ + if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) { + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGBUS; + ksi.ksi_code = BUS_OBJERR; + ksi.ksi_addr = (void *)regs->tf_eip; + trapsignal(td, &ksi); + } + + if (vm86->vm86_has_vme) { + eflags = (tf->tf_eflags & ~VME_USERCHANGE) | + (eflags & VME_USERCHANGE) | PSL_VM; + } else { + vm86->vm86_eflags = eflags; /* save VIF, VIP */ + eflags = (tf->tf_eflags & ~VM_USERCHANGE) | + (eflags & VM_USERCHANGE) | PSL_VM; + } + tf->tf_vm86_ds = scp->sc_ds; + tf->tf_vm86_es = scp->sc_es; + tf->tf_vm86_fs = scp->sc_fs; + tf->tf_vm86_gs = scp->sc_gs; + tf->tf_ds = _udatasel; + tf->tf_es = _udatasel; + tf->tf_fs = _udatasel; + } else { + /* + * Don't allow users to change privileged or reserved flags. + */ + if (!EFL_SECURE(eflags, regs->tf_eflags)) { + 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. + */ + if (!CS_SECURE(scp->sc_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_eip; + trapsignal(td, &ksi); + return (EINVAL); + } + regs->tf_ds = scp->sc_ds; + regs->tf_es = scp->sc_es; + regs->tf_fs = scp->sc_fs; + } + + /* Restore remaining registers. */ + regs->tf_eax = scp->sc_eax; + regs->tf_ebx = scp->sc_ebx; + regs->tf_ecx = scp->sc_ecx; + regs->tf_edx = scp->sc_edx; + regs->tf_esi = scp->sc_esi; + regs->tf_edi = scp->sc_edi; + regs->tf_cs = scp->sc_cs; + regs->tf_ss = scp->sc_ss; + regs->tf_isp = scp->sc_isp; + regs->tf_ebp = scp->sc_fp; + regs->tf_esp = scp->sc_sp; + regs->tf_eip = scp->sc_pc; + regs->tf_eflags = eflags; + +#if defined(COMPAT_43) + if (scp->sc_onstack & 1) + td->td_sigstk.ss_flags |= SS_ONSTACK; + else + td->td_sigstk.ss_flags &= ~SS_ONSTACK; +#endif + kern_sigprocmask(td, SIG_SETMASK, (sigset_t *)&scp->sc_mask, NULL, + SIGPROCMASK_OLD); + return (EJUSTRETURN); +} +#endif /* COMPAT_43 */ + +#ifdef COMPAT_FREEBSD4 +int +freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) +{ + struct ucontext4 uc; + struct trapframe *regs; + struct ucontext4 *ucp; + int cs, eflags, error; + ksiginfo_t ksi; + + error = copyin(uap->sigcntxp, &uc, sizeof(uc)); + if (error != 0) + return (error); + ucp = &uc; + regs = td->td_frame; + eflags = ucp->uc_mcontext.mc_eflags; + if (eflags & PSL_VM) { + struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; + struct vm86_kernel *vm86; + + /* + * if pcb_ext == 0 or vm86_inited == 0, the user hasn't + * set up the vm86 area, and we can't enter vm86 mode. + */ + if (td->td_pcb->pcb_ext == 0) + return (EINVAL); + vm86 = &td->td_pcb->pcb_ext->ext_vm86; + if (vm86->vm86_inited == 0) + return (EINVAL); + + /* Go back to user mode if both flags are set. */ + if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) { + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGBUS; + ksi.ksi_code = BUS_OBJERR; + ksi.ksi_addr = (void *)regs->tf_eip; + trapsignal(td, &ksi); + } + if (vm86->vm86_has_vme) { + eflags = (tf->tf_eflags & ~VME_USERCHANGE) | + (eflags & VME_USERCHANGE) | PSL_VM; + } else { + vm86->vm86_eflags = eflags; /* save VIF, VIP */ + eflags = (tf->tf_eflags & ~VM_USERCHANGE) | + (eflags & VM_USERCHANGE) | PSL_VM; + } + bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); + tf->tf_eflags = eflags; + tf->tf_vm86_ds = tf->tf_ds; + tf->tf_vm86_es = tf->tf_es; + tf->tf_vm86_fs = tf->tf_fs; + tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; + tf->tf_ds = _udatasel; + tf->tf_es = _udatasel; + tf->tf_fs = _udatasel; + } else { + /* + * Don't allow users to change privileged or reserved flags. + */ + if (!EFL_SECURE(eflags, regs->tf_eflags)) { + uprintf( + "pid %d (%s): freebsd4_sigreturn eflags = 0x%x\n", + td->td_proc->p_pid, td->td_name, eflags); + 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): freebsd4_sigreturn cs = 0x%x\n", + td->td_proc->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_eip; + trapsignal(td, &ksi); + return (EINVAL); + } + + bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); + } + +#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); +} +#endif /* COMPAT_FREEBSD4 */ + +int +sys_sigreturn(struct thread *td, struct sigreturn_args *uap) +{ + ucontext_t uc; + struct proc *p; + struct trapframe *regs; + ucontext_t *ucp; + char *xfpustate; + size_t xfpustate_len; + int cs, eflags, error, ret; + ksiginfo_t ksi; + + p = td->td_proc; + + error = copyin(uap->sigcntxp, &uc, sizeof(uc)); + if (error != 0) + 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; + eflags = ucp->uc_mcontext.mc_eflags; + if (eflags & PSL_VM) { + struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; + struct vm86_kernel *vm86; + + /* + * if pcb_ext == 0 or vm86_inited == 0, the user hasn't + * set up the vm86 area, and we can't enter vm86 mode. + */ + if (td->td_pcb->pcb_ext == 0) + return (EINVAL); + vm86 = &td->td_pcb->pcb_ext->ext_vm86; + if (vm86->vm86_inited == 0) + return (EINVAL); + + /* Go back to user mode if both flags are set. */ + if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) { + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGBUS; + ksi.ksi_code = BUS_OBJERR; + ksi.ksi_addr = (void *)regs->tf_eip; + trapsignal(td, &ksi); + } + + if (vm86->vm86_has_vme) { + eflags = (tf->tf_eflags & ~VME_USERCHANGE) | + (eflags & VME_USERCHANGE) | PSL_VM; + } else { + vm86->vm86_eflags = eflags; /* save VIF, VIP */ + eflags = (tf->tf_eflags & ~VM_USERCHANGE) | + (eflags & VM_USERCHANGE) | PSL_VM; + } + bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); + tf->tf_eflags = eflags; + tf->tf_vm86_ds = tf->tf_ds; + tf->tf_vm86_es = tf->tf_es; + tf->tf_vm86_fs = tf->tf_fs; + tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; + tf->tf_ds = _udatasel; + tf->tf_es = _udatasel; + tf->tf_fs = _udatasel; + } else { + /* + * Don't allow users to change privileged or reserved flags. + */ + if (!EFL_SECURE(eflags, regs->tf_eflags)) { + uprintf("pid %d (%s): sigreturn eflags = 0x%x\n", + td->td_proc->p_pid, td->td_name, eflags); + 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", + td->td_proc->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_eip; + 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(union 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) + return (ret); + bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); + } + +#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); +} + +/* + * 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(); + } + pcb->pcb_flags &= ~PCB_DBREGS; +} + +#ifdef COMPAT_43 +static void +setup_priv_lcall_gate(struct proc *p) +{ + struct i386_ldt_args uap; + union descriptor desc; + u_int lcall_addr; + + bzero(&uap, sizeof(uap)); + uap.start = 0; + uap.num = 1; + lcall_addr = p->p_sysent->sv_psstrings - sz_lcall_tramp; + bzero(&desc, sizeof(desc)); + desc.sd.sd_type = SDT_MEMERA; + desc.sd.sd_dpl = SEL_UPL; + desc.sd.sd_p = 1; + desc.sd.sd_def32 = 1; + desc.sd.sd_gran = 1; + desc.sd.sd_lolimit = 0xffff; + desc.sd.sd_hilimit = 0xf; + desc.sd.sd_lobase = lcall_addr; + desc.sd.sd_hibase = lcall_addr >> 24; + i386_set_ldt(curthread, &uap, &desc); +} +#endif + +/* + * 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_eflags; + + regs = td->td_frame; + pcb = td->td_pcb; + + /* Reset pc->pcb_gs and %gs before possibly invalidating it. */ + pcb->pcb_gs = _udatasel; + load_gs(_udatasel); + + mtx_lock_spin(&dt_lock); + if (td->td_proc->p_md.md_ldt != NULL) + user_ldt_free(td); + else + mtx_unlock_spin(&dt_lock); + +#ifdef COMPAT_43 + if (td->td_proc->p_sysent->sv_psstrings != + elf32_freebsd_sysvec.sv_psstrings) + setup_priv_lcall_gate(td->td_proc); +#endif + + /* + * Reset the fs and gs bases. The values from the old address + * space do not make sense for the new program. In particular, + * gsbase might be the TLS base for the old program but the new + * program has no TLS now. + */ + set_fsbase(td, 0); + set_gsbase(td, 0); + + /* Make sure edx is 0x0 on entry. Linux binaries depend on it. */ + saved_eflags = regs->tf_eflags & PSL_T; + bzero((char *)regs, sizeof(struct trapframe)); + regs->tf_eip = imgp->entry_addr; + regs->tf_esp = stack; + regs->tf_eflags = PSL_USER | saved_eflags; + regs->tf_ss = _udatasel; + regs->tf_ds = _udatasel; + regs->tf_es = _udatasel; + regs->tf_fs = _udatasel; + regs->tf_cs = _ucodesel; + + /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ + regs->tf_ebx = (register_t)imgp->ps_strings; + + x86_clear_dbregs(pcb); + + pcb->pcb_initial_npxcw = __INITIAL_NPXCW__; + + /* + * 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 pcb *pcb; + struct trapframe *tp; + + tp = td->td_frame; + pcb = td->td_pcb; + regs->r_gs = pcb->pcb_gs; + return (fill_frame_regs(tp, regs)); +} + +int +fill_frame_regs(struct trapframe *tp, struct reg *regs) +{ + + regs->r_fs = tp->tf_fs; + regs->r_es = tp->tf_es; + regs->r_ds = tp->tf_ds; + regs->r_edi = tp->tf_edi; + regs->r_esi = tp->tf_esi; + regs->r_ebp = tp->tf_ebp; + regs->r_ebx = tp->tf_ebx; + regs->r_edx = tp->tf_edx; + regs->r_ecx = tp->tf_ecx; + regs->r_eax = tp->tf_eax; + regs->r_eip = tp->tf_eip; + regs->r_cs = tp->tf_cs; + regs->r_eflags = tp->tf_eflags; + regs->r_esp = tp->tf_esp; + regs->r_ss = tp->tf_ss; + regs->r_err = 0; + regs->r_trapno = 0; + return (0); +} + +int +set_regs(struct thread *td, struct reg *regs) +{ + struct pcb *pcb; + struct trapframe *tp; + + tp = td->td_frame; + if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || + !CS_SECURE(regs->r_cs)) + return (EINVAL); + pcb = td->td_pcb; + tp->tf_fs = regs->r_fs; + tp->tf_es = regs->r_es; + tp->tf_ds = regs->r_ds; + tp->tf_edi = regs->r_edi; + tp->tf_esi = regs->r_esi; + tp->tf_ebp = regs->r_ebp; + tp->tf_ebx = regs->r_ebx; + tp->tf_edx = regs->r_edx; + tp->tf_ecx = regs->r_ecx; + tp->tf_eax = regs->r_eax; + tp->tf_eip = regs->r_eip; + tp->tf_cs = regs->r_cs; + tp->tf_eflags = regs->r_eflags; + tp->tf_esp = regs->r_esp; + tp->tf_ss = regs->r_ss; + pcb->pcb_gs = regs->r_gs; + return (0); +} + +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)); + npxgetregs(td); + if (cpu_fxsr) + npx_fill_fpregs_xmm(&get_pcb_user_save_td(td)->sv_xmm, + (struct save87 *)fpregs); + else + bcopy(&get_pcb_user_save_td(td)->sv_87, fpregs, + sizeof(*fpregs)); + return (0); +} + +int +set_fpregs(struct thread *td, struct fpreg *fpregs) +{ + + critical_enter(); + if (cpu_fxsr) + npx_set_fpregs_xmm((struct save87 *)fpregs, + &get_pcb_user_save_td(td)->sv_xmm); + else + bcopy(fpregs, &get_pcb_user_save_td(td)->sv_87, + sizeof(*fpregs)); + npxuserinited(td); + critical_exit(); + return (0); +} + +/* + * Get machine context. + */ +int +get_mcontext(struct thread *td, mcontext_t *mcp, int flags) +{ + struct trapframe *tp; + struct segment_descriptor *sdp; + + tp = td->td_frame; + + PROC_LOCK(curthread->td_proc); + mcp->mc_onstack = sigonstack(tp->tf_esp); + PROC_UNLOCK(curthread->td_proc); + mcp->mc_gs = td->td_pcb->pcb_gs; + mcp->mc_fs = tp->tf_fs; + mcp->mc_es = tp->tf_es; + mcp->mc_ds = tp->tf_ds; + mcp->mc_edi = tp->tf_edi; + mcp->mc_esi = tp->tf_esi; + mcp->mc_ebp = tp->tf_ebp; + mcp->mc_isp = tp->tf_isp; + mcp->mc_eflags = tp->tf_eflags; + if (flags & GET_MC_CLEAR_RET) { + mcp->mc_eax = 0; + mcp->mc_edx = 0; + mcp->mc_eflags &= ~PSL_C; + } else { + mcp->mc_eax = tp->tf_eax; + mcp->mc_edx = tp->tf_edx; + } + mcp->mc_ebx = tp->tf_ebx; + mcp->mc_ecx = tp->tf_ecx; + mcp->mc_eip = tp->tf_eip; + mcp->mc_cs = tp->tf_cs; + mcp->mc_esp = tp->tf_esp; + mcp->mc_ss = tp->tf_ss; + mcp->mc_len = sizeof(*mcp); + get_fpcontext(td, mcp, NULL, 0); + sdp = &td->td_pcb->pcb_fsd; + mcp->mc_fsbase = sdp->sd_hibase << 24 | sdp->sd_lobase; + sdp = &td->td_pcb->pcb_gsd; + mcp->mc_gsbase = sdp->sd_hibase << 24 | sdp->sd_lobase; + mcp->mc_flags = 0; + mcp->mc_xfpustate = 0; + mcp->mc_xfpustate_len = 0; + bzero(mcp->mc_spare2, sizeof(mcp->mc_spare2)); + 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 trapframe *tp; + char *xfpustate; + int eflags, ret; + + tp = td->td_frame; + if (mcp->mc_len != sizeof(*mcp) || + (mcp->mc_flags & ~_MC_FLAG_MASK) != 0) + return (EINVAL); + eflags = (mcp->mc_eflags & PSL_USERCHANGE) | + (tp->tf_eflags & ~PSL_USERCHANGE); + if (mcp->mc_flags & _MC_HASFPXSTATE) { + if (mcp->mc_xfpustate_len > cpu_max_ext_state_size - + sizeof(union 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_fs = mcp->mc_fs; + tp->tf_es = mcp->mc_es; + tp->tf_ds = mcp->mc_ds; + tp->tf_edi = mcp->mc_edi; + tp->tf_esi = mcp->mc_esi; + tp->tf_ebp = mcp->mc_ebp; + tp->tf_ebx = mcp->mc_ebx; + tp->tf_edx = mcp->mc_edx; + tp->tf_ecx = mcp->mc_ecx; + tp->tf_eax = mcp->mc_eax; + tp->tf_eip = mcp->mc_eip; + tp->tf_eflags = eflags; + tp->tf_esp = mcp->mc_esp; + tp->tf_ss = mcp->mc_ss; + td->td_pcb->pcb_gs = mcp->mc_gs; + 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 = npxgetregs(td); + bcopy(get_pcb_user_save_td(td), &mcp->mc_fpstate[0], + sizeof(mcp->mc_fpstate)); + mcp->mc_fpformat = npxformat(); + if (!use_xsave || xfpusave_len == 0) + return; + max_len = cpu_max_ext_state_size - sizeof(union 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_387 && + 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 = npxsetregs(td, (union savefpu *)&mcp->mc_fpstate, + xfpustate, xfpustate_len); + } else + return (EINVAL); + return (error); +} + +static 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) + npxdrop(); + /* + * XXX force a full drop of the npx. The above only drops it if we + * owned it. npxgetregs() has the same bug in the !cpu_fxsr case. + * + * XXX I don't much like npxgetregs()'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 npxgetregs()... perhaps we just + * have too many layers. + */ + curthread->td_pcb->pcb_flags &= ~(PCB_NPXINITDONE | + PCB_NPXUSERINITDONE); + 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; + 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. + */ + for (i = 0; i < 4; i++) { + if (DBREG_DR7_ACCESS(dbregs->dr[7], i) == 0x02) + return (EINVAL); + if (DBREG_DR7_LEN(dbregs->dr[7], i) == 0x02) + 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]; + + pcb->pcb_flags |= PCB_DBREGS; + } + + return (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_int32_t dr7; + u_int32_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/i386/i386/machdep.c b/sys/i386/i386/machdep.c --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -163,11 +163,6 @@ void identify_cpu(void); static void cpu_startup(void *); -static void fpstate_drop(struct thread *td); -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); /* Intel ICH registers */ @@ -181,13 +176,6 @@ int cold = 1; -#ifdef COMPAT_43 -static void osendsig(sig_t catcher, ksiginfo_t *, sigset_t *mask); -#endif -#ifdef COMPAT_FREEBSD4 -static void freebsd4_sendsig(sig_t catcher, ksiginfo_t *, sigset_t *mask); -#endif - long Maxmem = 0; long realmem = 0; @@ -304,912 +292,6 @@ cpu_setregs(); } -/* - * 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. - */ -#ifdef COMPAT_43 -static void -osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) -{ - struct osigframe sf, *fp; - struct proc *p; - struct thread *td; - struct sigacts *psp; - struct trapframe *regs; - int sig; - int oonstack; - - td = curthread; - 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_esp); - - /* Allocate space for the signal handler context. */ - if ((td->td_pflags & TDP_ALTSTACK) && !oonstack && - SIGISMEMBER(psp->ps_sigonstack, sig)) { - fp = (struct osigframe *)((uintptr_t)td->td_sigstk.ss_sp + - td->td_sigstk.ss_size - sizeof(struct osigframe)); -#if defined(COMPAT_43) - td->td_sigstk.ss_flags |= SS_ONSTACK; -#endif - } else - fp = (struct osigframe *)regs->tf_esp - 1; - - /* Build the argument list for the signal handler. */ - sf.sf_signum = sig; - sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; - bzero(&sf.sf_siginfo, sizeof(sf.sf_siginfo)); - if (SIGISMEMBER(psp->ps_siginfo, sig)) { - /* Signal handler installed with SA_SIGINFO. */ - sf.sf_arg2 = (register_t)&fp->sf_siginfo; - sf.sf_siginfo.si_signo = sig; - sf.sf_siginfo.si_code = ksi->ksi_code; - sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; - sf.sf_addr = 0; - } else { - /* Old FreeBSD-style arguments. */ - sf.sf_arg2 = ksi->ksi_code; - sf.sf_addr = (register_t)ksi->ksi_addr; - sf.sf_ahu.sf_handler = catcher; - } - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(p); - - /* Save most if not all of trap frame. */ - sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; - sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; - sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; - sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; - sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; - sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; - sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; - sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; - sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; - sf.sf_siginfo.si_sc.sc_es = regs->tf_es; - sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; - sf.sf_siginfo.si_sc.sc_gs = rgs(); - sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; - - /* Build the signal context to be used by osigreturn(). */ - sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; - SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); - sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; - sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; - sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; - sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; - sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; - sf.sf_siginfo.si_sc.sc_err = regs->tf_err; - - /* - * If we're a vm86 process, we want to save the segment registers. - * We also change eflags to be our emulated eflags, not the actual - * eflags. - */ - if (regs->tf_eflags & PSL_VM) { - /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ - struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; - struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; - - sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; - sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; - sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; - sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; - - if (vm86->vm86_has_vme == 0) - sf.sf_siginfo.si_sc.sc_ps = - (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | - (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); - - /* See sendsig() for comments. */ - tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); - } - - /* - * Copy the sigframe out to the user's stack. - */ - if (copyout(&sf, fp, sizeof(*fp)) != 0) { - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - regs->tf_esp = (int)fp; - if (p->p_sysent->sv_sigcode_base != 0) { - regs->tf_eip = p->p_sysent->sv_sigcode_base + szsigcode - - szosigcode; - } else { - /* a.out sysentvec does not use shared page */ - regs->tf_eip = p->p_sysent->sv_psstrings - szosigcode; - } - regs->tf_eflags &= ~(PSL_T | PSL_D); - regs->tf_cs = _ucodesel; - regs->tf_ds = _udatasel; - regs->tf_es = _udatasel; - regs->tf_fs = _udatasel; - load_gs(_udatasel); - regs->tf_ss = _udatasel; - PROC_LOCK(p); - mtx_lock(&psp->ps_mtx); -} -#endif /* COMPAT_43 */ - -#ifdef COMPAT_FREEBSD4 -static void -freebsd4_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) -{ - struct sigframe4 sf, *sfp; - struct proc *p; - struct thread *td; - struct sigacts *psp; - struct trapframe *regs; - int sig; - int oonstack; - - td = curthread; - 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_esp); - - /* 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; - sf.sf_uc.uc_mcontext.mc_gs = rgs(); - bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); - bzero(sf.sf_uc.uc_mcontext.mc_fpregs, - sizeof(sf.sf_uc.uc_mcontext.mc_fpregs)); - bzero(sf.sf_uc.uc_mcontext.__spare__, - sizeof(sf.sf_uc.uc_mcontext.__spare__)); - bzero(sf.sf_uc.__spare__, sizeof(sf.sf_uc.__spare__)); - - /* Allocate space for the signal handler context. */ - if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && - SIGISMEMBER(psp->ps_sigonstack, sig)) { - sfp = (struct sigframe4 *)((uintptr_t)td->td_sigstk.ss_sp + - td->td_sigstk.ss_size - sizeof(struct sigframe4)); -#if defined(COMPAT_43) - td->td_sigstk.ss_flags |= SS_ONSTACK; -#endif - } else - sfp = (struct sigframe4 *)regs->tf_esp - 1; - - /* Build the argument list for the signal handler. */ - sf.sf_signum = sig; - sf.sf_ucontext = (register_t)&sfp->sf_uc; - bzero(&sf.sf_si, sizeof(sf.sf_si)); - if (SIGISMEMBER(psp->ps_siginfo, sig)) { - /* Signal handler installed with SA_SIGINFO. */ - sf.sf_siginfo = (register_t)&sfp->sf_si; - sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; - - /* Fill in POSIX parts */ - sf.sf_si.si_signo = sig; - sf.sf_si.si_code = ksi->ksi_code; - sf.sf_si.si_addr = ksi->ksi_addr; - } else { - /* Old FreeBSD-style arguments. */ - sf.sf_siginfo = ksi->ksi_code; - sf.sf_addr = (register_t)ksi->ksi_addr; - sf.sf_ahu.sf_handler = catcher; - } - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(p); - - /* - * If we're a vm86 process, we want to save the segment registers. - * We also change eflags to be our emulated eflags, not the actual - * eflags. - */ - if (regs->tf_eflags & PSL_VM) { - struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; - struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; - - sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; - sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; - sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; - sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; - - if (vm86->vm86_has_vme == 0) - sf.sf_uc.uc_mcontext.mc_eflags = - (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | - (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); - - /* - * Clear PSL_NT to inhibit T_TSSFLT faults on return from - * syscalls made by the signal handler. This just avoids - * wasting time for our lazy fixup of such faults. PSL_NT - * does nothing in vm86 mode, but vm86 programs can set it - * almost legitimately in probes for old cpu types. - */ - tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); - } - - /* - * Copy the sigframe out to the user's stack. - */ - if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - regs->tf_esp = (int)sfp; - regs->tf_eip = p->p_sysent->sv_sigcode_base + szsigcode - - szfreebsd4_sigcode; - regs->tf_eflags &= ~(PSL_T | PSL_D); - regs->tf_cs = _ucodesel; - regs->tf_ds = _udatasel; - regs->tf_es = _udatasel; - regs->tf_fs = _udatasel; - regs->tf_ss = _udatasel; - PROC_LOCK(p); - mtx_lock(&psp->ps_mtx); -} -#endif /* COMPAT_FREEBSD4 */ - -void -sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) -{ - struct sigframe sf, *sfp; - struct proc *p; - struct thread *td; - struct sigacts *psp; - char *sp; - struct trapframe *regs; - struct segment_descriptor *sdp; - char *xfpusave; - size_t xfpusave_len; - int sig; - int oonstack; - - td = curthread; - 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); -#ifdef COMPAT_FREEBSD4 - if (SIGISMEMBER(psp->ps_freebsd4, sig)) { - freebsd4_sendsig(catcher, ksi, mask); - return; - } -#endif -#ifdef COMPAT_43 - if (SIGISMEMBER(psp->ps_osigset, sig)) { - osendsig(catcher, ksi, mask); - return; - } -#endif - regs = td->td_frame; - oonstack = sigonstack(regs->tf_esp); - - if (cpu_max_ext_state_size > sizeof(union savefpu) && use_xsave) { - xfpusave_len = cpu_max_ext_state_size - sizeof(union 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; - sf.sf_uc.uc_mcontext.mc_gs = rgs(); - bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, 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); - /* - * Unconditionally fill the fsbase and gsbase into the mcontext. - */ - sdp = &td->td_pcb->pcb_fsd; - sf.sf_uc.uc_mcontext.mc_fsbase = sdp->sd_hibase << 24 | - sdp->sd_lobase; - sdp = &td->td_pcb->pcb_gsd; - sf.sf_uc.uc_mcontext.mc_gsbase = sdp->sd_hibase << 24 | - sdp->sd_lobase; - bzero(sf.sf_uc.uc_mcontext.mc_spare2, - sizeof(sf.sf_uc.uc_mcontext.mc_spare2)); - - /* 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_esp - 128; - if (xfpusave != NULL) { - sp -= xfpusave_len; - sp = (char *)((unsigned int)sp & ~0x3F); - sf.sf_uc.uc_mcontext.mc_xfpustate = (register_t)sp; - } - sp -= sizeof(struct sigframe); - - /* Align to 16 bytes. */ - sfp = (struct sigframe *)((unsigned int)sp & ~0xF); - - /* Build the argument list for the signal handler. */ - sf.sf_signum = sig; - sf.sf_ucontext = (register_t)&sfp->sf_uc; - bzero(&sf.sf_si, sizeof(sf.sf_si)); - if (SIGISMEMBER(psp->ps_siginfo, sig)) { - /* Signal handler installed with SA_SIGINFO. */ - sf.sf_siginfo = (register_t)&sfp->sf_si; - 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 */ - } else { - /* Old FreeBSD-style arguments. */ - sf.sf_siginfo = ksi->ksi_code; - sf.sf_addr = (register_t)ksi->ksi_addr; - sf.sf_ahu.sf_handler = catcher; - } - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(p); - - /* - * If we're a vm86 process, we want to save the segment registers. - * We also change eflags to be our emulated eflags, not the actual - * eflags. - */ - if (regs->tf_eflags & PSL_VM) { - struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; - struct vm86_kernel *vm86 = &td->td_pcb->pcb_ext->ext_vm86; - - sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; - sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; - sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; - sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; - - if (vm86->vm86_has_vme == 0) - sf.sf_uc.uc_mcontext.mc_eflags = - (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | - (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); - - /* - * Clear PSL_NT to inhibit T_TSSFLT faults on return from - * syscalls made by the signal handler. This just avoids - * wasting time for our lazy fixup of such faults. PSL_NT - * does nothing in vm86 mode, but vm86 programs can set it - * almost legitimately in probes for old cpu types. - */ - tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP); - } - - /* - * 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)) { - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - regs->tf_esp = (int)sfp; - regs->tf_eip = p->p_sysent->sv_sigcode_base; - if (regs->tf_eip == 0) - regs->tf_eip = p->p_sysent->sv_psstrings - szsigcode; - regs->tf_eflags &= ~(PSL_T | PSL_D); - regs->tf_cs = _ucodesel; - regs->tf_ds = _udatasel; - regs->tf_es = _udatasel; - regs->tf_fs = _udatasel; - regs->tf_ss = _udatasel; - 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 - */ -#ifdef COMPAT_43 -int -osigreturn(td, uap) - struct thread *td; - struct osigreturn_args /* { - struct osigcontext *sigcntxp; - } */ *uap; -{ - struct osigcontext sc; - struct trapframe *regs; - struct osigcontext *scp; - int eflags, error; - ksiginfo_t ksi; - - regs = td->td_frame; - error = copyin(uap->sigcntxp, &sc, sizeof(sc)); - if (error != 0) - return (error); - scp = ≻ - eflags = scp->sc_ps; - if (eflags & PSL_VM) { - struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; - struct vm86_kernel *vm86; - - /* - * if pcb_ext == 0 or vm86_inited == 0, the user hasn't - * set up the vm86 area, and we can't enter vm86 mode. - */ - if (td->td_pcb->pcb_ext == 0) - return (EINVAL); - vm86 = &td->td_pcb->pcb_ext->ext_vm86; - if (vm86->vm86_inited == 0) - return (EINVAL); - - /* Go back to user mode if both flags are set. */ - if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) { - ksiginfo_init_trap(&ksi); - ksi.ksi_signo = SIGBUS; - ksi.ksi_code = BUS_OBJERR; - ksi.ksi_addr = (void *)regs->tf_eip; - trapsignal(td, &ksi); - } - - if (vm86->vm86_has_vme) { - eflags = (tf->tf_eflags & ~VME_USERCHANGE) | - (eflags & VME_USERCHANGE) | PSL_VM; - } else { - vm86->vm86_eflags = eflags; /* save VIF, VIP */ - eflags = (tf->tf_eflags & ~VM_USERCHANGE) | - (eflags & VM_USERCHANGE) | PSL_VM; - } - tf->tf_vm86_ds = scp->sc_ds; - tf->tf_vm86_es = scp->sc_es; - tf->tf_vm86_fs = scp->sc_fs; - tf->tf_vm86_gs = scp->sc_gs; - tf->tf_ds = _udatasel; - tf->tf_es = _udatasel; - tf->tf_fs = _udatasel; - } else { - /* - * Don't allow users to change privileged or reserved flags. - */ - if (!EFL_SECURE(eflags, regs->tf_eflags)) { - 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. - */ - if (!CS_SECURE(scp->sc_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_eip; - trapsignal(td, &ksi); - return (EINVAL); - } - regs->tf_ds = scp->sc_ds; - regs->tf_es = scp->sc_es; - regs->tf_fs = scp->sc_fs; - } - - /* Restore remaining registers. */ - regs->tf_eax = scp->sc_eax; - regs->tf_ebx = scp->sc_ebx; - regs->tf_ecx = scp->sc_ecx; - regs->tf_edx = scp->sc_edx; - regs->tf_esi = scp->sc_esi; - regs->tf_edi = scp->sc_edi; - regs->tf_cs = scp->sc_cs; - regs->tf_ss = scp->sc_ss; - regs->tf_isp = scp->sc_isp; - regs->tf_ebp = scp->sc_fp; - regs->tf_esp = scp->sc_sp; - regs->tf_eip = scp->sc_pc; - regs->tf_eflags = eflags; - -#if defined(COMPAT_43) - if (scp->sc_onstack & 1) - td->td_sigstk.ss_flags |= SS_ONSTACK; - else - td->td_sigstk.ss_flags &= ~SS_ONSTACK; -#endif - kern_sigprocmask(td, SIG_SETMASK, (sigset_t *)&scp->sc_mask, NULL, - SIGPROCMASK_OLD); - return (EJUSTRETURN); -} -#endif /* COMPAT_43 */ - -#ifdef COMPAT_FREEBSD4 -/* - * MPSAFE - */ -int -freebsd4_sigreturn(td, uap) - struct thread *td; - struct freebsd4_sigreturn_args /* { - const ucontext4 *sigcntxp; - } */ *uap; -{ - struct ucontext4 uc; - struct trapframe *regs; - struct ucontext4 *ucp; - int cs, eflags, error; - ksiginfo_t ksi; - - error = copyin(uap->sigcntxp, &uc, sizeof(uc)); - if (error != 0) - return (error); - ucp = &uc; - regs = td->td_frame; - eflags = ucp->uc_mcontext.mc_eflags; - if (eflags & PSL_VM) { - struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; - struct vm86_kernel *vm86; - - /* - * if pcb_ext == 0 or vm86_inited == 0, the user hasn't - * set up the vm86 area, and we can't enter vm86 mode. - */ - if (td->td_pcb->pcb_ext == 0) - return (EINVAL); - vm86 = &td->td_pcb->pcb_ext->ext_vm86; - if (vm86->vm86_inited == 0) - return (EINVAL); - - /* Go back to user mode if both flags are set. */ - if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) { - ksiginfo_init_trap(&ksi); - ksi.ksi_signo = SIGBUS; - ksi.ksi_code = BUS_OBJERR; - ksi.ksi_addr = (void *)regs->tf_eip; - trapsignal(td, &ksi); - } - if (vm86->vm86_has_vme) { - eflags = (tf->tf_eflags & ~VME_USERCHANGE) | - (eflags & VME_USERCHANGE) | PSL_VM; - } else { - vm86->vm86_eflags = eflags; /* save VIF, VIP */ - eflags = (tf->tf_eflags & ~VM_USERCHANGE) | - (eflags & VM_USERCHANGE) | PSL_VM; - } - bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); - tf->tf_eflags = eflags; - tf->tf_vm86_ds = tf->tf_ds; - tf->tf_vm86_es = tf->tf_es; - tf->tf_vm86_fs = tf->tf_fs; - tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; - tf->tf_ds = _udatasel; - tf->tf_es = _udatasel; - tf->tf_fs = _udatasel; - } else { - /* - * Don't allow users to change privileged or reserved flags. - */ - if (!EFL_SECURE(eflags, regs->tf_eflags)) { - uprintf("pid %d (%s): freebsd4_sigreturn eflags = 0x%x\n", - td->td_proc->p_pid, td->td_name, eflags); - 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): freebsd4_sigreturn cs = 0x%x\n", - td->td_proc->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_eip; - trapsignal(td, &ksi); - return (EINVAL); - } - - bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); - } - -#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); -} -#endif /* COMPAT_FREEBSD4 */ - -/* - * MPSAFE - */ -int -sys_sigreturn(td, uap) - struct thread *td; - struct sigreturn_args /* { - const struct __ucontext *sigcntxp; - } */ *uap; -{ - ucontext_t uc; - struct proc *p; - struct trapframe *regs; - ucontext_t *ucp; - char *xfpustate; - size_t xfpustate_len; - int cs, eflags, error, ret; - ksiginfo_t ksi; - - p = td->td_proc; - - error = copyin(uap->sigcntxp, &uc, sizeof(uc)); - if (error != 0) - 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; - eflags = ucp->uc_mcontext.mc_eflags; - if (eflags & PSL_VM) { - struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; - struct vm86_kernel *vm86; - - /* - * if pcb_ext == 0 or vm86_inited == 0, the user hasn't - * set up the vm86 area, and we can't enter vm86 mode. - */ - if (td->td_pcb->pcb_ext == 0) - return (EINVAL); - vm86 = &td->td_pcb->pcb_ext->ext_vm86; - if (vm86->vm86_inited == 0) - return (EINVAL); - - /* Go back to user mode if both flags are set. */ - if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) { - ksiginfo_init_trap(&ksi); - ksi.ksi_signo = SIGBUS; - ksi.ksi_code = BUS_OBJERR; - ksi.ksi_addr = (void *)regs->tf_eip; - trapsignal(td, &ksi); - } - - if (vm86->vm86_has_vme) { - eflags = (tf->tf_eflags & ~VME_USERCHANGE) | - (eflags & VME_USERCHANGE) | PSL_VM; - } else { - vm86->vm86_eflags = eflags; /* save VIF, VIP */ - eflags = (tf->tf_eflags & ~VM_USERCHANGE) | - (eflags & VM_USERCHANGE) | PSL_VM; - } - bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); - tf->tf_eflags = eflags; - tf->tf_vm86_ds = tf->tf_ds; - tf->tf_vm86_es = tf->tf_es; - tf->tf_vm86_fs = tf->tf_fs; - tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; - tf->tf_ds = _udatasel; - tf->tf_es = _udatasel; - tf->tf_fs = _udatasel; - } else { - /* - * Don't allow users to change privileged or reserved flags. - */ - if (!EFL_SECURE(eflags, regs->tf_eflags)) { - uprintf("pid %d (%s): sigreturn eflags = 0x%x\n", - td->td_proc->p_pid, td->td_name, eflags); - 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", - td->td_proc->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_eip; - 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(union 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) - return (ret); - bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); - } - -#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_43 -static void -setup_priv_lcall_gate(struct proc *p) -{ - struct i386_ldt_args uap; - union descriptor desc; - u_int lcall_addr; - - bzero(&uap, sizeof(uap)); - uap.start = 0; - uap.num = 1; - lcall_addr = p->p_sysent->sv_psstrings - sz_lcall_tramp; - bzero(&desc, sizeof(desc)); - desc.sd.sd_type = SDT_MEMERA; - desc.sd.sd_dpl = SEL_UPL; - desc.sd.sd_p = 1; - desc.sd.sd_def32 = 1; - desc.sd.sd_gran = 1; - desc.sd.sd_lolimit = 0xffff; - desc.sd.sd_hilimit = 0xf; - desc.sd.sd_lobase = lcall_addr; - desc.sd.sd_hibase = lcall_addr >> 24; - i386_set_ldt(curthread, &uap, &desc); -} -#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(); - } - pcb->pcb_flags &= ~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_eflags; - - regs = td->td_frame; - pcb = td->td_pcb; - - /* Reset pc->pcb_gs and %gs before possibly invalidating it. */ - pcb->pcb_gs = _udatasel; - load_gs(_udatasel); - - mtx_lock_spin(&dt_lock); - if (td->td_proc->p_md.md_ldt != NULL) - user_ldt_free(td); - else - mtx_unlock_spin(&dt_lock); - -#ifdef COMPAT_43 - if (td->td_proc->p_sysent->sv_psstrings != - elf32_freebsd_sysvec.sv_psstrings) - setup_priv_lcall_gate(td->td_proc); -#endif - - /* - * Reset the fs and gs bases. The values from the old address - * space do not make sense for the new program. In particular, - * gsbase might be the TLS base for the old program but the new - * program has no TLS now. - */ - set_fsbase(td, 0); - set_gsbase(td, 0); - - /* Make sure edx is 0x0 on entry. Linux binaries depend on it. */ - saved_eflags = regs->tf_eflags & PSL_T; - bzero((char *)regs, sizeof(struct trapframe)); - regs->tf_eip = imgp->entry_addr; - regs->tf_esp = stack; - regs->tf_eflags = PSL_USER | saved_eflags; - regs->tf_ss = _udatasel; - regs->tf_ds = _udatasel; - regs->tf_es = _udatasel; - regs->tf_fs = _udatasel; - regs->tf_cs = _ucodesel; - - /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ - regs->tf_ebx = (register_t)imgp->ps_strings; - - x86_clear_dbregs(pcb); - - pcb->pcb_initial_npxcw = __INITIAL_NPXCW__; - - /* - * 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) { @@ -2759,479 +1841,6 @@ pcb->pcb_gs = rgs(); } -int -ptrace_set_pc(struct thread *td, u_long addr) -{ - - td->td_frame->tf_eip = addr; - return (0); -} - -int -ptrace_single_step(struct thread *td) -{ - - PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); - if ((td->td_frame->tf_eflags & PSL_T) == 0) { - td->td_frame->tf_eflags |= 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_eflags &= ~PSL_T; - td->td_dbgflags &= ~TDB_STEP; - return (0); -} - -int -fill_regs(struct thread *td, struct reg *regs) -{ - struct pcb *pcb; - struct trapframe *tp; - - tp = td->td_frame; - pcb = td->td_pcb; - regs->r_gs = pcb->pcb_gs; - return (fill_frame_regs(tp, regs)); -} - -int -fill_frame_regs(struct trapframe *tp, struct reg *regs) -{ - - regs->r_fs = tp->tf_fs; - regs->r_es = tp->tf_es; - regs->r_ds = tp->tf_ds; - regs->r_edi = tp->tf_edi; - regs->r_esi = tp->tf_esi; - regs->r_ebp = tp->tf_ebp; - regs->r_ebx = tp->tf_ebx; - regs->r_edx = tp->tf_edx; - regs->r_ecx = tp->tf_ecx; - regs->r_eax = tp->tf_eax; - regs->r_eip = tp->tf_eip; - regs->r_cs = tp->tf_cs; - regs->r_eflags = tp->tf_eflags; - regs->r_esp = tp->tf_esp; - regs->r_ss = tp->tf_ss; - regs->r_err = 0; - regs->r_trapno = 0; - return (0); -} - -int -set_regs(struct thread *td, struct reg *regs) -{ - struct pcb *pcb; - struct trapframe *tp; - - tp = td->td_frame; - if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || - !CS_SECURE(regs->r_cs)) - return (EINVAL); - pcb = td->td_pcb; - tp->tf_fs = regs->r_fs; - tp->tf_es = regs->r_es; - tp->tf_ds = regs->r_ds; - tp->tf_edi = regs->r_edi; - tp->tf_esi = regs->r_esi; - tp->tf_ebp = regs->r_ebp; - tp->tf_ebx = regs->r_ebx; - tp->tf_edx = regs->r_edx; - tp->tf_ecx = regs->r_ecx; - tp->tf_eax = regs->r_eax; - tp->tf_eip = regs->r_eip; - tp->tf_cs = regs->r_cs; - tp->tf_eflags = regs->r_eflags; - tp->tf_esp = regs->r_esp; - tp->tf_ss = regs->r_ss; - pcb->pcb_gs = regs->r_gs; - return (0); -} - -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)); - npxgetregs(td); - if (cpu_fxsr) - npx_fill_fpregs_xmm(&get_pcb_user_save_td(td)->sv_xmm, - (struct save87 *)fpregs); - else - bcopy(&get_pcb_user_save_td(td)->sv_87, fpregs, - sizeof(*fpregs)); - return (0); -} - -int -set_fpregs(struct thread *td, struct fpreg *fpregs) -{ - - critical_enter(); - if (cpu_fxsr) - npx_set_fpregs_xmm((struct save87 *)fpregs, - &get_pcb_user_save_td(td)->sv_xmm); - else - bcopy(fpregs, &get_pcb_user_save_td(td)->sv_87, - sizeof(*fpregs)); - npxuserinited(td); - critical_exit(); - return (0); -} - -/* - * Get machine context. - */ -int -get_mcontext(struct thread *td, mcontext_t *mcp, int flags) -{ - struct trapframe *tp; - struct segment_descriptor *sdp; - - tp = td->td_frame; - - PROC_LOCK(curthread->td_proc); - mcp->mc_onstack = sigonstack(tp->tf_esp); - PROC_UNLOCK(curthread->td_proc); - mcp->mc_gs = td->td_pcb->pcb_gs; - mcp->mc_fs = tp->tf_fs; - mcp->mc_es = tp->tf_es; - mcp->mc_ds = tp->tf_ds; - mcp->mc_edi = tp->tf_edi; - mcp->mc_esi = tp->tf_esi; - mcp->mc_ebp = tp->tf_ebp; - mcp->mc_isp = tp->tf_isp; - mcp->mc_eflags = tp->tf_eflags; - if (flags & GET_MC_CLEAR_RET) { - mcp->mc_eax = 0; - mcp->mc_edx = 0; - mcp->mc_eflags &= ~PSL_C; - } else { - mcp->mc_eax = tp->tf_eax; - mcp->mc_edx = tp->tf_edx; - } - mcp->mc_ebx = tp->tf_ebx; - mcp->mc_ecx = tp->tf_ecx; - mcp->mc_eip = tp->tf_eip; - mcp->mc_cs = tp->tf_cs; - mcp->mc_esp = tp->tf_esp; - mcp->mc_ss = tp->tf_ss; - mcp->mc_len = sizeof(*mcp); - get_fpcontext(td, mcp, NULL, 0); - sdp = &td->td_pcb->pcb_fsd; - mcp->mc_fsbase = sdp->sd_hibase << 24 | sdp->sd_lobase; - sdp = &td->td_pcb->pcb_gsd; - mcp->mc_gsbase = sdp->sd_hibase << 24 | sdp->sd_lobase; - mcp->mc_flags = 0; - mcp->mc_xfpustate = 0; - mcp->mc_xfpustate_len = 0; - bzero(mcp->mc_spare2, sizeof(mcp->mc_spare2)); - 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 trapframe *tp; - char *xfpustate; - int eflags, ret; - - tp = td->td_frame; - if (mcp->mc_len != sizeof(*mcp) || - (mcp->mc_flags & ~_MC_FLAG_MASK) != 0) - return (EINVAL); - eflags = (mcp->mc_eflags & PSL_USERCHANGE) | - (tp->tf_eflags & ~PSL_USERCHANGE); - if (mcp->mc_flags & _MC_HASFPXSTATE) { - if (mcp->mc_xfpustate_len > cpu_max_ext_state_size - - sizeof(union 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_fs = mcp->mc_fs; - tp->tf_es = mcp->mc_es; - tp->tf_ds = mcp->mc_ds; - tp->tf_edi = mcp->mc_edi; - tp->tf_esi = mcp->mc_esi; - tp->tf_ebp = mcp->mc_ebp; - tp->tf_ebx = mcp->mc_ebx; - tp->tf_edx = mcp->mc_edx; - tp->tf_ecx = mcp->mc_ecx; - tp->tf_eax = mcp->mc_eax; - tp->tf_eip = mcp->mc_eip; - tp->tf_eflags = eflags; - tp->tf_esp = mcp->mc_esp; - tp->tf_ss = mcp->mc_ss; - td->td_pcb->pcb_gs = mcp->mc_gs; - 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 = npxgetregs(td); - bcopy(get_pcb_user_save_td(td), &mcp->mc_fpstate[0], - sizeof(mcp->mc_fpstate)); - mcp->mc_fpformat = npxformat(); - if (!use_xsave || xfpusave_len == 0) - return; - max_len = cpu_max_ext_state_size - sizeof(union 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_387 && - 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 = npxsetregs(td, (union savefpu *)&mcp->mc_fpstate, - xfpustate, xfpustate_len); - } else - return (EINVAL); - return (error); -} - -static 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) - npxdrop(); - /* - * XXX force a full drop of the npx. The above only drops it if we - * owned it. npxgetregs() has the same bug in the !cpu_fxsr case. - * - * XXX I don't much like npxgetregs()'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 npxgetregs()... perhaps we just - * have too many layers. - */ - curthread->td_pcb->pcb_flags &= ~(PCB_NPXINITDONE | - PCB_NPXUSERINITDONE); - 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; - 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. - */ - for (i = 0; i < 4; i++) { - if (DBREG_DR7_ACCESS(dbregs->dr[7], i) == 0x02) - return (EINVAL); - if (DBREG_DR7_LEN(dbregs->dr[7], i) == 0x02) - 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]; - - pcb->pcb_flags |= PCB_DBREGS; - } - - return (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_int32_t dr7; - u_int32_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; -} - #ifdef KDB /* diff --git a/sys/i386/i386/ptrace_machdep.c b/sys/i386/i386/ptrace_machdep.c --- a/sys/i386/i386/ptrace_machdep.c +++ b/sys/i386/i386/ptrace_machdep.c @@ -34,7 +34,9 @@ #include #include +#include #include +#include #include #include #include @@ -196,3 +198,33 @@ return (error); } + +int +ptrace_set_pc(struct thread *td, u_long addr) +{ + + td->td_frame->tf_eip = addr; + return (0); +} + +int +ptrace_single_step(struct thread *td) +{ + + PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); + if ((td->td_frame->tf_eflags & PSL_T) == 0) { + td->td_frame->tf_eflags |= 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_eflags &= ~PSL_T; + td->td_dbgflags &= ~TDB_STEP; + return (0); +}