On Sat, Apr 14, 2012 at 13:35 +0200, Mike Belopuhov wrote:
> 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?
> 

updated and improved version of the above:

 - don't use ci_cur_gsbase to avoid the problem described by guenther
   in the fs.base diff;
 - set %gs before gs.base in order to preserve both segment value
   and the override;
 - change the testcase to check the GS.base value (%gs:0) as well;

ok?

Index: sys/arch/amd64/amd64/genassym.cf
===================================================================
RCS file: /home/cvs/src/sys/arch/amd64/amd64/genassym.cf,v
retrieving revision 1.29
diff -u -p -r1.29 genassym.cf
--- sys/arch/amd64/amd64/genassym.cf    17 Apr 2012 16:02:33 -0000      1.29
+++ sys/arch/amd64/amd64/genassym.cf    18 Apr 2012 09:30:46 -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
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/frameasm.h
===================================================================
RCS file: /home/cvs/src/sys/arch/amd64/include/frameasm.h,v
retrieving revision 1.7
diff -u -p -r1.7 frameasm.h
--- sys/arch/amd64/include/frameasm.h   17 Apr 2012 16:02:33 -0000      1.7
+++ sys/arch/amd64/include/frameasm.h   19 Apr 2012 15:40:44 -0000
@@ -55,27 +55,37 @@
        pushq   %r13                    ;
 
 /*
- * 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
+ * Restore %ds, %es, %fs, and %gs, dealing with the FS.base MSR for %fs,
+ * GS.base MSR and 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 */       \
        movw    TF_FS(%rsp),%fs                                         ; \
-       movq    PCB_FSBASE(%rdx),%rax                                   ; \
+       movq    PCB_FSBASE(%r11),%rax                                   ; \
        cmpq    $0,%rax                                                 ; \
-       je      99f             /* setting %fs has zeroed FS.base */    ; \
+       je      96f                                                     ; \
        movq    %rax,%rdx                                               ; \
        shrq    $32,%rdx                                                ; \
        movl    $MSR_FSBASE,%ecx                                        ; \
        wrmsr                                                           ; \
-99:    cli             /* %fs done, so swapgs and do %gs */            ; \
+96:    cli                                                             ; \
        swapgs                                                          ; \
-       movw    TF_GS(%rsp),%gs
+       /* Make sure both %gs and GS.base are the desired values */       \
+       movw    TF_GS(%rsp),%gs                                         ; \
+       movq    PCB_GSBASE(%r11),%rax                                   ; \
+       cmpq    $0,%rax                                                 ; \
+       je      97f                                                     ; \
+       movq    %rax,%rdx                                               ; \
+       shrq    $32,%rdx                                                ; \
+       movl    $MSR_GSBASE,%ecx                                        ; \
+       wrmsr                                                           ; \
+97:    /* out */
 
 
 #define CHECK_ASTPENDING(reg)  movq    CPUVAR(CURPROC),reg             ; \
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: regress/lib/libpthread/Makefile
===================================================================
RCS file: /home/cvs/src/regress/lib/libpthread/Makefile,v
retrieving revision 1.38
diff -u -p -r1.38 Makefile
--- regress/lib/libpthread/Makefile     13 Apr 2012 19:04:09 -0000      1.38
+++ regress/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: regress/lib/libpthread/amd64_set_gsbase/Makefile
===================================================================
RCS file: regress/lib/libpthread/amd64_set_gsbase/Makefile
diff -N regress/lib/libpthread/amd64_set_gsbase/Makefile
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/lib/libpthread/amd64_set_gsbase/Makefile    18 Apr 2012 10:27:18 
-0000
@@ -0,0 +1,12 @@
+# $OpenBSD$
+
+PROG=  amd64_set_gsbase
+
+LIBARCH=/usr/lib/libamd64.a
+
+LDADD+=        ${LIBARCH}
+DPADD+=        ${LIBARCH}
+
+CFLAGS+=-O0 -g
+
+.include <bsd.regress.mk>
Index: regress/lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c
===================================================================
RCS file: regress/lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c
diff -N regress/lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/lib/libpthread/amd64_set_gsbase/amd64_set_gsbase.c  19 Apr 2012 
11:46:07 -0000
@@ -0,0 +1,55 @@
+/*     $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)
+{
+       long gsbase = 0;
+       int *val = NULL;
+
+       CHECKr(amd64_set_gsbase(arg));
+
+       pthread_yield();
+
+       CHECKr(amd64_get_gsbase((void **)&val));
+
+       __asm __volatile("movq %%gs:0,%0" : "=r" (gsbase));
+
+       ASSERT(val == arg);
+       ASSERT((long)val == gsbase);
+
+       return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+       pthread_t threads[NTHREADS];
+       int *vals[NTHREADS];
+       int i;
+
+       for (i = 0; i < NTHREADS; i++) {
+               vals[i] = (int *)&vals[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