diff --git a/sys/arm/arm/exec_machdep.c b/sys/arm/arm/exec_machdep.c new file mode 100644 --- /dev/null +++ b/sys/arm/arm/exec_machdep.c @@ -0,0 +1,379 @@ +/* $NetBSD: arm32_machdep.c,v 1.44 2004/03/24 15:34:47 atatat Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 2004 Olivier Houchard + * Copyright (c) 1994-1998 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Mark Brinicombe + * for the NetBSD Project. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Clear registers on exec + */ +void +exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) +{ + struct trapframe *tf = td->td_frame; + + memset(tf, 0, sizeof(*tf)); + tf->tf_usr_sp = stack; + tf->tf_usr_lr = imgp->entry_addr; + tf->tf_svc_lr = 0x77777777; + tf->tf_pc = imgp->entry_addr; + tf->tf_spsr = PSR_USR32_MODE; + if ((register_t)imgp->entry_addr & 1) + tf->tf_spsr |= PSR_T; +} + +#ifdef VFP +/* + * Get machine VFP context. + */ +void +get_vfpcontext(struct thread *td, mcontext_vfp_t *vfp) +{ + struct pcb *pcb; + + pcb = td->td_pcb; + if (td == curthread) { + critical_enter(); + vfp_store(&pcb->pcb_vfpstate, false); + critical_exit(); + } else + MPASS(TD_IS_SUSPENDED(td)); + memcpy(vfp->mcv_reg, pcb->pcb_vfpstate.reg, + sizeof(vfp->mcv_reg)); + vfp->mcv_fpscr = pcb->pcb_vfpstate.fpscr; +} + +/* + * Set machine VFP context. + */ +void +set_vfpcontext(struct thread *td, mcontext_vfp_t *vfp) +{ + struct pcb *pcb; + + pcb = td->td_pcb; + if (td == curthread) { + critical_enter(); + vfp_discard(td); + critical_exit(); + } else + MPASS(TD_IS_SUSPENDED(td)); + memcpy(pcb->pcb_vfpstate.reg, vfp->mcv_reg, + sizeof(pcb->pcb_vfpstate.reg)); + pcb->pcb_vfpstate.fpscr = vfp->mcv_fpscr; +} +#endif + +int +arm_get_vfpstate(struct thread *td, void *args) +{ + int rv; + struct arm_get_vfpstate_args ua; + mcontext_vfp_t mcontext_vfp; + + rv = copyin(args, &ua, sizeof(ua)); + if (rv != 0) + return (rv); + if (ua.mc_vfp_size != sizeof(mcontext_vfp_t)) + return (EINVAL); +#ifdef VFP + get_vfpcontext(td, &mcontext_vfp); +#else + bzero(&mcontext_vfp, sizeof(mcontext_vfp)); +#endif + + rv = copyout(&mcontext_vfp, ua.mc_vfp, sizeof(mcontext_vfp)); + if (rv != 0) + return (rv); + return (0); +} + +/* + * Get machine context. + */ +int +get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) +{ + struct trapframe *tf = td->td_frame; + __greg_t *gr = mcp->__gregs; + + if (clear_ret & GET_MC_CLEAR_RET) { + gr[_REG_R0] = 0; + gr[_REG_CPSR] = tf->tf_spsr & ~PSR_C; + } else { + gr[_REG_R0] = tf->tf_r0; + gr[_REG_CPSR] = tf->tf_spsr; + } + gr[_REG_R1] = tf->tf_r1; + gr[_REG_R2] = tf->tf_r2; + gr[_REG_R3] = tf->tf_r3; + gr[_REG_R4] = tf->tf_r4; + gr[_REG_R5] = tf->tf_r5; + gr[_REG_R6] = tf->tf_r6; + gr[_REG_R7] = tf->tf_r7; + gr[_REG_R8] = tf->tf_r8; + gr[_REG_R9] = tf->tf_r9; + gr[_REG_R10] = tf->tf_r10; + gr[_REG_R11] = tf->tf_r11; + gr[_REG_R12] = tf->tf_r12; + gr[_REG_SP] = tf->tf_usr_sp; + gr[_REG_LR] = tf->tf_usr_lr; + gr[_REG_PC] = tf->tf_pc; + + mcp->mc_vfp_size = 0; + mcp->mc_vfp_ptr = NULL; + memset(&mcp->mc_spare, 0, 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) +{ + mcontext_vfp_t mc_vfp, *vfp; + struct trapframe *tf = td->td_frame; + const __greg_t *gr = mcp->__gregs; + int spsr; + + /* + * Make sure the processor mode has not been tampered with and + * interrupts have not been disabled. + */ + spsr = gr[_REG_CPSR]; + if ((spsr & PSR_MODE) != PSR_USR32_MODE || + (spsr & (PSR_I | PSR_F)) != 0) + return (EINVAL); + +#ifdef WITNESS + if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(mc_vfp)) { + printf("%s: %s: Malformed mc_vfp_size: %d (0x%08X)\n", + td->td_proc->p_comm, __func__, + mcp->mc_vfp_size, mcp->mc_vfp_size); + } else if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr == NULL) { + printf("%s: %s: c_vfp_size != 0 but mc_vfp_ptr == NULL\n", + td->td_proc->p_comm, __func__); + } +#endif + + if (mcp->mc_vfp_size == sizeof(mc_vfp) && mcp->mc_vfp_ptr != NULL) { + if (copyin(mcp->mc_vfp_ptr, &mc_vfp, sizeof(mc_vfp)) != 0) + return (EFAULT); + vfp = &mc_vfp; + } else { + vfp = NULL; + } + + tf->tf_r0 = gr[_REG_R0]; + tf->tf_r1 = gr[_REG_R1]; + tf->tf_r2 = gr[_REG_R2]; + tf->tf_r3 = gr[_REG_R3]; + tf->tf_r4 = gr[_REG_R4]; + tf->tf_r5 = gr[_REG_R5]; + tf->tf_r6 = gr[_REG_R6]; + tf->tf_r7 = gr[_REG_R7]; + tf->tf_r8 = gr[_REG_R8]; + tf->tf_r9 = gr[_REG_R9]; + tf->tf_r10 = gr[_REG_R10]; + tf->tf_r11 = gr[_REG_R11]; + tf->tf_r12 = gr[_REG_R12]; + tf->tf_usr_sp = gr[_REG_SP]; + tf->tf_usr_lr = gr[_REG_LR]; + tf->tf_pc = gr[_REG_PC]; + tf->tf_spsr = gr[_REG_CPSR]; +#ifdef VFP + if (vfp != NULL) + set_vfpcontext(td, vfp); +#endif + return (0); +} + +void +sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) +{ + struct thread *td; + struct proc *p; + struct trapframe *tf; + struct sigframe *fp, frame; + struct sigacts *psp; + struct sysentvec *sysent; + int onstack; + int sig; + int code; + + td = curthread; + p = td->td_proc; + PROC_LOCK_ASSERT(p, MA_OWNED); + sig = ksi->ksi_signo; + code = ksi->ksi_code; + psp = p->p_sigacts; + mtx_assert(&psp->ps_mtx, MA_OWNED); + tf = td->td_frame; + onstack = sigonstack(tf->tf_usr_sp); + + CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, + catcher, sig); + + /* Allocate and validate space for the signal handler context. */ + if ((td->td_pflags & TDP_ALTSTACK) != 0 && !(onstack) && + SIGISMEMBER(psp->ps_sigonstack, sig)) { + fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp + + td->td_sigstk.ss_size); +#if defined(COMPAT_43) + td->td_sigstk.ss_flags |= SS_ONSTACK; +#endif + } else + fp = (struct sigframe *)td->td_frame->tf_usr_sp; + + /* make room on the stack */ + fp--; + + /* make the stack aligned */ + fp = (struct sigframe *)STACKALIGN(fp); + /* Populate the siginfo frame. */ + bzero(&frame, sizeof(frame)); + get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); +#ifdef VFP + get_vfpcontext(td, &frame.sf_vfp); + frame.sf_uc.uc_mcontext.mc_vfp_size = sizeof(fp->sf_vfp); + frame.sf_uc.uc_mcontext.mc_vfp_ptr = &fp->sf_vfp; +#else + frame.sf_uc.uc_mcontext.mc_vfp_size = 0; + frame.sf_uc.uc_mcontext.mc_vfp_ptr = NULL; +#endif + frame.sf_si = ksi->ksi_info; + frame.sf_uc.uc_sigmask = *mask; + frame.sf_uc.uc_stack = td->td_sigstk; + frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? + (onstack ? SS_ONSTACK : 0) : SS_DISABLE; + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(td->td_proc); + + /* Copy the sigframe out to the user's stack. */ + if (copyout(&frame, fp, sizeof(*fp)) != 0) { + /* Process has trashed its stack. Kill it. */ + CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + /* + * Build context to run handler in. We invoke the handler + * directly, only returning via the trampoline. Note the + * trampoline version numbers are coordinated with machine- + * dependent code in libc. + */ + + tf->tf_r0 = sig; + tf->tf_r1 = (register_t)&fp->sf_si; + tf->tf_r2 = (register_t)&fp->sf_uc; + + /* the trampoline uses r5 as the uc address */ + tf->tf_r5 = (register_t)&fp->sf_uc; + tf->tf_pc = (register_t)catcher; + tf->tf_usr_sp = (register_t)fp; + sysent = p->p_sysent; + if (sysent->sv_sigcode_base != 0) + tf->tf_usr_lr = (register_t)sysent->sv_sigcode_base; + else + tf->tf_usr_lr = (register_t)(sysent->sv_psstrings - + *(sysent->sv_szsigcode)); + /* Set the mode to enter in the signal handler */ +#if __ARM_ARCH >= 7 + if ((register_t)catcher & 1) + tf->tf_spsr |= PSR_T; + else + tf->tf_spsr &= ~PSR_T; +#endif + + CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_usr_lr, + tf->tf_usr_sp); + + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); +} + +int +sys_sigreturn(struct thread *td, struct sigreturn_args *uap) +{ + ucontext_t uc; + int error; + + if (uap == NULL) + return (EFAULT); + if (copyin(uap->sigcntxp, &uc, sizeof(uc))) + return (EFAULT); + /* Restore register context. */ + error = set_mcontext(td, &uc.uc_mcontext); + if (error != 0) + return (error); + + /* Restore signal mask. */ + kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); + + return (EJUSTRETURN); +} diff --git a/sys/arm/arm/machdep.c b/sys/arm/arm/machdep.c --- a/sys/arm/arm/machdep.c +++ b/sys/arm/arm/machdep.c @@ -332,328 +332,6 @@ } } -/* - * Clear registers on exec - */ -void -exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) -{ - struct trapframe *tf = td->td_frame; - - memset(tf, 0, sizeof(*tf)); - tf->tf_usr_sp = stack; - tf->tf_usr_lr = imgp->entry_addr; - tf->tf_svc_lr = 0x77777777; - tf->tf_pc = imgp->entry_addr; - tf->tf_spsr = PSR_USR32_MODE; - if ((register_t)imgp->entry_addr & 1) - tf->tf_spsr |= PSR_T; -} - -#ifdef VFP -/* - * Get machine VFP context. - */ -void -get_vfpcontext(struct thread *td, mcontext_vfp_t *vfp) -{ - struct pcb *pcb; - - pcb = td->td_pcb; - if (td == curthread) { - critical_enter(); - vfp_store(&pcb->pcb_vfpstate, false); - critical_exit(); - } else - MPASS(TD_IS_SUSPENDED(td)); - memcpy(vfp->mcv_reg, pcb->pcb_vfpstate.reg, - sizeof(vfp->mcv_reg)); - vfp->mcv_fpscr = pcb->pcb_vfpstate.fpscr; -} - -/* - * Set machine VFP context. - */ -void -set_vfpcontext(struct thread *td, mcontext_vfp_t *vfp) -{ - struct pcb *pcb; - - pcb = td->td_pcb; - if (td == curthread) { - critical_enter(); - vfp_discard(td); - critical_exit(); - } else - MPASS(TD_IS_SUSPENDED(td)); - memcpy(pcb->pcb_vfpstate.reg, vfp->mcv_reg, - sizeof(pcb->pcb_vfpstate.reg)); - pcb->pcb_vfpstate.fpscr = vfp->mcv_fpscr; -} -#endif - -int -arm_get_vfpstate(struct thread *td, void *args) -{ - int rv; - struct arm_get_vfpstate_args ua; - mcontext_vfp_t mcontext_vfp; - - rv = copyin(args, &ua, sizeof(ua)); - if (rv != 0) - return (rv); - if (ua.mc_vfp_size != sizeof(mcontext_vfp_t)) - return (EINVAL); -#ifdef VFP - get_vfpcontext(td, &mcontext_vfp); -#else - bzero(&mcontext_vfp, sizeof(mcontext_vfp)); -#endif - - rv = copyout(&mcontext_vfp, ua.mc_vfp, sizeof(mcontext_vfp)); - if (rv != 0) - return (rv); - return (0); -} - -/* - * Get machine context. - */ -int -get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) -{ - struct trapframe *tf = td->td_frame; - __greg_t *gr = mcp->__gregs; - - if (clear_ret & GET_MC_CLEAR_RET) { - gr[_REG_R0] = 0; - gr[_REG_CPSR] = tf->tf_spsr & ~PSR_C; - } else { - gr[_REG_R0] = tf->tf_r0; - gr[_REG_CPSR] = tf->tf_spsr; - } - gr[_REG_R1] = tf->tf_r1; - gr[_REG_R2] = tf->tf_r2; - gr[_REG_R3] = tf->tf_r3; - gr[_REG_R4] = tf->tf_r4; - gr[_REG_R5] = tf->tf_r5; - gr[_REG_R6] = tf->tf_r6; - gr[_REG_R7] = tf->tf_r7; - gr[_REG_R8] = tf->tf_r8; - gr[_REG_R9] = tf->tf_r9; - gr[_REG_R10] = tf->tf_r10; - gr[_REG_R11] = tf->tf_r11; - gr[_REG_R12] = tf->tf_r12; - gr[_REG_SP] = tf->tf_usr_sp; - gr[_REG_LR] = tf->tf_usr_lr; - gr[_REG_PC] = tf->tf_pc; - - mcp->mc_vfp_size = 0; - mcp->mc_vfp_ptr = NULL; - memset(&mcp->mc_spare, 0, 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) -{ - mcontext_vfp_t mc_vfp, *vfp; - struct trapframe *tf = td->td_frame; - const __greg_t *gr = mcp->__gregs; - int spsr; - - /* - * Make sure the processor mode has not been tampered with and - * interrupts have not been disabled. - */ - spsr = gr[_REG_CPSR]; - if ((spsr & PSR_MODE) != PSR_USR32_MODE || - (spsr & (PSR_I | PSR_F)) != 0) - return (EINVAL); - -#ifdef WITNESS - if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(mc_vfp)) { - printf("%s: %s: Malformed mc_vfp_size: %d (0x%08X)\n", - td->td_proc->p_comm, __func__, - mcp->mc_vfp_size, mcp->mc_vfp_size); - } else if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr == NULL) { - printf("%s: %s: c_vfp_size != 0 but mc_vfp_ptr == NULL\n", - td->td_proc->p_comm, __func__); - } -#endif - - if (mcp->mc_vfp_size == sizeof(mc_vfp) && mcp->mc_vfp_ptr != NULL) { - if (copyin(mcp->mc_vfp_ptr, &mc_vfp, sizeof(mc_vfp)) != 0) - return (EFAULT); - vfp = &mc_vfp; - } else { - vfp = NULL; - } - - tf->tf_r0 = gr[_REG_R0]; - tf->tf_r1 = gr[_REG_R1]; - tf->tf_r2 = gr[_REG_R2]; - tf->tf_r3 = gr[_REG_R3]; - tf->tf_r4 = gr[_REG_R4]; - tf->tf_r5 = gr[_REG_R5]; - tf->tf_r6 = gr[_REG_R6]; - tf->tf_r7 = gr[_REG_R7]; - tf->tf_r8 = gr[_REG_R8]; - tf->tf_r9 = gr[_REG_R9]; - tf->tf_r10 = gr[_REG_R10]; - tf->tf_r11 = gr[_REG_R11]; - tf->tf_r12 = gr[_REG_R12]; - tf->tf_usr_sp = gr[_REG_SP]; - tf->tf_usr_lr = gr[_REG_LR]; - tf->tf_pc = gr[_REG_PC]; - tf->tf_spsr = gr[_REG_CPSR]; -#ifdef VFP - if (vfp != NULL) - set_vfpcontext(td, vfp); -#endif - return (0); -} - -void -sendsig(catcher, ksi, mask) - sig_t catcher; - ksiginfo_t *ksi; - sigset_t *mask; -{ - struct thread *td; - struct proc *p; - struct trapframe *tf; - struct sigframe *fp, frame; - struct sigacts *psp; - struct sysentvec *sysent; - int onstack; - int sig; - int code; - - td = curthread; - p = td->td_proc; - PROC_LOCK_ASSERT(p, MA_OWNED); - sig = ksi->ksi_signo; - code = ksi->ksi_code; - psp = p->p_sigacts; - mtx_assert(&psp->ps_mtx, MA_OWNED); - tf = td->td_frame; - onstack = sigonstack(tf->tf_usr_sp); - - CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, - catcher, sig); - - /* Allocate and validate space for the signal handler context. */ - if ((td->td_pflags & TDP_ALTSTACK) != 0 && !(onstack) && - SIGISMEMBER(psp->ps_sigonstack, sig)) { - fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp + - td->td_sigstk.ss_size); -#if defined(COMPAT_43) - td->td_sigstk.ss_flags |= SS_ONSTACK; -#endif - } else - fp = (struct sigframe *)td->td_frame->tf_usr_sp; - - /* make room on the stack */ - fp--; - - /* make the stack aligned */ - fp = (struct sigframe *)STACKALIGN(fp); - /* Populate the siginfo frame. */ - bzero(&frame, sizeof(frame)); - get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); -#ifdef VFP - get_vfpcontext(td, &frame.sf_vfp); - frame.sf_uc.uc_mcontext.mc_vfp_size = sizeof(fp->sf_vfp); - frame.sf_uc.uc_mcontext.mc_vfp_ptr = &fp->sf_vfp; -#else - frame.sf_uc.uc_mcontext.mc_vfp_size = 0; - frame.sf_uc.uc_mcontext.mc_vfp_ptr = NULL; -#endif - frame.sf_si = ksi->ksi_info; - frame.sf_uc.uc_sigmask = *mask; - frame.sf_uc.uc_stack = td->td_sigstk; - frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? - (onstack ? SS_ONSTACK : 0) : SS_DISABLE; - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(td->td_proc); - - /* Copy the sigframe out to the user's stack. */ - if (copyout(&frame, fp, sizeof(*fp)) != 0) { - /* Process has trashed its stack. Kill it. */ - CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - /* - * Build context to run handler in. We invoke the handler - * directly, only returning via the trampoline. Note the - * trampoline version numbers are coordinated with machine- - * dependent code in libc. - */ - - tf->tf_r0 = sig; - tf->tf_r1 = (register_t)&fp->sf_si; - tf->tf_r2 = (register_t)&fp->sf_uc; - - /* the trampoline uses r5 as the uc address */ - tf->tf_r5 = (register_t)&fp->sf_uc; - tf->tf_pc = (register_t)catcher; - tf->tf_usr_sp = (register_t)fp; - sysent = p->p_sysent; - if (sysent->sv_sigcode_base != 0) - tf->tf_usr_lr = (register_t)sysent->sv_sigcode_base; - else - tf->tf_usr_lr = (register_t)(sysent->sv_psstrings - - *(sysent->sv_szsigcode)); - /* Set the mode to enter in the signal handler */ -#if __ARM_ARCH >= 7 - if ((register_t)catcher & 1) - tf->tf_spsr |= PSR_T; - else - tf->tf_spsr &= ~PSR_T; -#endif - - CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_usr_lr, - tf->tf_usr_sp); - - PROC_LOCK(p); - mtx_lock(&psp->ps_mtx); -} - -int -sys_sigreturn(td, uap) - struct thread *td; - struct sigreturn_args /* { - const struct __ucontext *sigcntxp; - } */ *uap; -{ - ucontext_t uc; - int error; - - if (uap == NULL) - return (EFAULT); - if (copyin(uap->sigcntxp, &uc, sizeof(uc))) - return (EFAULT); - /* Restore register context. */ - error = set_mcontext(td, &uc.uc_mcontext); - if (error != 0) - return (error); - - /* Restore signal mask. */ - kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); - - return (EJUSTRETURN); -} - /* * Construct a PCB from a trapframe. This is called from kdb_trap() where * we want to start a backtrace from the function that caused us to enter diff --git a/sys/arm64/arm64/exec_machdep.c b/sys/arm64/arm64/exec_machdep.c new file mode 100644 --- /dev/null +++ b/sys/arm64/arm64/exec_machdep.c @@ -0,0 +1,617 @@ +/*- + * Copyright (c) 2014 Andrew Turner + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#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 VFP +#include +#endif + +static void get_fpcontext(struct thread *td, mcontext_t *mcp); +static void set_fpcontext(struct thread *td, mcontext_t *mcp); + +int +fill_regs(struct thread *td, struct reg *regs) +{ + struct trapframe *frame; + + frame = td->td_frame; + regs->sp = frame->tf_sp; + regs->lr = frame->tf_lr; + regs->elr = frame->tf_elr; + regs->spsr = frame->tf_spsr; + + memcpy(regs->x, frame->tf_x, sizeof(regs->x)); + +#ifdef COMPAT_FREEBSD32 + /* + * We may be called here for a 32bits process, if we're using a + * 64bits debugger. If so, put PC and SPSR where it expects it. + */ + if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { + regs->x[15] = frame->tf_elr; + regs->x[16] = frame->tf_spsr; + } +#endif + return (0); +} + +int +set_regs(struct thread *td, struct reg *regs) +{ + struct trapframe *frame; + + frame = td->td_frame; + frame->tf_sp = regs->sp; + frame->tf_lr = regs->lr; + frame->tf_spsr &= ~PSR_FLAGS; + + memcpy(frame->tf_x, regs->x, sizeof(frame->tf_x)); + +#ifdef COMPAT_FREEBSD32 + if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { + /* + * We may be called for a 32bits process if we're using + * a 64bits debugger. If so, get PC and SPSR from where + * it put it. + */ + frame->tf_elr = regs->x[15]; + frame->tf_spsr |= regs->x[16] & PSR_FLAGS; + } else +#endif + { + frame->tf_elr = regs->elr; + frame->tf_spsr |= regs->spsr & PSR_FLAGS; + } + return (0); +} + +int +fill_fpregs(struct thread *td, struct fpreg *regs) +{ +#ifdef VFP + struct pcb *pcb; + + pcb = td->td_pcb; + if ((pcb->pcb_fpflags & PCB_FP_STARTED) != 0) { + /* + * If we have just been running VFP instructions we will + * need to save the state to memcpy it below. + */ + if (td == curthread) + vfp_save_state(td, pcb); + + KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate, + ("Called fill_fpregs while the kernel is using the VFP")); + memcpy(regs->fp_q, pcb->pcb_fpustate.vfp_regs, + sizeof(regs->fp_q)); + regs->fp_cr = pcb->pcb_fpustate.vfp_fpcr; + regs->fp_sr = pcb->pcb_fpustate.vfp_fpsr; + } else +#endif + memset(regs, 0, sizeof(*regs)); + return (0); +} + +int +set_fpregs(struct thread *td, struct fpreg *regs) +{ +#ifdef VFP + struct pcb *pcb; + + pcb = td->td_pcb; + KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate, + ("Called set_fpregs while the kernel is using the VFP")); + memcpy(pcb->pcb_fpustate.vfp_regs, regs->fp_q, sizeof(regs->fp_q)); + pcb->pcb_fpustate.vfp_fpcr = regs->fp_cr; + pcb->pcb_fpustate.vfp_fpsr = regs->fp_sr; +#endif + return (0); +} + +int +fill_dbregs(struct thread *td, struct dbreg *regs) +{ + struct debug_monitor_state *monitor; + int i; + uint8_t debug_ver, nbkpts, nwtpts; + + memset(regs, 0, sizeof(*regs)); + + extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_DebugVer_SHIFT, + &debug_ver); + extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_BRPs_SHIFT, + &nbkpts); + extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_WRPs_SHIFT, + &nwtpts); + + /* + * The BRPs field contains the number of breakpoints - 1. Armv8-A + * allows the hardware to provide 2-16 breakpoints so this won't + * overflow an 8 bit value. The same applies to the WRPs field. + */ + nbkpts++; + nwtpts++; + + regs->db_debug_ver = debug_ver; + regs->db_nbkpts = nbkpts; + regs->db_nwtpts = nwtpts; + + monitor = &td->td_pcb->pcb_dbg_regs; + if ((monitor->dbg_flags & DBGMON_ENABLED) != 0) { + for (i = 0; i < nbkpts; i++) { + regs->db_breakregs[i].dbr_addr = monitor->dbg_bvr[i]; + regs->db_breakregs[i].dbr_ctrl = monitor->dbg_bcr[i]; + } + for (i = 0; i < nwtpts; i++) { + regs->db_watchregs[i].dbw_addr = monitor->dbg_wvr[i]; + regs->db_watchregs[i].dbw_ctrl = monitor->dbg_wcr[i]; + } + } + + return (0); +} + +int +set_dbregs(struct thread *td, struct dbreg *regs) +{ + struct debug_monitor_state *monitor; + uint64_t addr; + uint32_t ctrl; + int count; + int i; + + monitor = &td->td_pcb->pcb_dbg_regs; + count = 0; + monitor->dbg_enable_count = 0; + + for (i = 0; i < DBG_BRP_MAX; i++) { + addr = regs->db_breakregs[i].dbr_addr; + ctrl = regs->db_breakregs[i].dbr_ctrl; + + /* + * Don't let the user set a breakpoint on a kernel or + * non-canonical user address. + */ + if (addr >= VM_MAXUSER_ADDRESS) + return (EINVAL); + + /* + * The lowest 2 bits are ignored, so record the effective + * address. + */ + addr = rounddown2(addr, 4); + + /* + * Some control fields are ignored, and other bits reserved. + * Only unlinked, address-matching breakpoints are supported. + * + * XXX: fields that appear unvalidated, such as BAS, have + * constrained undefined behaviour. If the user mis-programs + * these, there is no risk to the system. + */ + ctrl &= DBG_BCR_EN | DBG_BCR_PMC | DBG_BCR_BAS; + if ((ctrl & DBG_BCR_EN) != 0) { + /* Only target EL0. */ + if ((ctrl & DBG_BCR_PMC) != DBG_BCR_PMC_EL0) + return (EINVAL); + + monitor->dbg_enable_count++; + } + + monitor->dbg_bvr[i] = addr; + monitor->dbg_bcr[i] = ctrl; + } + + for (i = 0; i < DBG_WRP_MAX; i++) { + addr = regs->db_watchregs[i].dbw_addr; + ctrl = regs->db_watchregs[i].dbw_ctrl; + + /* + * Don't let the user set a watchpoint on a kernel or + * non-canonical user address. + */ + if (addr >= VM_MAXUSER_ADDRESS) + return (EINVAL); + + /* + * Some control fields are ignored, and other bits reserved. + * Only unlinked watchpoints are supported. + */ + ctrl &= DBG_WCR_EN | DBG_WCR_PAC | DBG_WCR_LSC | DBG_WCR_BAS | + DBG_WCR_MASK; + + if ((ctrl & DBG_WCR_EN) != 0) { + /* Only target EL0. */ + if ((ctrl & DBG_WCR_PAC) != DBG_WCR_PAC_EL0) + return (EINVAL); + + /* Must set at least one of the load/store bits. */ + if ((ctrl & DBG_WCR_LSC) == 0) + return (EINVAL); + + /* + * When specifying the address range with BAS, the MASK + * field must be zero. + */ + if ((ctrl & DBG_WCR_BAS) != DBG_WCR_BAS_MASK && + (ctrl & DBG_WCR_MASK) != 0) + return (EINVAL); + + monitor->dbg_enable_count++; + } + monitor->dbg_wvr[i] = addr; + monitor->dbg_wcr[i] = ctrl; + } + + if (monitor->dbg_enable_count > 0) + monitor->dbg_flags |= DBGMON_ENABLED; + + return (0); +} + +#ifdef COMPAT_FREEBSD32 +int +fill_regs32(struct thread *td, struct reg32 *regs) +{ + int i; + struct trapframe *tf; + + tf = td->td_frame; + for (i = 0; i < 13; i++) + regs->r[i] = tf->tf_x[i]; + /* For arm32, SP is r13 and LR is r14 */ + regs->r_sp = tf->tf_x[13]; + regs->r_lr = tf->tf_x[14]; + regs->r_pc = tf->tf_elr; + regs->r_cpsr = tf->tf_spsr; + + return (0); +} + +int +set_regs32(struct thread *td, struct reg32 *regs) +{ + int i; + struct trapframe *tf; + + tf = td->td_frame; + for (i = 0; i < 13; i++) + tf->tf_x[i] = regs->r[i]; + /* For arm 32, SP is r13 an LR is r14 */ + tf->tf_x[13] = regs->r_sp; + tf->tf_x[14] = regs->r_lr; + tf->tf_elr = regs->r_pc; + tf->tf_spsr &= ~PSR_FLAGS; + tf->tf_spsr |= regs->r_cpsr & PSR_FLAGS; + + return (0); +} + +/* XXX fill/set dbregs/fpregs are stubbed on 32-bit arm. */ +int +fill_fpregs32(struct thread *td, struct fpreg32 *regs) +{ + + memset(regs, 0, sizeof(*regs)); + return (0); +} + +int +set_fpregs32(struct thread *td, struct fpreg32 *regs) +{ + + return (0); +} + +int +fill_dbregs32(struct thread *td, struct dbreg32 *regs) +{ + + memset(regs, 0, sizeof(*regs)); + return (0); +} + +int +set_dbregs32(struct thread *td, struct dbreg32 *regs) +{ + + return (0); +} +#endif + +void +exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) +{ + struct trapframe *tf = td->td_frame; + struct pcb *pcb = td->td_pcb; + + memset(tf, 0, sizeof(struct trapframe)); + + tf->tf_x[0] = stack; + tf->tf_sp = STACKALIGN(stack); + tf->tf_lr = imgp->entry_addr; + tf->tf_elr = imgp->entry_addr; + + td->td_pcb->pcb_tpidr_el0 = 0; + td->td_pcb->pcb_tpidrro_el0 = 0; + WRITE_SPECIALREG(tpidrro_el0, 0); + WRITE_SPECIALREG(tpidr_el0, 0); + +#ifdef VFP + vfp_reset_state(td, pcb); +#endif + + /* + * Clear debug register state. It is not applicable to the new process. + */ + bzero(&pcb->pcb_dbg_regs, sizeof(pcb->pcb_dbg_regs)); +} + +/* Sanity check these are the same size, they will be memcpy'd to and from */ +CTASSERT(sizeof(((struct trapframe *)0)->tf_x) == + sizeof((struct gpregs *)0)->gp_x); +CTASSERT(sizeof(((struct trapframe *)0)->tf_x) == + sizeof((struct reg *)0)->x); + +int +get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) +{ + struct trapframe *tf = td->td_frame; + + if (clear_ret & GET_MC_CLEAR_RET) { + mcp->mc_gpregs.gp_x[0] = 0; + mcp->mc_gpregs.gp_spsr = tf->tf_spsr & ~PSR_C; + } else { + mcp->mc_gpregs.gp_x[0] = tf->tf_x[0]; + mcp->mc_gpregs.gp_spsr = tf->tf_spsr; + } + + memcpy(&mcp->mc_gpregs.gp_x[1], &tf->tf_x[1], + sizeof(mcp->mc_gpregs.gp_x[1]) * (nitems(mcp->mc_gpregs.gp_x) - 1)); + + mcp->mc_gpregs.gp_sp = tf->tf_sp; + mcp->mc_gpregs.gp_lr = tf->tf_lr; + mcp->mc_gpregs.gp_elr = tf->tf_elr; + get_fpcontext(td, mcp); + + return (0); +} + +int +set_mcontext(struct thread *td, mcontext_t *mcp) +{ + struct trapframe *tf = td->td_frame; + uint32_t spsr; + + spsr = mcp->mc_gpregs.gp_spsr; + if ((spsr & PSR_M_MASK) != PSR_M_EL0t || + (spsr & PSR_AARCH32) != 0 || + (spsr & PSR_DAIF) != (td->td_frame->tf_spsr & PSR_DAIF)) + return (EINVAL); + + memcpy(tf->tf_x, mcp->mc_gpregs.gp_x, sizeof(tf->tf_x)); + + tf->tf_sp = mcp->mc_gpregs.gp_sp; + tf->tf_lr = mcp->mc_gpregs.gp_lr; + tf->tf_elr = mcp->mc_gpregs.gp_elr; + tf->tf_spsr = mcp->mc_gpregs.gp_spsr; + set_fpcontext(td, mcp); + + return (0); +} + +static void +get_fpcontext(struct thread *td, mcontext_t *mcp) +{ +#ifdef VFP + struct pcb *curpcb; + + critical_enter(); + + curpcb = curthread->td_pcb; + + if ((curpcb->pcb_fpflags & PCB_FP_STARTED) != 0) { + /* + * If we have just been running VFP instructions we will + * need to save the state to memcpy it below. + */ + vfp_save_state(td, curpcb); + + KASSERT(curpcb->pcb_fpusaved == &curpcb->pcb_fpustate, + ("Called get_fpcontext while the kernel is using the VFP")); + KASSERT((curpcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0, + ("Non-userspace FPU flags set in get_fpcontext")); + memcpy(mcp->mc_fpregs.fp_q, curpcb->pcb_fpustate.vfp_regs, + sizeof(mcp->mc_fpregs.fp_q)); + mcp->mc_fpregs.fp_cr = curpcb->pcb_fpustate.vfp_fpcr; + mcp->mc_fpregs.fp_sr = curpcb->pcb_fpustate.vfp_fpsr; + mcp->mc_fpregs.fp_flags = curpcb->pcb_fpflags; + mcp->mc_flags |= _MC_FP_VALID; + } + + critical_exit(); +#endif +} + +static void +set_fpcontext(struct thread *td, mcontext_t *mcp) +{ +#ifdef VFP + struct pcb *curpcb; + + critical_enter(); + + if ((mcp->mc_flags & _MC_FP_VALID) != 0) { + curpcb = curthread->td_pcb; + + /* + * Discard any vfp state for the current thread, we + * are about to override it. + */ + vfp_discard(td); + + KASSERT(curpcb->pcb_fpusaved == &curpcb->pcb_fpustate, + ("Called set_fpcontext while the kernel is using the VFP")); + memcpy(curpcb->pcb_fpustate.vfp_regs, mcp->mc_fpregs.fp_q, + sizeof(mcp->mc_fpregs.fp_q)); + curpcb->pcb_fpustate.vfp_fpcr = mcp->mc_fpregs.fp_cr; + curpcb->pcb_fpustate.vfp_fpsr = mcp->mc_fpregs.fp_sr; + curpcb->pcb_fpflags = mcp->mc_fpregs.fp_flags & PCB_FP_USERMASK; + } + + critical_exit(); +#endif +} + +int +sys_sigreturn(struct thread *td, struct sigreturn_args *uap) +{ + ucontext_t uc; + int error; + + if (copyin(uap->sigcntxp, &uc, sizeof(uc))) + return (EFAULT); + + error = set_mcontext(td, &uc.uc_mcontext); + if (error != 0) + return (error); + + /* Restore signal mask. */ + kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); + + return (EJUSTRETURN); +} + +void +sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) +{ + struct thread *td; + struct proc *p; + struct trapframe *tf; + struct sigframe *fp, frame; + struct sigacts *psp; + struct sysentvec *sysent; + int onstack, sig; + + 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); + + tf = td->td_frame; + onstack = sigonstack(tf->tf_sp); + + CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, + catcher, sig); + + /* Allocate and validate space for the signal handler context. */ + if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack && + SIGISMEMBER(psp->ps_sigonstack, sig)) { + fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp + + td->td_sigstk.ss_size); +#if defined(COMPAT_43) + td->td_sigstk.ss_flags |= SS_ONSTACK; +#endif + } else { + fp = (struct sigframe *)td->td_frame->tf_sp; + } + + /* Make room, keeping the stack aligned */ + fp--; + fp = (struct sigframe *)STACKALIGN(fp); + + /* Fill in the frame to copy out */ + bzero(&frame, sizeof(frame)); + get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); + frame.sf_si = ksi->ksi_info; + frame.sf_uc.uc_sigmask = *mask; + frame.sf_uc.uc_stack = td->td_sigstk; + frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? + (onstack ? SS_ONSTACK : 0) : SS_DISABLE; + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(td->td_proc); + + /* Copy the sigframe out to the user's stack. */ + if (copyout(&frame, fp, sizeof(*fp)) != 0) { + /* Process has trashed its stack. Kill it. */ + CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + tf->tf_x[0]= sig; + tf->tf_x[1] = (register_t)&fp->sf_si; + tf->tf_x[2] = (register_t)&fp->sf_uc; + + tf->tf_elr = (register_t)catcher; + tf->tf_sp = (register_t)fp; + sysent = p->p_sysent; + if (sysent->sv_sigcode_base != 0) + tf->tf_lr = (register_t)sysent->sv_sigcode_base; + else + tf->tf_lr = (register_t)(sysent->sv_psstrings - + *(sysent->sv_szsigcode)); + + CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_elr, + tf->tf_sp); + + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); +} diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -100,9 +100,6 @@ #include #endif -static void get_fpcontext(struct thread *td, mcontext_t *mcp); -static void set_fpcontext(struct thread *td, mcontext_t *mcp); - enum arm64_bus arm64_bus_method = ARM64_BUS_NONE; struct pcpu __pcpu[MAXCPU]; @@ -220,486 +217,6 @@ return (0); } -int -fill_regs(struct thread *td, struct reg *regs) -{ - struct trapframe *frame; - - frame = td->td_frame; - regs->sp = frame->tf_sp; - regs->lr = frame->tf_lr; - regs->elr = frame->tf_elr; - regs->spsr = frame->tf_spsr; - - memcpy(regs->x, frame->tf_x, sizeof(regs->x)); - -#ifdef COMPAT_FREEBSD32 - /* - * We may be called here for a 32bits process, if we're using a - * 64bits debugger. If so, put PC and SPSR where it expects it. - */ - if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { - regs->x[15] = frame->tf_elr; - regs->x[16] = frame->tf_spsr; - } -#endif - return (0); -} - -int -set_regs(struct thread *td, struct reg *regs) -{ - struct trapframe *frame; - - frame = td->td_frame; - frame->tf_sp = regs->sp; - frame->tf_lr = regs->lr; - frame->tf_spsr &= ~PSR_FLAGS; - - memcpy(frame->tf_x, regs->x, sizeof(frame->tf_x)); - -#ifdef COMPAT_FREEBSD32 - if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { - /* - * We may be called for a 32bits process if we're using - * a 64bits debugger. If so, get PC and SPSR from where - * it put it. - */ - frame->tf_elr = regs->x[15]; - frame->tf_spsr |= regs->x[16] & PSR_FLAGS; - } else -#endif - { - frame->tf_elr = regs->elr; - frame->tf_spsr |= regs->spsr & PSR_FLAGS; - } - return (0); -} - -int -fill_fpregs(struct thread *td, struct fpreg *regs) -{ -#ifdef VFP - struct pcb *pcb; - - pcb = td->td_pcb; - if ((pcb->pcb_fpflags & PCB_FP_STARTED) != 0) { - /* - * If we have just been running VFP instructions we will - * need to save the state to memcpy it below. - */ - if (td == curthread) - vfp_save_state(td, pcb); - - KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate, - ("Called fill_fpregs while the kernel is using the VFP")); - memcpy(regs->fp_q, pcb->pcb_fpustate.vfp_regs, - sizeof(regs->fp_q)); - regs->fp_cr = pcb->pcb_fpustate.vfp_fpcr; - regs->fp_sr = pcb->pcb_fpustate.vfp_fpsr; - } else -#endif - memset(regs, 0, sizeof(*regs)); - return (0); -} - -int -set_fpregs(struct thread *td, struct fpreg *regs) -{ -#ifdef VFP - struct pcb *pcb; - - pcb = td->td_pcb; - KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate, - ("Called set_fpregs while the kernel is using the VFP")); - memcpy(pcb->pcb_fpustate.vfp_regs, regs->fp_q, sizeof(regs->fp_q)); - pcb->pcb_fpustate.vfp_fpcr = regs->fp_cr; - pcb->pcb_fpustate.vfp_fpsr = regs->fp_sr; -#endif - return (0); -} - -int -fill_dbregs(struct thread *td, struct dbreg *regs) -{ - struct debug_monitor_state *monitor; - int i; - uint8_t debug_ver, nbkpts, nwtpts; - - memset(regs, 0, sizeof(*regs)); - - extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_DebugVer_SHIFT, - &debug_ver); - extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_BRPs_SHIFT, - &nbkpts); - extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_WRPs_SHIFT, - &nwtpts); - - /* - * The BRPs field contains the number of breakpoints - 1. Armv8-A - * allows the hardware to provide 2-16 breakpoints so this won't - * overflow an 8 bit value. The same applies to the WRPs field. - */ - nbkpts++; - nwtpts++; - - regs->db_debug_ver = debug_ver; - regs->db_nbkpts = nbkpts; - regs->db_nwtpts = nwtpts; - - monitor = &td->td_pcb->pcb_dbg_regs; - if ((monitor->dbg_flags & DBGMON_ENABLED) != 0) { - for (i = 0; i < nbkpts; i++) { - regs->db_breakregs[i].dbr_addr = monitor->dbg_bvr[i]; - regs->db_breakregs[i].dbr_ctrl = monitor->dbg_bcr[i]; - } - for (i = 0; i < nwtpts; i++) { - regs->db_watchregs[i].dbw_addr = monitor->dbg_wvr[i]; - regs->db_watchregs[i].dbw_ctrl = monitor->dbg_wcr[i]; - } - } - - return (0); -} - -int -set_dbregs(struct thread *td, struct dbreg *regs) -{ - struct debug_monitor_state *monitor; - uint64_t addr; - uint32_t ctrl; - int count; - int i; - - monitor = &td->td_pcb->pcb_dbg_regs; - count = 0; - monitor->dbg_enable_count = 0; - - for (i = 0; i < DBG_BRP_MAX; i++) { - addr = regs->db_breakregs[i].dbr_addr; - ctrl = regs->db_breakregs[i].dbr_ctrl; - - /* - * Don't let the user set a breakpoint on a kernel or - * non-canonical user address. - */ - if (addr >= VM_MAXUSER_ADDRESS) - return (EINVAL); - - /* - * The lowest 2 bits are ignored, so record the effective - * address. - */ - addr = rounddown2(addr, 4); - - /* - * Some control fields are ignored, and other bits reserved. - * Only unlinked, address-matching breakpoints are supported. - * - * XXX: fields that appear unvalidated, such as BAS, have - * constrained undefined behaviour. If the user mis-programs - * these, there is no risk to the system. - */ - ctrl &= DBG_BCR_EN | DBG_BCR_PMC | DBG_BCR_BAS; - if ((ctrl & DBG_BCR_EN) != 0) { - /* Only target EL0. */ - if ((ctrl & DBG_BCR_PMC) != DBG_BCR_PMC_EL0) - return (EINVAL); - - monitor->dbg_enable_count++; - } - - monitor->dbg_bvr[i] = addr; - monitor->dbg_bcr[i] = ctrl; - } - - for (i = 0; i < DBG_WRP_MAX; i++) { - addr = regs->db_watchregs[i].dbw_addr; - ctrl = regs->db_watchregs[i].dbw_ctrl; - - /* - * Don't let the user set a watchpoint on a kernel or - * non-canonical user address. - */ - if (addr >= VM_MAXUSER_ADDRESS) - return (EINVAL); - - /* - * Some control fields are ignored, and other bits reserved. - * Only unlinked watchpoints are supported. - */ - ctrl &= DBG_WCR_EN | DBG_WCR_PAC | DBG_WCR_LSC | DBG_WCR_BAS | - DBG_WCR_MASK; - - if ((ctrl & DBG_WCR_EN) != 0) { - /* Only target EL0. */ - if ((ctrl & DBG_WCR_PAC) != DBG_WCR_PAC_EL0) - return (EINVAL); - - /* Must set at least one of the load/store bits. */ - if ((ctrl & DBG_WCR_LSC) == 0) - return (EINVAL); - - /* - * When specifying the address range with BAS, the MASK - * field must be zero. - */ - if ((ctrl & DBG_WCR_BAS) != DBG_WCR_BAS_MASK && - (ctrl & DBG_WCR_MASK) != 0) - return (EINVAL); - - monitor->dbg_enable_count++; - } - monitor->dbg_wvr[i] = addr; - monitor->dbg_wcr[i] = ctrl; - } - - if (monitor->dbg_enable_count > 0) - monitor->dbg_flags |= DBGMON_ENABLED; - - return (0); -} - -#ifdef COMPAT_FREEBSD32 -int -fill_regs32(struct thread *td, struct reg32 *regs) -{ - int i; - struct trapframe *tf; - - tf = td->td_frame; - for (i = 0; i < 13; i++) - regs->r[i] = tf->tf_x[i]; - /* For arm32, SP is r13 and LR is r14 */ - regs->r_sp = tf->tf_x[13]; - regs->r_lr = tf->tf_x[14]; - regs->r_pc = tf->tf_elr; - regs->r_cpsr = tf->tf_spsr; - - return (0); -} - -int -set_regs32(struct thread *td, struct reg32 *regs) -{ - int i; - struct trapframe *tf; - - tf = td->td_frame; - for (i = 0; i < 13; i++) - tf->tf_x[i] = regs->r[i]; - /* For arm 32, SP is r13 an LR is r14 */ - tf->tf_x[13] = regs->r_sp; - tf->tf_x[14] = regs->r_lr; - tf->tf_elr = regs->r_pc; - tf->tf_spsr &= ~PSR_FLAGS; - tf->tf_spsr |= regs->r_cpsr & PSR_FLAGS; - - return (0); -} - -/* XXX fill/set dbregs/fpregs are stubbed on 32-bit arm. */ -int -fill_fpregs32(struct thread *td, struct fpreg32 *regs) -{ - - memset(regs, 0, sizeof(*regs)); - return (0); -} - -int -set_fpregs32(struct thread *td, struct fpreg32 *regs) -{ - - return (0); -} - -int -fill_dbregs32(struct thread *td, struct dbreg32 *regs) -{ - - memset(regs, 0, sizeof(*regs)); - return (0); -} - -int -set_dbregs32(struct thread *td, struct dbreg32 *regs) -{ - - return (0); -} -#endif - -int -ptrace_set_pc(struct thread *td, u_long addr) -{ - - td->td_frame->tf_elr = addr; - return (0); -} - -int -ptrace_single_step(struct thread *td) -{ - - td->td_frame->tf_spsr |= PSR_SS; - td->td_pcb->pcb_flags |= PCB_SINGLE_STEP; - return (0); -} - -int -ptrace_clear_single_step(struct thread *td) -{ - - td->td_frame->tf_spsr &= ~PSR_SS; - td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP; - return (0); -} - -void -exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) -{ - struct trapframe *tf = td->td_frame; - struct pcb *pcb = td->td_pcb; - - memset(tf, 0, sizeof(struct trapframe)); - - tf->tf_x[0] = stack; - tf->tf_sp = STACKALIGN(stack); - tf->tf_lr = imgp->entry_addr; - tf->tf_elr = imgp->entry_addr; - - td->td_pcb->pcb_tpidr_el0 = 0; - td->td_pcb->pcb_tpidrro_el0 = 0; - WRITE_SPECIALREG(tpidrro_el0, 0); - WRITE_SPECIALREG(tpidr_el0, 0); - -#ifdef VFP - vfp_reset_state(td, pcb); -#endif - - /* - * Clear debug register state. It is not applicable to the new process. - */ - bzero(&pcb->pcb_dbg_regs, sizeof(pcb->pcb_dbg_regs)); -} - -/* Sanity check these are the same size, they will be memcpy'd to and fro */ -CTASSERT(sizeof(((struct trapframe *)0)->tf_x) == - sizeof((struct gpregs *)0)->gp_x); -CTASSERT(sizeof(((struct trapframe *)0)->tf_x) == - sizeof((struct reg *)0)->x); - -int -get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) -{ - struct trapframe *tf = td->td_frame; - - if (clear_ret & GET_MC_CLEAR_RET) { - mcp->mc_gpregs.gp_x[0] = 0; - mcp->mc_gpregs.gp_spsr = tf->tf_spsr & ~PSR_C; - } else { - mcp->mc_gpregs.gp_x[0] = tf->tf_x[0]; - mcp->mc_gpregs.gp_spsr = tf->tf_spsr; - } - - memcpy(&mcp->mc_gpregs.gp_x[1], &tf->tf_x[1], - sizeof(mcp->mc_gpregs.gp_x[1]) * (nitems(mcp->mc_gpregs.gp_x) - 1)); - - mcp->mc_gpregs.gp_sp = tf->tf_sp; - mcp->mc_gpregs.gp_lr = tf->tf_lr; - mcp->mc_gpregs.gp_elr = tf->tf_elr; - get_fpcontext(td, mcp); - - return (0); -} - -int -set_mcontext(struct thread *td, mcontext_t *mcp) -{ - struct trapframe *tf = td->td_frame; - uint32_t spsr; - - spsr = mcp->mc_gpregs.gp_spsr; - if ((spsr & PSR_M_MASK) != PSR_M_EL0t || - (spsr & PSR_AARCH32) != 0 || - (spsr & PSR_DAIF) != (td->td_frame->tf_spsr & PSR_DAIF)) - return (EINVAL); - - memcpy(tf->tf_x, mcp->mc_gpregs.gp_x, sizeof(tf->tf_x)); - - tf->tf_sp = mcp->mc_gpregs.gp_sp; - tf->tf_lr = mcp->mc_gpregs.gp_lr; - tf->tf_elr = mcp->mc_gpregs.gp_elr; - tf->tf_spsr = mcp->mc_gpregs.gp_spsr; - set_fpcontext(td, mcp); - - return (0); -} - -static void -get_fpcontext(struct thread *td, mcontext_t *mcp) -{ -#ifdef VFP - struct pcb *curpcb; - - critical_enter(); - - curpcb = curthread->td_pcb; - - if ((curpcb->pcb_fpflags & PCB_FP_STARTED) != 0) { - /* - * If we have just been running VFP instructions we will - * need to save the state to memcpy it below. - */ - vfp_save_state(td, curpcb); - - KASSERT(curpcb->pcb_fpusaved == &curpcb->pcb_fpustate, - ("Called get_fpcontext while the kernel is using the VFP")); - KASSERT((curpcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0, - ("Non-userspace FPU flags set in get_fpcontext")); - memcpy(mcp->mc_fpregs.fp_q, curpcb->pcb_fpustate.vfp_regs, - sizeof(mcp->mc_fpregs.fp_q)); - mcp->mc_fpregs.fp_cr = curpcb->pcb_fpustate.vfp_fpcr; - mcp->mc_fpregs.fp_sr = curpcb->pcb_fpustate.vfp_fpsr; - mcp->mc_fpregs.fp_flags = curpcb->pcb_fpflags; - mcp->mc_flags |= _MC_FP_VALID; - } - - critical_exit(); -#endif -} - -static void -set_fpcontext(struct thread *td, mcontext_t *mcp) -{ -#ifdef VFP - struct pcb *curpcb; - - critical_enter(); - - if ((mcp->mc_flags & _MC_FP_VALID) != 0) { - curpcb = curthread->td_pcb; - - /* - * Discard any vfp state for the current thread, we - * are about to override it. - */ - vfp_discard(td); - - KASSERT(curpcb->pcb_fpusaved == &curpcb->pcb_fpustate, - ("Called set_fpcontext while the kernel is using the VFP")); - memcpy(curpcb->pcb_fpustate.vfp_regs, mcp->mc_fpregs.fp_q, - sizeof(mcp->mc_fpregs.fp_q)); - curpcb->pcb_fpustate.vfp_fpcr = mcp->mc_fpregs.fp_cr; - curpcb->pcb_fpustate.vfp_fpsr = mcp->mc_fpregs.fp_sr; - curpcb->pcb_fpflags = mcp->mc_fpregs.fp_flags & PCB_FP_USERMASK; - } - - critical_exit(); -#endif -} - void cpu_idle(int busy) { @@ -794,31 +311,6 @@ } } -#ifndef _SYS_SYSPROTO_H_ -struct sigreturn_args { - ucontext_t *ucp; -}; -#endif - -int -sys_sigreturn(struct thread *td, struct sigreturn_args *uap) -{ - ucontext_t uc; - int error; - - if (copyin(uap->sigcntxp, &uc, sizeof(uc))) - return (EFAULT); - - error = set_mcontext(td, &uc.uc_mcontext); - if (error != 0) - return (error); - - /* Restore signal mask. */ - kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); - - return (EJUSTRETURN); -} - /* * Construct a PCB from a trapframe. This is called from kdb_trap() where * we want to start a backtrace from the function that caused us to enter @@ -839,86 +331,6 @@ pcb->pcb_sp = tf->tf_sp; } -void -sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) -{ - struct thread *td; - struct proc *p; - struct trapframe *tf; - struct sigframe *fp, frame; - struct sigacts *psp; - struct sysentvec *sysent; - int onstack, sig; - - 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); - - tf = td->td_frame; - onstack = sigonstack(tf->tf_sp); - - CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, - catcher, sig); - - /* Allocate and validate space for the signal handler context. */ - if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack && - SIGISMEMBER(psp->ps_sigonstack, sig)) { - fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp + - td->td_sigstk.ss_size); -#if defined(COMPAT_43) - td->td_sigstk.ss_flags |= SS_ONSTACK; -#endif - } else { - fp = (struct sigframe *)td->td_frame->tf_sp; - } - - /* Make room, keeping the stack aligned */ - fp--; - fp = (struct sigframe *)STACKALIGN(fp); - - /* Fill in the frame to copy out */ - bzero(&frame, sizeof(frame)); - get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); - frame.sf_si = ksi->ksi_info; - frame.sf_uc.uc_sigmask = *mask; - frame.sf_uc.uc_stack = td->td_sigstk; - frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? - (onstack ? SS_ONSTACK : 0) : SS_DISABLE; - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(td->td_proc); - - /* Copy the sigframe out to the user's stack. */ - if (copyout(&frame, fp, sizeof(*fp)) != 0) { - /* Process has trashed its stack. Kill it. */ - CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - tf->tf_x[0]= sig; - tf->tf_x[1] = (register_t)&fp->sf_si; - tf->tf_x[2] = (register_t)&fp->sf_uc; - - tf->tf_elr = (register_t)catcher; - tf->tf_sp = (register_t)fp; - sysent = p->p_sysent; - if (sysent->sv_sigcode_base != 0) - tf->tf_lr = (register_t)sysent->sv_sigcode_base; - else - tf->tf_lr = (register_t)(sysent->sv_psstrings - - *(sysent->sv_szsigcode)); - - CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_elr, - tf->tf_sp); - - PROC_LOCK(p); - mtx_lock(&psp->ps_mtx); -} - static void init_proc0(vm_offset_t kstack) { diff --git a/sys/arm64/arm64/ptrace_machdep.c b/sys/arm64/arm64/ptrace_machdep.c new file mode 100644 --- /dev/null +++ b/sys/arm64/arm64/ptrace_machdep.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2014 Andrew Turner + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int +ptrace_set_pc(struct thread *td, u_long addr) +{ + + td->td_frame->tf_elr = addr; + return (0); +} + +int +ptrace_single_step(struct thread *td) +{ + + td->td_frame->tf_spsr |= PSR_SS; + td->td_pcb->pcb_flags |= PCB_SINGLE_STEP; + return (0); +} + +int +ptrace_clear_single_step(struct thread *td) +{ + + td->td_frame->tf_spsr &= ~PSR_SS; + td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP; + return (0); +} + diff --git a/sys/conf/files.arm b/sys/conf/files.arm --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -25,6 +25,7 @@ arm/arm/elf_machdep.c standard arm/arm/elf_note.S standard arm/arm/exception.S standard +arm/arm/exec_machdep.c standard arm/arm/fiq.c standard arm/arm/fiq_subr.S standard arm/arm/fusu.S standard diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -47,6 +47,7 @@ arm64/arm64/elf32_machdep.c optional compat_freebsd32 arm64/arm64/elf_machdep.c standard arm64/arm64/exception.S standard +arm64/arm64/exec_machdep.c standard arm64/arm64/freebsd32_machdep.c optional compat_freebsd32 arm64/arm64/gdb_machdep.c optional gdb arm64/arm64/gicv3_its.c optional intrng fdt @@ -66,6 +67,7 @@ arm64/arm64/nexus.c standard arm64/arm64/ofw_machdep.c optional fdt arm64/arm64/pmap.c standard +arm64/arm64/ptrace_machdep.c standard arm64/arm64/stack_machdep.c optional ddb | stack arm64/arm64/support.S standard arm64/arm64/swtch.S standard 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/conf/files.riscv b/sys/conf/files.riscv --- a/sys/conf/files.riscv +++ b/sys/conf/files.riscv @@ -45,6 +45,7 @@ riscv/riscv/dump_machdep.c standard riscv/riscv/elf_machdep.c standard riscv/riscv/exception.S standard +riscv/riscv/exec_machdep.c standard riscv/riscv/intr_machdep.c standard riscv/riscv/in_cksum.c optional inet | inet6 riscv/riscv/identcpu.c standard @@ -57,6 +58,7 @@ riscv/riscv/ofw_machdep.c optional fdt riscv/riscv/plic.c standard riscv/riscv/pmap.c standard +riscv/riscv/ptrace_machdep.c standard riscv/riscv/riscv_console.c optional rcons riscv/riscv/riscv_syscon.c optional ext_resources syscon riscv_syscon fdt riscv/riscv/sbi.c 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); +} diff --git a/sys/riscv/riscv/exec_machdep.c b/sys/riscv/riscv/exec_machdep.c new file mode 100644 --- /dev/null +++ b/sys/riscv/riscv/exec_machdep.c @@ -0,0 +1,420 @@ +/*- + * Copyright (c) 2014 Andrew Turner + * Copyright (c) 2015-2017 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#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 FPE +#include +#endif + +static void get_fpcontext(struct thread *td, mcontext_t *mcp); +static void set_fpcontext(struct thread *td, mcontext_t *mcp); + +int +fill_regs(struct thread *td, struct reg *regs) +{ + struct trapframe *frame; + + frame = td->td_frame; + regs->sepc = frame->tf_sepc; + regs->sstatus = frame->tf_sstatus; + regs->ra = frame->tf_ra; + regs->sp = frame->tf_sp; + regs->gp = frame->tf_gp; + regs->tp = frame->tf_tp; + + memcpy(regs->t, frame->tf_t, sizeof(regs->t)); + memcpy(regs->s, frame->tf_s, sizeof(regs->s)); + memcpy(regs->a, frame->tf_a, sizeof(regs->a)); + + return (0); +} + +int +set_regs(struct thread *td, struct reg *regs) +{ + struct trapframe *frame; + + frame = td->td_frame; + frame->tf_sepc = regs->sepc; + frame->tf_ra = regs->ra; + frame->tf_sp = regs->sp; + frame->tf_gp = regs->gp; + frame->tf_tp = regs->tp; + + memcpy(frame->tf_t, regs->t, sizeof(frame->tf_t)); + memcpy(frame->tf_s, regs->s, sizeof(frame->tf_s)); + memcpy(frame->tf_a, regs->a, sizeof(frame->tf_a)); + + return (0); +} + +int +fill_fpregs(struct thread *td, struct fpreg *regs) +{ +#ifdef FPE + struct pcb *pcb; + + pcb = td->td_pcb; + + if ((pcb->pcb_fpflags & PCB_FP_STARTED) != 0) { + /* + * If we have just been running FPE instructions we will + * need to save the state to memcpy it below. + */ + if (td == curthread) + fpe_state_save(td); + + memcpy(regs->fp_x, pcb->pcb_x, sizeof(regs->fp_x)); + regs->fp_fcsr = pcb->pcb_fcsr; + } else +#endif + memset(regs, 0, sizeof(*regs)); + + return (0); +} + +int +set_fpregs(struct thread *td, struct fpreg *regs) +{ +#ifdef FPE + struct trapframe *frame; + struct pcb *pcb; + + frame = td->td_frame; + pcb = td->td_pcb; + + memcpy(pcb->pcb_x, regs->fp_x, sizeof(regs->fp_x)); + pcb->pcb_fcsr = regs->fp_fcsr; + pcb->pcb_fpflags |= PCB_FP_STARTED; + frame->tf_sstatus &= ~SSTATUS_FS_MASK; + frame->tf_sstatus |= SSTATUS_FS_CLEAN; +#endif + + return (0); +} + +int +fill_dbregs(struct thread *td, struct dbreg *regs) +{ + + panic("fill_dbregs"); +} + +int +set_dbregs(struct thread *td, struct dbreg *regs) +{ + + panic("set_dbregs"); +} + +void +exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) +{ + struct trapframe *tf; + struct pcb *pcb; + + tf = td->td_frame; + pcb = td->td_pcb; + + memset(tf, 0, sizeof(struct trapframe)); + + tf->tf_a[0] = stack; + tf->tf_sp = STACKALIGN(stack); + tf->tf_ra = imgp->entry_addr; + tf->tf_sepc = imgp->entry_addr; + + pcb->pcb_fpflags &= ~PCB_FP_STARTED; +} + +/* Sanity check these are the same size, they will be memcpy'd to and from */ +CTASSERT(sizeof(((struct trapframe *)0)->tf_a) == + sizeof((struct gpregs *)0)->gp_a); +CTASSERT(sizeof(((struct trapframe *)0)->tf_s) == + sizeof((struct gpregs *)0)->gp_s); +CTASSERT(sizeof(((struct trapframe *)0)->tf_t) == + sizeof((struct gpregs *)0)->gp_t); +CTASSERT(sizeof(((struct trapframe *)0)->tf_a) == + sizeof((struct reg *)0)->a); +CTASSERT(sizeof(((struct trapframe *)0)->tf_s) == + sizeof((struct reg *)0)->s); +CTASSERT(sizeof(((struct trapframe *)0)->tf_t) == + sizeof((struct reg *)0)->t); + +int +get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) +{ + struct trapframe *tf = td->td_frame; + + memcpy(mcp->mc_gpregs.gp_t, tf->tf_t, sizeof(mcp->mc_gpregs.gp_t)); + memcpy(mcp->mc_gpregs.gp_s, tf->tf_s, sizeof(mcp->mc_gpregs.gp_s)); + memcpy(mcp->mc_gpregs.gp_a, tf->tf_a, sizeof(mcp->mc_gpregs.gp_a)); + + if (clear_ret & GET_MC_CLEAR_RET) { + mcp->mc_gpregs.gp_a[0] = 0; + mcp->mc_gpregs.gp_t[0] = 0; /* clear syscall error */ + } + + mcp->mc_gpregs.gp_ra = tf->tf_ra; + mcp->mc_gpregs.gp_sp = tf->tf_sp; + mcp->mc_gpregs.gp_gp = tf->tf_gp; + mcp->mc_gpregs.gp_tp = tf->tf_tp; + mcp->mc_gpregs.gp_sepc = tf->tf_sepc; + mcp->mc_gpregs.gp_sstatus = tf->tf_sstatus; + get_fpcontext(td, mcp); + + return (0); +} + +int +set_mcontext(struct thread *td, mcontext_t *mcp) +{ + struct trapframe *tf; + + tf = td->td_frame; + + /* + * Permit changes to the USTATUS bits of SSTATUS. + * + * Ignore writes to read-only bits (SD, XS). + * + * Ignore writes to the FS field as set_fpcontext() will set + * it explicitly. + */ + if (((mcp->mc_gpregs.gp_sstatus ^ tf->tf_sstatus) & + ~(SSTATUS_SD | SSTATUS_XS_MASK | SSTATUS_FS_MASK | SSTATUS_UPIE | + SSTATUS_UIE)) != 0) + return (EINVAL); + + memcpy(tf->tf_t, mcp->mc_gpregs.gp_t, sizeof(tf->tf_t)); + memcpy(tf->tf_s, mcp->mc_gpregs.gp_s, sizeof(tf->tf_s)); + memcpy(tf->tf_a, mcp->mc_gpregs.gp_a, sizeof(tf->tf_a)); + + tf->tf_ra = mcp->mc_gpregs.gp_ra; + tf->tf_sp = mcp->mc_gpregs.gp_sp; + tf->tf_gp = mcp->mc_gpregs.gp_gp; + tf->tf_sepc = mcp->mc_gpregs.gp_sepc; + tf->tf_sstatus = mcp->mc_gpregs.gp_sstatus; + set_fpcontext(td, mcp); + + return (0); +} + +static void +get_fpcontext(struct thread *td, mcontext_t *mcp) +{ +#ifdef FPE + struct pcb *curpcb; + + critical_enter(); + + curpcb = curthread->td_pcb; + + KASSERT(td->td_pcb == curpcb, ("Invalid fpe pcb")); + + if ((curpcb->pcb_fpflags & PCB_FP_STARTED) != 0) { + /* + * If we have just been running FPE instructions we will + * need to save the state to memcpy it below. + */ + fpe_state_save(td); + + KASSERT((curpcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0, + ("Non-userspace FPE flags set in get_fpcontext")); + memcpy(mcp->mc_fpregs.fp_x, curpcb->pcb_x, + sizeof(mcp->mc_fpregs.fp_x)); + mcp->mc_fpregs.fp_fcsr = curpcb->pcb_fcsr; + mcp->mc_fpregs.fp_flags = curpcb->pcb_fpflags; + mcp->mc_flags |= _MC_FP_VALID; + } + + critical_exit(); +#endif +} + +static void +set_fpcontext(struct thread *td, mcontext_t *mcp) +{ +#ifdef FPE + struct pcb *curpcb; +#endif + + td->td_frame->tf_sstatus &= ~SSTATUS_FS_MASK; + td->td_frame->tf_sstatus |= SSTATUS_FS_OFF; + +#ifdef FPE + critical_enter(); + + if ((mcp->mc_flags & _MC_FP_VALID) != 0) { + curpcb = curthread->td_pcb; + /* FPE usage is enabled, override registers. */ + memcpy(curpcb->pcb_x, mcp->mc_fpregs.fp_x, + sizeof(mcp->mc_fpregs.fp_x)); + curpcb->pcb_fcsr = mcp->mc_fpregs.fp_fcsr; + curpcb->pcb_fpflags = mcp->mc_fpregs.fp_flags & PCB_FP_USERMASK; + td->td_frame->tf_sstatus |= SSTATUS_FS_CLEAN; + } + + critical_exit(); +#endif +} + +int +sys_sigreturn(struct thread *td, struct sigreturn_args *uap) +{ + ucontext_t uc; + int error; + + if (copyin(uap->sigcntxp, &uc, sizeof(uc))) + return (EFAULT); + + error = set_mcontext(td, &uc.uc_mcontext); + if (error != 0) + return (error); + + /* Restore signal mask. */ + kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); + + return (EJUSTRETURN); +} + +void +sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) +{ + struct sigframe *fp, frame; + struct sysentvec *sysent; + struct trapframe *tf; + struct sigacts *psp; + struct thread *td; + struct proc *p; + int onstack; + int sig; + + 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); + + tf = td->td_frame; + onstack = sigonstack(tf->tf_sp); + + CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, + catcher, sig); + + /* Allocate and validate space for the signal handler context. */ + if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack && + SIGISMEMBER(psp->ps_sigonstack, sig)) { + fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp + + td->td_sigstk.ss_size); + } else { + fp = (struct sigframe *)td->td_frame->tf_sp; + } + + /* Make room, keeping the stack aligned */ + fp--; + fp = (struct sigframe *)STACKALIGN(fp); + + /* Fill in the frame to copy out */ + bzero(&frame, sizeof(frame)); + get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); + frame.sf_si = ksi->ksi_info; + frame.sf_uc.uc_sigmask = *mask; + frame.sf_uc.uc_stack = td->td_sigstk; + frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? + (onstack ? SS_ONSTACK : 0) : SS_DISABLE; + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(td->td_proc); + + /* Copy the sigframe out to the user's stack. */ + if (copyout(&frame, fp, sizeof(*fp)) != 0) { + /* Process has trashed its stack. Kill it. */ + CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + tf->tf_a[0] = sig; + tf->tf_a[1] = (register_t)&fp->sf_si; + tf->tf_a[2] = (register_t)&fp->sf_uc; + + tf->tf_sepc = (register_t)catcher; + tf->tf_sp = (register_t)fp; + + sysent = p->p_sysent; + if (sysent->sv_sigcode_base != 0) + tf->tf_ra = (register_t)sysent->sv_sigcode_base; + else + tf->tf_ra = (register_t)(sysent->sv_psstrings - + *(sysent->sv_szsigcode)); + + CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_sepc, + tf->tf_sp); + + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); +} diff --git a/sys/riscv/riscv/machdep.c b/sys/riscv/riscv/machdep.c --- a/sys/riscv/riscv/machdep.c +++ b/sys/riscv/riscv/machdep.c @@ -102,9 +102,6 @@ #include #endif -static void get_fpcontext(struct thread *td, mcontext_t *mcp); -static void set_fpcontext(struct thread *td, mcontext_t *mcp); - struct pcpu __pcpu[MAXCPU]; static struct trapframe proc0_tf; @@ -182,282 +179,6 @@ return (0); } -int -fill_regs(struct thread *td, struct reg *regs) -{ - struct trapframe *frame; - - frame = td->td_frame; - regs->sepc = frame->tf_sepc; - regs->sstatus = frame->tf_sstatus; - regs->ra = frame->tf_ra; - regs->sp = frame->tf_sp; - regs->gp = frame->tf_gp; - regs->tp = frame->tf_tp; - - memcpy(regs->t, frame->tf_t, sizeof(regs->t)); - memcpy(regs->s, frame->tf_s, sizeof(regs->s)); - memcpy(regs->a, frame->tf_a, sizeof(regs->a)); - - return (0); -} - -int -set_regs(struct thread *td, struct reg *regs) -{ - struct trapframe *frame; - - frame = td->td_frame; - frame->tf_sepc = regs->sepc; - frame->tf_ra = regs->ra; - frame->tf_sp = regs->sp; - frame->tf_gp = regs->gp; - frame->tf_tp = regs->tp; - - memcpy(frame->tf_t, regs->t, sizeof(frame->tf_t)); - memcpy(frame->tf_s, regs->s, sizeof(frame->tf_s)); - memcpy(frame->tf_a, regs->a, sizeof(frame->tf_a)); - - return (0); -} - -int -fill_fpregs(struct thread *td, struct fpreg *regs) -{ -#ifdef FPE - struct pcb *pcb; - - pcb = td->td_pcb; - - if ((pcb->pcb_fpflags & PCB_FP_STARTED) != 0) { - /* - * If we have just been running FPE instructions we will - * need to save the state to memcpy it below. - */ - if (td == curthread) - fpe_state_save(td); - - memcpy(regs->fp_x, pcb->pcb_x, sizeof(regs->fp_x)); - regs->fp_fcsr = pcb->pcb_fcsr; - } else -#endif - memset(regs, 0, sizeof(*regs)); - - return (0); -} - -int -set_fpregs(struct thread *td, struct fpreg *regs) -{ -#ifdef FPE - struct trapframe *frame; - struct pcb *pcb; - - frame = td->td_frame; - pcb = td->td_pcb; - - memcpy(pcb->pcb_x, regs->fp_x, sizeof(regs->fp_x)); - pcb->pcb_fcsr = regs->fp_fcsr; - pcb->pcb_fpflags |= PCB_FP_STARTED; - frame->tf_sstatus &= ~SSTATUS_FS_MASK; - frame->tf_sstatus |= SSTATUS_FS_CLEAN; -#endif - - return (0); -} - -int -fill_dbregs(struct thread *td, struct dbreg *regs) -{ - - panic("fill_dbregs"); -} - -int -set_dbregs(struct thread *td, struct dbreg *regs) -{ - - panic("set_dbregs"); -} - -int -ptrace_set_pc(struct thread *td, u_long addr) -{ - - td->td_frame->tf_sepc = addr; - return (0); -} - -int -ptrace_single_step(struct thread *td) -{ - - /* TODO; */ - return (EOPNOTSUPP); -} - -int -ptrace_clear_single_step(struct thread *td) -{ - - /* TODO; */ - return (EOPNOTSUPP); -} - -void -exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) -{ - struct trapframe *tf; - struct pcb *pcb; - - tf = td->td_frame; - pcb = td->td_pcb; - - memset(tf, 0, sizeof(struct trapframe)); - - tf->tf_a[0] = stack; - tf->tf_sp = STACKALIGN(stack); - tf->tf_ra = imgp->entry_addr; - tf->tf_sepc = imgp->entry_addr; - - pcb->pcb_fpflags &= ~PCB_FP_STARTED; -} - -/* Sanity check these are the same size, they will be memcpy'd to and fro */ -CTASSERT(sizeof(((struct trapframe *)0)->tf_a) == - sizeof((struct gpregs *)0)->gp_a); -CTASSERT(sizeof(((struct trapframe *)0)->tf_s) == - sizeof((struct gpregs *)0)->gp_s); -CTASSERT(sizeof(((struct trapframe *)0)->tf_t) == - sizeof((struct gpregs *)0)->gp_t); -CTASSERT(sizeof(((struct trapframe *)0)->tf_a) == - sizeof((struct reg *)0)->a); -CTASSERT(sizeof(((struct trapframe *)0)->tf_s) == - sizeof((struct reg *)0)->s); -CTASSERT(sizeof(((struct trapframe *)0)->tf_t) == - sizeof((struct reg *)0)->t); - -/* Support for FDT configurations only. */ -CTASSERT(FDT); - -int -get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) -{ - struct trapframe *tf = td->td_frame; - - memcpy(mcp->mc_gpregs.gp_t, tf->tf_t, sizeof(mcp->mc_gpregs.gp_t)); - memcpy(mcp->mc_gpregs.gp_s, tf->tf_s, sizeof(mcp->mc_gpregs.gp_s)); - memcpy(mcp->mc_gpregs.gp_a, tf->tf_a, sizeof(mcp->mc_gpregs.gp_a)); - - if (clear_ret & GET_MC_CLEAR_RET) { - mcp->mc_gpregs.gp_a[0] = 0; - mcp->mc_gpregs.gp_t[0] = 0; /* clear syscall error */ - } - - mcp->mc_gpregs.gp_ra = tf->tf_ra; - mcp->mc_gpregs.gp_sp = tf->tf_sp; - mcp->mc_gpregs.gp_gp = tf->tf_gp; - mcp->mc_gpregs.gp_tp = tf->tf_tp; - mcp->mc_gpregs.gp_sepc = tf->tf_sepc; - mcp->mc_gpregs.gp_sstatus = tf->tf_sstatus; - get_fpcontext(td, mcp); - - return (0); -} - -int -set_mcontext(struct thread *td, mcontext_t *mcp) -{ - struct trapframe *tf; - - tf = td->td_frame; - - /* - * Permit changes to the USTATUS bits of SSTATUS. - * - * Ignore writes to read-only bits (SD, XS). - * - * Ignore writes to the FS field as set_fpcontext() will set - * it explicitly. - */ - if (((mcp->mc_gpregs.gp_sstatus ^ tf->tf_sstatus) & - ~(SSTATUS_SD | SSTATUS_XS_MASK | SSTATUS_FS_MASK | SSTATUS_UPIE | - SSTATUS_UIE)) != 0) - return (EINVAL); - - memcpy(tf->tf_t, mcp->mc_gpregs.gp_t, sizeof(tf->tf_t)); - memcpy(tf->tf_s, mcp->mc_gpregs.gp_s, sizeof(tf->tf_s)); - memcpy(tf->tf_a, mcp->mc_gpregs.gp_a, sizeof(tf->tf_a)); - - tf->tf_ra = mcp->mc_gpregs.gp_ra; - tf->tf_sp = mcp->mc_gpregs.gp_sp; - tf->tf_gp = mcp->mc_gpregs.gp_gp; - tf->tf_sepc = mcp->mc_gpregs.gp_sepc; - tf->tf_sstatus = mcp->mc_gpregs.gp_sstatus; - set_fpcontext(td, mcp); - - return (0); -} - -static void -get_fpcontext(struct thread *td, mcontext_t *mcp) -{ -#ifdef FPE - struct pcb *curpcb; - - critical_enter(); - - curpcb = curthread->td_pcb; - - KASSERT(td->td_pcb == curpcb, ("Invalid fpe pcb")); - - if ((curpcb->pcb_fpflags & PCB_FP_STARTED) != 0) { - /* - * If we have just been running FPE instructions we will - * need to save the state to memcpy it below. - */ - fpe_state_save(td); - - KASSERT((curpcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0, - ("Non-userspace FPE flags set in get_fpcontext")); - memcpy(mcp->mc_fpregs.fp_x, curpcb->pcb_x, - sizeof(mcp->mc_fpregs.fp_x)); - mcp->mc_fpregs.fp_fcsr = curpcb->pcb_fcsr; - mcp->mc_fpregs.fp_flags = curpcb->pcb_fpflags; - mcp->mc_flags |= _MC_FP_VALID; - } - - critical_exit(); -#endif -} - -static void -set_fpcontext(struct thread *td, mcontext_t *mcp) -{ -#ifdef FPE - struct pcb *curpcb; -#endif - - td->td_frame->tf_sstatus &= ~SSTATUS_FS_MASK; - td->td_frame->tf_sstatus |= SSTATUS_FS_OFF; - -#ifdef FPE - critical_enter(); - - if ((mcp->mc_flags & _MC_FP_VALID) != 0) { - curpcb = curthread->td_pcb; - /* FPE usage is enabled, override registers. */ - memcpy(curpcb->pcb_x, mcp->mc_fpregs.fp_x, - sizeof(mcp->mc_fpregs.fp_x)); - curpcb->pcb_fcsr = mcp->mc_fpregs.fp_fcsr; - curpcb->pcb_fpflags = mcp->mc_fpregs.fp_flags & PCB_FP_USERMASK; - td->td_frame->tf_sstatus |= SSTATUS_FS_CLEAN; - } - - critical_exit(); -#endif -} - void cpu_idle(int busy) { @@ -545,31 +266,6 @@ } } -#ifndef _SYS_SYSPROTO_H_ -struct sigreturn_args { - ucontext_t *ucp; -}; -#endif - -int -sys_sigreturn(struct thread *td, struct sigreturn_args *uap) -{ - ucontext_t uc; - int error; - - if (copyin(uap->sigcntxp, &uc, sizeof(uc))) - return (EFAULT); - - error = set_mcontext(td, &uc.uc_mcontext); - if (error != 0) - return (error); - - /* Restore signal mask. */ - kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); - - return (EJUSTRETURN); -} - /* * Construct a PCB from a trapframe. This is called from kdb_trap() where * we want to start a backtrace from the function that caused us to enter @@ -589,85 +285,6 @@ pcb->pcb_tp = tf->tf_tp; } -void -sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) -{ - struct sigframe *fp, frame; - struct sysentvec *sysent; - struct trapframe *tf; - struct sigacts *psp; - struct thread *td; - struct proc *p; - int onstack; - int sig; - - 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); - - tf = td->td_frame; - onstack = sigonstack(tf->tf_sp); - - CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, - catcher, sig); - - /* Allocate and validate space for the signal handler context. */ - if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack && - SIGISMEMBER(psp->ps_sigonstack, sig)) { - fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp + - td->td_sigstk.ss_size); - } else { - fp = (struct sigframe *)td->td_frame->tf_sp; - } - - /* Make room, keeping the stack aligned */ - fp--; - fp = (struct sigframe *)STACKALIGN(fp); - - /* Fill in the frame to copy out */ - bzero(&frame, sizeof(frame)); - get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); - frame.sf_si = ksi->ksi_info; - frame.sf_uc.uc_sigmask = *mask; - frame.sf_uc.uc_stack = td->td_sigstk; - frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? - (onstack ? SS_ONSTACK : 0) : SS_DISABLE; - mtx_unlock(&psp->ps_mtx); - PROC_UNLOCK(td->td_proc); - - /* Copy the sigframe out to the user's stack. */ - if (copyout(&frame, fp, sizeof(*fp)) != 0) { - /* Process has trashed its stack. Kill it. */ - CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); - PROC_LOCK(p); - sigexit(td, SIGILL); - } - - tf->tf_a[0] = sig; - tf->tf_a[1] = (register_t)&fp->sf_si; - tf->tf_a[2] = (register_t)&fp->sf_uc; - - tf->tf_sepc = (register_t)catcher; - tf->tf_sp = (register_t)fp; - - sysent = p->p_sysent; - if (sysent->sv_sigcode_base != 0) - tf->tf_ra = (register_t)sysent->sv_sigcode_base; - else - tf->tf_ra = (register_t)(sysent->sv_psstrings - - *(sysent->sv_szsigcode)); - - CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_sepc, - tf->tf_sp); - - PROC_LOCK(p); - mtx_lock(&psp->ps_mtx); -} - static void init_proc0(vm_offset_t kstack) { @@ -803,6 +420,9 @@ rvbp->kern_phys, rvbp->kern_phys + (lastaddr - KERNBASE)); } +/* Support for FDT configurations only. */ +CTASSERT(FDT); + #ifdef FDT static void parse_fdt_bootargs(void) diff --git a/sys/riscv/riscv/ptrace_machdep.c b/sys/riscv/riscv/ptrace_machdep.c new file mode 100644 --- /dev/null +++ b/sys/riscv/riscv/ptrace_machdep.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2014 Andrew Turner + * Copyright (c) 2015-2017 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#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 + +int +ptrace_set_pc(struct thread *td, u_long addr) +{ + + td->td_frame->tf_sepc = addr; + return (0); +} + +int +ptrace_single_step(struct thread *td) +{ + + /* TODO; */ + return (EOPNOTSUPP); +} + +int +ptrace_clear_single_step(struct thread *td) +{ + + /* TODO; */ + return (EOPNOTSUPP); +} +