diff options
author | Richard Lowe <richlowe@richlowe.net> | 2021-06-04 15:15:12 -0500 |
---|---|---|
committer | Richard Lowe <richlowe@richlowe.net> | 2021-08-16 12:46:39 -0500 |
commit | f0089e391b2bc4be2755f1a1b51fb4cd9b8f3988 (patch) | |
tree | c4ac2f5e703ed459d50bcee7ddb38a993d961520 /usr/src/uts/intel/syscall | |
parent | d083fed0c91296a88878f7a468910ad5b5c888ea (diff) | |
download | illumos-joyent-f0089e391b2bc4be2755f1a1b51fb4cd9b8f3988.tar.gz |
13941 intel code and headers should not look ia32 specific
Reviewed by: Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Patrick Mooney <pmooney@pfmooney.com>
Approved by: Garret D'Amore <garrett@damore.org>
Diffstat (limited to 'usr/src/uts/intel/syscall')
-rw-r--r-- | usr/src/uts/intel/syscall/getcontext.c | 379 | ||||
-rw-r--r-- | usr/src/uts/intel/syscall/lwp_private.c | 222 |
2 files changed, 601 insertions, 0 deletions
diff --git a/usr/src/uts/intel/syscall/getcontext.c b/usr/src/uts/intel/syscall/getcontext.c new file mode 100644 index 0000000000..f7c404ba72 --- /dev/null +++ b/usr/src/uts/intel/syscall/getcontext.c @@ -0,0 +1,379 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/vmparam.h> +#include <sys/systm.h> +#include <sys/signal.h> +#include <sys/stack.h> +#include <sys/regset.h> +#include <sys/privregs.h> +#include <sys/frame.h> +#include <sys/proc.h> +#include <sys/brand.h> +#include <sys/psw.h> +#include <sys/ucontext.h> +#include <sys/asm_linkage.h> +#include <sys/errno.h> +#include <sys/archsystm.h> +#include <sys/schedctl.h> +#include <sys/debug.h> +#include <sys/sysmacros.h> + +/* + * Save user context. + */ +void +savecontext(ucontext_t *ucp, const k_sigset_t *mask) +{ + proc_t *p = ttoproc(curthread); + klwp_t *lwp = ttolwp(curthread); + struct regs *rp = lwptoregs(lwp); + + /* + * We unconditionally assign to every field through the end + * of the gregs, but we need to bzero() everything -after- that + * to avoid having any kernel stack garbage escape to userland. + */ + bzero(&ucp->uc_mcontext.fpregs, sizeof (ucontext_t) - + offsetof(ucontext_t, uc_mcontext.fpregs)); + + ucp->uc_flags = UC_ALL; + ucp->uc_link = (struct ucontext *)lwp->lwp_oldcontext; + + /* + * Try to copyin() the ustack if one is registered. If the stack + * has zero size, this indicates that stack bounds checking has + * been disabled for this LWP. If stack bounds checking is disabled + * or the copyin() fails, we fall back to the legacy behavior. + */ + if (lwp->lwp_ustack == (uintptr_t)NULL || + copyin((void *)lwp->lwp_ustack, &ucp->uc_stack, + sizeof (ucp->uc_stack)) != 0 || + ucp->uc_stack.ss_size == 0) { + + if (lwp->lwp_sigaltstack.ss_flags == SS_ONSTACK) { + ucp->uc_stack = lwp->lwp_sigaltstack; + } else { + ucp->uc_stack.ss_sp = p->p_usrstack - p->p_stksize; + ucp->uc_stack.ss_size = p->p_stksize; + ucp->uc_stack.ss_flags = 0; + } + } + + /* + * If either the trace flag or REQUEST_STEP is set, + * arrange for single-stepping and turn off the trace flag. + */ + if ((rp->r_ps & PS_T) || (lwp->lwp_pcb.pcb_flags & REQUEST_STEP)) { + /* + * Clear PS_T so that saved user context won't have trace + * flag set. + */ + rp->r_ps &= ~PS_T; + + if (!(lwp->lwp_pcb.pcb_flags & REQUEST_NOSTEP)) { + lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING; + /* + * trap() always checks DEBUG_PENDING before + * checking for any pending signal. This at times + * can potentially lead to DEBUG_PENDING not being + * honoured. (for eg: the lwp is stopped by + * stop_on_fault() called from trap(), after being + * awakened it might see a pending signal and call + * savecontext(), however on the way back to userland + * there is no place it can be detected). Hence in + * anticipation of such occassions, set AST flag for + * the thread which will make the thread take an + * excursion through trap() where it will be handled + * appropriately. + */ + aston(curthread); + } + } + + getgregs(lwp, ucp->uc_mcontext.gregs); + if (lwp->lwp_pcb.pcb_fpu.fpu_flags & FPU_EN) + getfpregs(lwp, &ucp->uc_mcontext.fpregs); + else + ucp->uc_flags &= ~UC_FPU; + + sigktou(mask, &ucp->uc_sigmask); +} + +/* + * Restore user context. + */ +void +restorecontext(ucontext_t *ucp) +{ + kthread_t *t = curthread; + klwp_t *lwp = ttolwp(t); + + lwp->lwp_oldcontext = (uintptr_t)ucp->uc_link; + + if (ucp->uc_flags & UC_STACK) { + if (ucp->uc_stack.ss_flags == SS_ONSTACK) + lwp->lwp_sigaltstack = ucp->uc_stack; + else + lwp->lwp_sigaltstack.ss_flags &= ~SS_ONSTACK; + } + + if (ucp->uc_flags & UC_CPU) { + /* + * If the trace flag is set, mark the lwp to take a + * single-step trap on return to user level (below). + * The x86 lcall interface and sysenter has already done this, + * and turned off the flag, but amd64 syscall interface has not. + */ + if (lwptoregs(lwp)->r_ps & PS_T) + lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING; + setgregs(lwp, ucp->uc_mcontext.gregs); + lwp->lwp_eosys = JUSTRETURN; + t->t_post_sys = 1; + aston(curthread); + } + + if (ucp->uc_flags & UC_FPU) + setfpregs(lwp, &ucp->uc_mcontext.fpregs); + + if (ucp->uc_flags & UC_SIGMASK) { + /* + * We don't need to acquire p->p_lock here; + * we are manipulating thread-private data. + */ + schedctl_finish_sigblock(t); + sigutok(&ucp->uc_sigmask, &t->t_hold); + if (sigcheck(ttoproc(t), t)) + t->t_sig_check = 1; + } +} + + +int +getsetcontext(int flag, void *arg) +{ + ucontext_t uc; + ucontext_t *ucp; + klwp_t *lwp = ttolwp(curthread); + stack_t dummy_stk; + + /* + * In future releases, when the ucontext structure grows, + * getcontext should be modified to only return the fields + * specified in the uc_flags. That way, the structure can grow + * and still be binary compatible will all .o's which will only + * have old fields defined in uc_flags + */ + + switch (flag) { + default: + return (set_errno(EINVAL)); + + case GETCONTEXT: + schedctl_finish_sigblock(curthread); + savecontext(&uc, &curthread->t_hold); + if (uc.uc_flags & UC_SIGMASK) + SIGSET_NATIVE_TO_BRAND(&uc.uc_sigmask); + if (copyout(&uc, arg, sizeof (uc))) + return (set_errno(EFAULT)); + return (0); + + case SETCONTEXT: + ucp = arg; + if (ucp == NULL) + exit(CLD_EXITED, 0); + /* + * Don't copyin filler or floating state unless we need it. + * The ucontext_t struct and fields are specified in the ABI. + */ + if (copyin(ucp, &uc, sizeof (ucontext_t) - + sizeof (uc.uc_filler) - + sizeof (uc.uc_mcontext.fpregs))) { + return (set_errno(EFAULT)); + } + if (uc.uc_flags & UC_SIGMASK) + SIGSET_BRAND_TO_NATIVE(&uc.uc_sigmask); + + if ((uc.uc_flags & UC_FPU) && + copyin(&ucp->uc_mcontext.fpregs, &uc.uc_mcontext.fpregs, + sizeof (uc.uc_mcontext.fpregs))) { + return (set_errno(EFAULT)); + } + + restorecontext(&uc); + + if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0)) + (void) copyout(&uc.uc_stack, (stack_t *)lwp->lwp_ustack, + sizeof (uc.uc_stack)); + return (0); + + case GETUSTACK: + if (copyout(&lwp->lwp_ustack, arg, sizeof (caddr_t))) + return (set_errno(EFAULT)); + return (0); + + case SETUSTACK: + if (copyin(arg, &dummy_stk, sizeof (dummy_stk))) + return (set_errno(EFAULT)); + lwp->lwp_ustack = (uintptr_t)arg; + return (0); + } +} + +#ifdef _SYSCALL32_IMPL + +/* + * Save user context for 32-bit processes. + */ +void +savecontext32(ucontext32_t *ucp, const k_sigset_t *mask) +{ + proc_t *p = ttoproc(curthread); + klwp_t *lwp = ttolwp(curthread); + struct regs *rp = lwptoregs(lwp); + + bzero(&ucp->uc_mcontext.fpregs, sizeof (ucontext32_t) - + offsetof(ucontext32_t, uc_mcontext.fpregs)); + + ucp->uc_flags = UC_ALL; + ucp->uc_link = (caddr32_t)lwp->lwp_oldcontext; + + if (lwp->lwp_ustack == (uintptr_t)NULL || + copyin((void *)lwp->lwp_ustack, &ucp->uc_stack, + sizeof (ucp->uc_stack)) != 0 || + ucp->uc_stack.ss_size == 0) { + + if (lwp->lwp_sigaltstack.ss_flags == SS_ONSTACK) { + ucp->uc_stack.ss_sp = + (caddr32_t)(uintptr_t)lwp->lwp_sigaltstack.ss_sp; + ucp->uc_stack.ss_size = + (size32_t)lwp->lwp_sigaltstack.ss_size; + ucp->uc_stack.ss_flags = SS_ONSTACK; + } else { + ucp->uc_stack.ss_sp = (caddr32_t)(uintptr_t) + (p->p_usrstack - p->p_stksize); + ucp->uc_stack.ss_size = (size32_t)p->p_stksize; + ucp->uc_stack.ss_flags = 0; + } + } + + /* + * If either the trace flag or REQUEST_STEP is set, arrange + * for single-stepping and turn off the trace flag. + */ + if ((rp->r_ps & PS_T) || (lwp->lwp_pcb.pcb_flags & REQUEST_STEP)) { + /* + * Clear PS_T so that saved user context won't have trace + * flag set. + */ + rp->r_ps &= ~PS_T; + + if (!(lwp->lwp_pcb.pcb_flags & REQUEST_NOSTEP)) { + lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING; + /* + * See comments in savecontext(). + */ + aston(curthread); + } + } + + getgregs32(lwp, ucp->uc_mcontext.gregs); + if (lwp->lwp_pcb.pcb_fpu.fpu_flags & FPU_EN) + getfpregs32(lwp, &ucp->uc_mcontext.fpregs); + else + ucp->uc_flags &= ~UC_FPU; + + sigktou(mask, &ucp->uc_sigmask); +} + +int +getsetcontext32(int flag, void *arg) +{ + ucontext32_t uc; + ucontext_t ucnat; + ucontext32_t *ucp; + klwp_t *lwp = ttolwp(curthread); + caddr32_t ustack32; + stack32_t dummy_stk32; + + switch (flag) { + default: + return (set_errno(EINVAL)); + + case GETCONTEXT: + schedctl_finish_sigblock(curthread); + savecontext32(&uc, &curthread->t_hold); + if (uc.uc_flags & UC_SIGMASK) + SIGSET_NATIVE_TO_BRAND(&uc.uc_sigmask); + if (copyout(&uc, arg, sizeof (uc))) + return (set_errno(EFAULT)); + return (0); + + case SETCONTEXT: + ucp = arg; + if (ucp == NULL) + exit(CLD_EXITED, 0); + if (copyin(ucp, &uc, sizeof (uc) - + sizeof (uc.uc_filler) - + sizeof (uc.uc_mcontext.fpregs))) { + return (set_errno(EFAULT)); + } + if (uc.uc_flags & UC_SIGMASK) + SIGSET_BRAND_TO_NATIVE(&uc.uc_sigmask); + if ((uc.uc_flags & UC_FPU) && + copyin(&ucp->uc_mcontext.fpregs, &uc.uc_mcontext.fpregs, + sizeof (uc.uc_mcontext.fpregs))) { + return (set_errno(EFAULT)); + } + + ucontext_32ton(&uc, &ucnat); + restorecontext(&ucnat); + + if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0)) + (void) copyout(&uc.uc_stack, + (stack32_t *)lwp->lwp_ustack, sizeof (uc.uc_stack)); + return (0); + + case GETUSTACK: + ustack32 = (caddr32_t)lwp->lwp_ustack; + if (copyout(&ustack32, arg, sizeof (ustack32))) + return (set_errno(EFAULT)); + return (0); + + case SETUSTACK: + if (copyin(arg, &dummy_stk32, sizeof (dummy_stk32))) + return (set_errno(EFAULT)); + lwp->lwp_ustack = (uintptr_t)arg; + return (0); + } +} + +#endif /* _SYSCALL32_IMPL */ diff --git a/usr/src/uts/intel/syscall/lwp_private.c b/usr/src/uts/intel/syscall/lwp_private.c new file mode 100644 index 0000000000..50331a2899 --- /dev/null +++ b/usr/src/uts/intel/syscall/lwp_private.c @@ -0,0 +1,222 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright (c) 2018, Joyent, Inc. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/disp.h> +#include <sys/sysmacros.h> +#include <sys/cpuvar.h> +#include <sys/systm.h> +#include <sys/thread.h> +#include <sys/lwp.h> +#include <sys/segments.h> +#include <sys/privregs.h> +#include <sys/cmn_err.h> + +int +lwp_setprivate(klwp_t *lwp, int which, uintptr_t base) +{ + pcb_t *pcb = &lwp->lwp_pcb; + struct regs *rp = lwptoregs(lwp); + kthread_t *t = lwptot(lwp); + int thisthread = t == curthread; + int rval; + + if (thisthread) + kpreempt_disable(); + + + /* + * 32-bit compatibility processes point to the per-cpu GDT segment + * descriptors that are virtualized to the lwp. That allows 32-bit + * programs to mess with %fs and %gs; in particular it allows + * things like this: + * + * movw %gs, %ax + * ... + * movw %ax, %gs + * + * to work, which is needed by emulators for legacy application + * environments .. + * + * 64-bit processes may also point to a per-cpu GDT segment descriptor + * virtualized to the lwp. However the descriptor base is forced + * to zero (because we can't express the full 64-bit address range + * in a long mode descriptor), so don't reload segment registers + * in a 64-bit program! 64-bit processes must have selector values + * of zero for %fs and %gs to use the 64-bit fs_base and gs_base + * respectively. + */ + if (!PCB_NEED_UPDATE_SEGS(pcb)) { + pcb->pcb_ds = rp->r_ds; + pcb->pcb_es = rp->r_es; + pcb->pcb_fs = rp->r_fs; + pcb->pcb_gs = rp->r_gs; + PCB_SET_UPDATE_SEGS(pcb); + t->t_post_sys = 1; + } + ASSERT(t->t_post_sys); + + switch (which) { + case _LWP_FSBASE: + if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { + set_usegd(&pcb->pcb_fsdesc, SDP_LONG, 0, 0, + SDT_MEMRWA, SEL_UPL, SDP_BYTES, SDP_OP32); + rval = pcb->pcb_fs = 0; /* null gdt descriptor */ + } else { + set_usegd(&pcb->pcb_fsdesc, SDP_SHORT, (void *)base, -1, + SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); + rval = pcb->pcb_fs = LWPFS_SEL; + } + if (thisthread) + gdt_update_usegd(GDT_LWPFS, &pcb->pcb_fsdesc); + + pcb->pcb_fsbase = base; + break; + case _LWP_GSBASE: + if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { + set_usegd(&pcb->pcb_gsdesc, SDP_LONG, 0, 0, + SDT_MEMRWA, SEL_UPL, SDP_BYTES, SDP_OP32); + rval = pcb->pcb_gs = 0; /* null gdt descriptor */ + } else { + set_usegd(&pcb->pcb_gsdesc, SDP_SHORT, (void *)base, -1, + SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); + rval = pcb->pcb_gs = LWPGS_SEL; + } + if (thisthread) + gdt_update_usegd(GDT_LWPGS, &pcb->pcb_gsdesc); + + pcb->pcb_gsbase = base; + break; + default: + rval = -1; + break; + } + + if (thisthread) + kpreempt_enable(); + return (rval); +} + +static int +lwp_getprivate(klwp_t *lwp, int which, uintptr_t base) +{ + pcb_t *pcb = &lwp->lwp_pcb; + struct regs *rp = lwptoregs(lwp); + uintptr_t sbase; + int error = 0; + + ASSERT(lwptot(lwp) == curthread); + + kpreempt_disable(); + switch (which) { + case _LWP_FSBASE: + if ((sbase = pcb->pcb_fsbase) != 0) { + if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { + if (PCB_NEED_UPDATE_SEGS(pcb)) { + if (pcb->pcb_fs == 0) + break; + } else { + if (rp->r_fs == 0) + break; + } + } else { + if (PCB_NEED_UPDATE_SEGS(pcb)) { + if (pcb->pcb_fs == LWPFS_SEL) + break; + } else { + if (rp->r_fs == LWPFS_SEL) + break; + } + } + } + error = EINVAL; + break; + case _LWP_GSBASE: + if ((sbase = pcb->pcb_gsbase) != 0) { + if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { + if (PCB_NEED_UPDATE_SEGS(pcb)) { + if (pcb->pcb_gs == 0) + break; + } else { + if (rp->r_gs == 0) + break; + } + } else { + if (PCB_NEED_UPDATE_SEGS(pcb)) { + if (pcb->pcb_gs == LWPGS_SEL) + break; + } else { + if (rp->r_gs == LWPGS_SEL) + break; + } + } + } + error = EINVAL; + break; + + + default: + error = ENOTSUP; + break; + } + kpreempt_enable(); + + if (error != 0) + return (error); + + if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { + if (sulword((void *)base, sbase) == -1) + error = EFAULT; +#if defined(_SYSCALL32_IMPL) + } else { + if (suword32((void *)base, (uint32_t)sbase) == -1) + error = EFAULT; +#endif + } + return (error); +} + +/* + * libc-private syscall for managing per-lwp %gs and %fs segment base values. + */ +int +syslwp_private(int cmd, int which, uintptr_t base) +{ + klwp_t *lwp = ttolwp(curthread); + int res, error; + + switch (cmd) { + case _LWP_SETPRIVATE: + res = lwp_setprivate(lwp, which, base); + return (res < 0 ? set_errno(ENOTSUP) : res); + case _LWP_GETPRIVATE: + error = lwp_getprivate(lwp, which, base); + return (error != 0 ? set_errno(error) : error); + default: + return (set_errno(ENOTSUP)); + } +} |