some software needs fully functional per-thread %gs base address, hence the diff. limited testing shows no regressions. INTR_RESTORE_SELECTORS changes include: - %r11 is used to store curpcb instead of rdx that is used for wrmsr and gets trashed (it's safe to use %r11 because it's restored after INTR_RESTORE_SELECTORS gets called);
- MSR_KERNELGSBASE needs to be updated instead of MSR_GSBASE so that we don't lose kernel gs.base stored in MSR_GSBASE and swapped by the swapgs. any objections? corrections? Index: sys/arch/amd64/amd64/cpu.c =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/amd64/cpu.c,v retrieving revision 1.47 diff -u -p -r1.47 cpu.c --- sys/arch/amd64/amd64/cpu.c 4 Apr 2012 18:44:22 -0000 1.47 +++ sys/arch/amd64/amd64/cpu.c 13 Apr 2012 15:26:51 -0000 @@ -682,6 +684,7 @@ cpu_init_msrs(struct cpu_info *ci) wrmsr(MSR_SFMASK, PSL_NT|PSL_T|PSL_I|PSL_C); ci->ci_cur_fsbase = 0; + ci->ci_cur_gsbase = (u_int64_t)ci; wrmsr(MSR_FSBASE, 0); wrmsr(MSR_GSBASE, (u_int64_t)ci); wrmsr(MSR_KERNELGSBASE, 0); Index: sys/arch/amd64/amd64/genassym.cf =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/amd64/genassym.cf,v retrieving revision 1.28 diff -u -p -r1.28 genassym.cf --- sys/arch/amd64/amd64/genassym.cf 13 Apr 2011 02:49:12 -0000 1.28 +++ sys/arch/amd64/amd64/genassym.cf 13 Apr 2012 15:25:26 -0000 @@ -86,6 +86,7 @@ member pcb_rsp member pcb_rbp member pcb_kstack member pcb_fsbase +member pcb_gsbase member pcb_onfault member pcb_fpcpu member pcb_pmap @@ -100,6 +101,7 @@ member tss_rsp0 struct cpu_info member CPU_INFO_SCRATCH ci_scratch member CPU_INFO_CUR_FSBASE ci_cur_fsbase +member CPU_INFO_CUR_GSBASE ci_cur_gsbase member CPU_INFO_SELF ci_self member CPU_INFO_CPUID ci_cpuid member CPU_INFO_APICID ci_apicid Index: sys/arch/amd64/amd64/machdep.c =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/amd64/machdep.c,v retrieving revision 1.152 diff -u -p -r1.152 machdep.c --- sys/arch/amd64/amd64/machdep.c 13 Jan 2012 12:55:52 -0000 1.152 +++ sys/arch/amd64/amd64/machdep.c 12 Apr 2012 16:41:18 -0000 @@ -363,7 +363,7 @@ x86_64_proc0_tss_ldt_init(void) cpu_info_primary.ci_curpcb = pcb = &proc0.p_addr->u_pcb; pcb->pcb_cr0 = rcr0(); - pcb->pcb_fsbase = 0; + pcb->pcb_fsbase = pcb->pcb_gsbase = 0; pcb->pcb_kstack = (u_int64_t)proc0.p_addr + USPACE - 16; proc0.p_md.md_regs = (struct trapframe *)pcb->pcb_kstack - 1; @@ -1022,6 +1022,7 @@ setregs(struct proc *p, struct exec_pack fpusave_proc(p, 0); p->p_md.md_flags &= ~MDP_USEDFPU; p->p_addr->u_pcb.pcb_fsbase = 0; + p->p_addr->u_pcb.pcb_gsbase = 0; tf = p->p_md.md_regs; tf->tf_ds = GSEL(GUDATA_SEL, SEL_UPL); Index: sys/arch/amd64/amd64/sys_machdep.c =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/amd64/sys_machdep.c,v retrieving revision 1.12 diff -u -p -r1.12 sys_machdep.c --- sys/arch/amd64/amd64/sys_machdep.c 13 Apr 2011 02:49:12 -0000 1.12 +++ sys/arch/amd64/amd64/sys_machdep.c 12 Apr 2012 16:37:30 -0000 @@ -159,6 +159,29 @@ amd64_set_fsbase(struct proc *p, void *a } int +amd64_get_gsbase(struct proc *p, void *args) +{ + return copyout(&p->p_addr->u_pcb.pcb_gsbase, args, + sizeof(p->p_addr->u_pcb.pcb_gsbase)); +} + +int +amd64_set_gsbase(struct proc *p, void *args) +{ + int error; + uint64_t base; + + if ((error = copyin(args, &base, sizeof(base))) != 0) + return (error); + + if (base >= VM_MAXUSER_ADDRESS) + return (EINVAL); + + p->p_addr->u_pcb.pcb_gsbase = base; + return 0; +} + +int sys_sysarch(struct proc *p, void *v, register_t *retval) { struct sys_sysarch_args /* { @@ -196,12 +219,20 @@ sys_sysarch(struct proc *p, void *v, reg break; #endif - case AMD64_GET_FSBASE: + case AMD64_GET_FSBASE: error = amd64_get_fsbase(p, SCARG(uap, parms)); break; - case AMD64_SET_FSBASE: + case AMD64_SET_FSBASE: error = amd64_set_fsbase(p, SCARG(uap, parms)); + break; + + case AMD64_GET_GSBASE: + error = amd64_get_gsbase(p, SCARG(uap, parms)); + break; + + case AMD64_SET_GSBASE: + error = amd64_set_gsbase(p, SCARG(uap, parms)); break; default: Index: sys/arch/amd64/include/cpu.h =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/include/cpu.h,v retrieving revision 1.72 diff -u -p -r1.72 cpu.h --- sys/arch/amd64/include/cpu.h 4 Apr 2012 18:44:22 -0000 1.72 +++ sys/arch/amd64/include/cpu.h 13 Apr 2012 15:25:13 -0000 @@ -73,6 +73,7 @@ struct cpu_info { u_int64_t ci_scratch; u_int64_t ci_cur_fsbase; + u_int64_t ci_cur_gsbase; struct proc *ci_fpcurproc; struct proc *ci_fpsaveproc; Index: sys/arch/amd64/include/frameasm.h =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/include/frameasm.h,v retrieving revision 1.6 diff -u -p -r1.6 frameasm.h --- sys/arch/amd64/include/frameasm.h 4 Jul 2011 15:54:24 -0000 1.6 +++ sys/arch/amd64/include/frameasm.h 13 Apr 2012 19:15:51 -0000 @@ -56,16 +56,16 @@ /* * Restore %ds, %es, %fs, and %gs, dealing with the FS.base MSR for - * %fs and doing the cli/swapgs for %gs. Uses %rax, %rcx, and %rdx + * %fs and doing the cli/swapgs for %gs. Uses %rax, %rcx, %rdx and %r11 */ #define INTR_RESTORE_SELECTORS \ - movq CPUVAR(CURPCB),%rdx /* for below */ ; \ + movq CPUVAR(CURPCB),%r11 /* for below */ ; \ /* %es and %ds */ \ movw TF_ES(%rsp),%es ; \ movw $(GSEL(GUDATA_SEL, SEL_UPL)),%ax ; \ movw %ax,%ds ; \ /* Make sure both %fs and FS.base are the desired values */ \ - movq PCB_FSBASE(%rdx),%rax ; \ + movq PCB_FSBASE(%r11),%rax ; \ cmpq $0,%rax ; \ jne 96f ; \ movw TF_FS(%rsp),%fs /* zero FS.base by setting %fs */ ; \ @@ -81,7 +81,20 @@ movl $MSR_FSBASE,%ecx ; \ wrmsr ; \ 98: movq %rax,CPUVAR(CUR_FSBASE) ; \ -99: cli /* %fs done, so swapgs and do %gs */ ; \ +99: /* Make sure GS.base is reset */ ; \ + movq PCB_GSBASE(%r11),%rax ; \ + cmpq $0,%rax ; \ + jne 100f ; \ + movq %rax,CPUVAR(CUR_GSBASE) ; \ + jmp 101f ; \ +100: cmpq CPUVAR(CUR_GSBASE),%rax ; \ + je 101f ; \ + movq %rax,CPUVAR(CUR_GSBASE) ; \ + movq %rax,%rdx ; \ + shrq $32,%rdx ; \ + movl $MSR_KERNELGSBASE,%ecx ; \ + wrmsr ; \ +101: cli /* %gs done, do swapgs */ ; \ swapgs ; \ movw TF_GS(%rsp),%gs Index: sys/arch/amd64/include/pcb.h =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/include/pcb.h,v retrieving revision 1.12 diff -u -p -r1.12 pcb.h --- sys/arch/amd64/include/pcb.h 10 Jul 2011 18:12:03 -0000 1.12 +++ sys/arch/amd64/include/pcb.h 12 Apr 2012 17:28:16 -0000 @@ -83,18 +83,19 @@ struct pcb { u_int64_t pcb_rbp; u_int64_t pcb_kstack; /* kernel stack address */ u_int64_t pcb_fsbase; /* per-thread offset: %fs */ + u_int64_t pcb_gsbase; /* per-thread offset: %gs */ caddr_t pcb_onfault; /* copyin/out fault recovery */ struct cpu_info *pcb_fpcpu; /* cpu holding our fp state. */ struct pmap *pcb_pmap; /* back pointer to our pmap */ int pcb_cr0; /* saved image of CR0 */ }; -/* - * The pcb is augmented with machine-dependent additional data for +/* + * The pcb is augmented with machine-dependent additional data for * core dumps. For the i386, there is nothing to add. - */ + */ struct md_coredump { long md_pad[8]; -}; +}; #endif /* _MACHINE_PCB_H_ */ Index: sys/arch/amd64/include/sysarch.h =================================================================== RCS file: /home/cvs/src/sys/arch/amd64/include/sysarch.h,v retrieving revision 1.10 diff -u -p -r1.10 sysarch.h --- sys/arch/amd64/include/sysarch.h 13 Apr 2011 02:49:12 -0000 1.10 +++ sys/arch/amd64/include/sysarch.h 12 Apr 2012 20:57:34 -0000 @@ -16,6 +16,8 @@ #define AMD64_PMC_READ 10 #define AMD64_GET_FSBASE 11 #define AMD64_SET_FSBASE 12 +#define AMD64_GET_GSBASE 13 +#define AMD64_SET_GSBASE 14 struct amd64_iopl_args { int iopl; @@ -67,6 +69,8 @@ struct amd64_pmc_read_args { int amd64_iopl(struct proc *, void *, register_t *); int amd64_set_fsbase(struct proc *, void *); int amd64_get_fsbase(struct proc *, void *); +int amd64_set_gsbase(struct proc *, void *); +int amd64_get_gsbase(struct proc *, void *); #else int amd64_iopl(int); int amd64_get_ioperm(u_long *); @@ -76,6 +80,8 @@ int amd64_pmc_startstop(struct amd64_pmc int amd64_pmc_read(struct amd64_pmc_read_args *); int amd64_set_fsbase(void *); int amd64_get_fsbase(void **); +int amd64_set_gsbase(void *); +int amd64_get_gsbase(void **); int sysarch(int, void *); #endif Index: lib/libarch/amd64/Makefile =================================================================== RCS file: /home/cvs/src/lib/libarch/amd64/Makefile,v retrieving revision 1.10 diff -u -p -r1.10 Makefile --- lib/libarch/amd64/Makefile 13 Apr 2011 02:49:12 -0000 1.10 +++ lib/libarch/amd64/Makefile 13 Apr 2012 19:20:17 -0000 @@ -3,15 +3,17 @@ MANSUBDIR=amd64 MAN+= amd64_iopl.2 amd64_get_ioperm.2 \ - amd64_get_fsbase.2 + amd64_get_fsbase.2 amd64_get_gsbase.2 MLINKS+=amd64_get_ioperm.2 amd64_set_ioperm.2 \ - amd64_get_fsbase.2 amd64_set_fsbase.2 + amd64_get_fsbase.2 amd64_set_fsbase.2 \ + amd64_get_gsbase.2 amd64_set_gsbase.2 .if ${MACHINE_ARCH} == "amd64" .PATH: ${LIBC}/amd64 NOPIC= SRCS+= amd64_iopl.c amd64_get_ioperm.c amd64_set_ioperm.c \ - amd64_get_fsbase.c amd64_set_fsbase.c + amd64_get_fsbase.c amd64_set_fsbase.c \ + amd64_get_gsbase.c amd64_set_gsbase.c .include <bsd.lib.mk> .else NOPROG= Index: lib/libarch/amd64/amd64_get_gsbase.2 =================================================================== RCS file: lib/libarch/amd64/amd64_get_gsbase.2 diff -N lib/libarch/amd64/amd64_get_gsbase.2 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libarch/amd64/amd64_get_gsbase.2 13 Apr 2012 19:24:18 -0000 @@ -0,0 +1,87 @@ +.\" $OpenBSD$ +.\" $NetBSD: i386_get_ioperm.2,v 1.3 1996/02/27 22:57:17 jtc Exp $ +.\" +.\" Copyright (c) 1996 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by John T. Kohl and Charles M. Hannum. +.\" +.\" 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 NETBSD FOUNDATION, INC. 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. +.\" +.Dd $Mdocdate$ +.Dt AMD64_GET_GSBASE 2 amd64 +.Os +.Sh NAME +.Nm amd64_get_gsbase , +.Nm amd64_set_gsbase +.Nd manage amd64 per-thread %gs base address +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <machine/sysarch.h> +.Ft int +.Fn amd64_get_gsbase "void **base" +.Ft int +.Fn amd64_set_gsbase "void *base" +.Sh DESCRIPTION +.Fn amd64_get_gsbase +copies the current base address of the %gs segment into the memory +referenced by +.Fa base . +.Pp +.Fn amd64_set_gsbase +sets the base address of the %gs segment to the address +.Fa base . +.Pp +The segment base address is local to each thread. +The initial thread of a new process inherits its segment base address +from the parent thread. +.Xr __tfork 2 +sets the initial segment base address for threads that it creates. +.Pp +.Sy Note: +Code using the +.Fn amd64_get_gsbase +and +.Fn amd64_set_gsbase +functions must be compiled using +.Cm -lamd64 . +.Sh RETURN VALUES +Upon successful completion, +.Fn amd64_get_gsbase +and +.Fn amd64_set_gsbase +return 0. +Otherwise, a value of \-1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Fn amd64_get_gsbase +will fail if: +.Bl -tag -width [EINVAL] +.It Bq Er EFAULT +.Fa base +points outside the process's allocated address space. +.El +.Sh SEE ALSO +.Xr __tfork 2 , +.Xr fork 2 Index: lib/libarch/amd64/amd64_get_gsbase.c =================================================================== RCS file: lib/libarch/amd64/amd64_get_gsbase.c diff -N lib/libarch/amd64/amd64_get_gsbase.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libarch/amd64/amd64_get_gsbase.c 13 Apr 2012 19:28:32 -0000 @@ -0,0 +1,15 @@ +/* $OpenBSD$ */ + +/* Public domain. Written by Mike Belopuhov <mi...@openbsd.org> */ + +#include <sys/cdefs.h> +#include <sys/types.h> + +#include <machine/segments.h> +#include <machine/sysarch.h> + +int +amd64_get_gsbase(void **base) +{ + return sysarch(AMD64_GET_GSBASE, base); +} Index: lib/libarch/amd64/amd64_set_gsbase.c =================================================================== RCS file: lib/libarch/amd64/amd64_set_gsbase.c diff -N lib/libarch/amd64/amd64_set_gsbase.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libarch/amd64/amd64_set_gsbase.c 13 Apr 2012 19:28:19 -0000 @@ -0,0 +1,15 @@ +/* $OpenBSD$ */ + +/* Public domain. Written by Mike Belopuhov <mi...@openbsd.org> */ + +#include <sys/cdefs.h> +#include <sys/types.h> + +#include <machine/segments.h> +#include <machine/sysarch.h> + +int +amd64_set_gsbase(void *base) +{ + return sysarch(AMD64_SET_GSBASE, &base); +} Index: lib/libpthread/Makefile =================================================================== RCS file: /home/cvs/src/regress/lib/libpthread/Makefile,v retrieving revision 1.38 diff -u -p -r1.38 Makefile --- lib/libpthread/Makefile 13 Apr 2012 19:04:09 -0000 1.38 +++ lib/libpthread/Makefile 14 Apr 2012 10:27:30 -0000 @@ -12,6 +12,9 @@ SUBDIR= barrier blocked_close blocked_du readdir restart select semaphore setjmp setsockopt sigdeliver siginfo \ siginterrupt signal signals signodefer sigsuspend sigwait sleep \ socket stack stdarg stdio switch system +.if ${MACHINE_ARCH} == "amd64" +SUBDIR+=amd64_set_gsbase +.endif # Not available or disabled: fcntl, getaddrinfo, pause, pw, sigmask, stdfiles Index: lib/libpthread/amd64_set_gsbase/Makefile =================================================================== RCS file: lib/libpthread/amd64_set_gsbase/Makefile diff -N lib/libpthread/amd64_set_gsbase/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libpthread/amd64_set_gsbase/Makefile 14 Apr 2012 10:54:33 -0000 @@ -0,0 +1,10 @@ +# $OpenBSD$ + +PROG= amd64_set_gsbase + +LIBARCH= /usr/lib/libamd64.a + +LDADD+= ${LIBARCH} +DPADD+= ${LIBARCH} + +.include <bsd.regress.mk> Index: lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c =================================================================== RCS file: lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c diff -N lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c 14 Apr 2012 11:25:53 -0000 @@ -0,0 +1,52 @@ +/* $OpenBSD$ */ + +/* + * Public domain. Written by Mike Belopuhov. + * + * Test amd64_set_gsbase and amd64_get_gsbase + */ + +#include <sys/types.h> +#include <machine/sysarch.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "test.h" + +#define NTHREADS 128 + +void * +new_thread(void *arg) +{ + int *val = NULL; + + amd64_set_gsbase(arg); + + pthread_yield(); + sleep(arc4random_uniform(10)); + pthread_yield(); + + amd64_get_gsbase((void **)&val); + + ASSERT(val == arg); + + return (NULL); +} + +int +main(int argc, char *argv[]) +{ + pthread_t threads[NTHREADS]; + int vals[NTHREADS]; + int i; + + for (i = 0; i < NTHREADS; i++) { + CHECKr(pthread_create(&threads[i], NULL, new_thread, + (void *)&vals[i])); + } + for (i = 0; i < NTHREADS; i++) { + CHECKr(pthread_join(threads[i], NULL)); + } + SUCCEED; +}