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;
+}

Reply via email to