From 806bf111d28cfaddca0cf1d7ee1a7e685dd33866 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 21 Feb 2005 21:41:01 +0000 Subject: [PATCH] Implement TLS support, tls manual pages, and link the umtx and tls manual pages together. TLS stands for 'thread local storage' and is used to support efficient userland threading and threaded data access models. Three TLS segments are supported in order to (eventually) support GCC3's __thread qualifier. David Xu's thread library only uses one descriptor for now. The system calls implement a mostly machine-independant API which return architecture-specific results. Rather then pass the actual descriptor structure, which unnecessarily pollutes the userland implementation, we pass a more generic (base,size) and the system call returns the %gs load value for IA32. For AMD64 and other architectures, the returned value will be something for those architectures. The current low level assembly support is not as efficient as it could be, but it is good enough for now. The heavy weight switch code for processes does the work. The light weight switch code for pure kernel threads has not been changed (since the kernel doesn't use TLS descriptors we can just ignore them). Based on work by David Xu and Matthew Dillon --- lib/libc/sys/Makefile.inc | 6 +- lib/libc/sys/tls.2 | 170 ++++++++++++++++++++++++ lib/libc/sys/umtx.2 | 4 +- sys/conf/files.i386 | 3 +- sys/cpu/i386/include/segments.h | 9 +- sys/i386/i386/machdep.c | 29 ++++- sys/i386/i386/swtch.s | 8 +- sys/i386/i386/sys_machdep.c | 95 ++++++++++---- sys/i386/i386/tls.c | 186 +++++++++++++++++++++++++++ sys/i386/i386/vm_machdep.c | 5 +- sys/i386/include/pcb_ext.h | 3 +- sys/i386/include/segments.h | 9 +- sys/i386/include/thread.h | 6 +- sys/platform/pc32/i386/machdep.c | 29 ++++- sys/platform/pc32/i386/swtch.s | 8 +- sys/platform/pc32/i386/sys_machdep.c | 95 ++++++++++---- sys/platform/pc32/i386/tls.c | 186 +++++++++++++++++++++++++++ sys/platform/pc32/i386/vm_machdep.c | 5 +- sys/platform/pc32/include/pcb_ext.h | 3 +- sys/platform/pc32/include/thread.h | 6 +- sys/sys/syscall-args | 4 +- sys/sys/syscall-hide.h | 4 +- sys/sys/syscall.h | 6 +- sys/sys/syscall.mk | 6 +- sys/sys/sysproto.h | 22 +++- sys/sys/sysunion.h | 4 +- sys/sys/tls.h | 17 +++ 27 files changed, 842 insertions(+), 86 deletions(-) create mode 100644 lib/libc/sys/tls.2 create mode 100644 sys/i386/i386/tls.c create mode 100644 sys/platform/pc32/i386/tls.c create mode 100644 sys/sys/tls.h diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 3fd4dd35a3..3f3f4e5fd2 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -1,6 +1,6 @@ # @(#)Makefile.inc 8.3 (Berkeley) 10/24/94 # $FreeBSD: src/lib/libc/sys/Makefile.inc,v 1.75.2.7 2003/04/22 17:31:18 trhodes Exp $ -# $DragonFly: src/lib/libc/sys/Makefile.inc,v 1.9 2005/02/21 19:53:34 dillon Exp $ +# $DragonFly: src/lib/libc/sys/Makefile.inc,v 1.10 2005/02/21 21:41:01 dillon Exp $ # sys sources .PATH: ${.CURDIR}/../libc/${MACHINE_ARCH}/sys ${.CURDIR}/../libc/sys @@ -105,7 +105,7 @@ MAN+= _exit.2 accept.2 access.2 acct.2 adjtime.2 \ sigaction.2 sigaltstack.2 sigpending.2 sigprocmask.2 sigreturn.2 \ sigstack.2 sigsuspend.2 socket.2 socketpair.2 stat.2 statfs.2 \ swapon.2 symlink.2 sync.2 sysarch.2 syscall.2 \ - truncate.2 umask.2 umtx.2 undelete.2 \ + truncate.2 tls.2 umask.2 umtx.2 undelete.2 \ unlink.2 utimes.2 upc_register.2 vfork.2 wait.2 write.2 .if !defined(NO_P1003_1B) MAN+= sched_get_priority_max.2 sched_setparam.2 \ @@ -158,5 +158,7 @@ MLINKS+=sched_get_priority_max.2 sched_get_priority_min.2 \ sched_get_priority_max.2 sched_rr_get_interval.2 MLINKS+=sched_setparam.2 sched_getparam.2 MLINKS+=sched_setscheduler.2 sched_getscheduler.2 +MLINKS+=tls.2 sys_set_tls_area.2 tls.2 sys_get_tls_area.2 +MLINKS+=umtx.2 umtx_sleep.2 umtx.2 umtx_wakeup.2 .endif .endif diff --git a/lib/libc/sys/tls.2 b/lib/libc/sys/tls.2 new file mode 100644 index 0000000000..7890720cf7 --- /dev/null +++ b/lib/libc/sys/tls.2 @@ -0,0 +1,170 @@ +.\" Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. +.\" +.\" This code is derived from software contributed to The DragonFly Project +.\" by David Xu and Matthew Dillon +.\" +.\" 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. Neither the name of The DragonFly Project 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 COPYRIGHT HOLDERS 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 +.\" COPYRIGHT HOLDERS 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. +.\" +.\" $DragonFly: src/lib/libc/sys/tls.2,v 1.1 2005/02/21 21:41:01 dillon Exp $ +.\" +.Dd February 21, 2005 +.Dt TLS 2 +.Os +.Sh NAME +.Nm sys_set_tls_area , +.Nm sys_get_tls_area +.Nd kernel TLS (thread local storage) support. +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/tls.h +.Ft int +.Fn sys_set_tls_area "int which" "struct tls_info *info" "int infosize" +.Ft int +.Fn sys_get_tls_area "int which" "struct tls_info *info" "int infosize" +.Sh DESCRIPTION +The +.Fn sys_set_tls_area +system call creates an entry for the TLS facility +.Fa which +representing thread local storage as specified by the +.Fa info +structure. A descriptor representing the facility is returned, or -1 if +an error occured. The facility may be cleared by specifying a NULL pointer +and an infosize of 0. +The +.Fn sys_get_tls_area +system call retrieves the requested TLS facility. A descriptor representing +the facility is returned, or -1 if an error occured. If you simply want the +descriptor you may specify a NULL pointer and an infosize of 0. +.Pp +The returned descriptor and the TLS mechanism is machine-dependant. On IA32 +three global segment descriptors are supported (0, 1, and 2) and the %gs +load value is returned. +.Pp +The +.Fa tls_info +structure passed to +.Fn sys_set_tls_area +should first be zerod (to remain compatible with future extensions) +and then initialized. +.Pp +.Bd -literal +struct tls_info { + void *base; /* base address of TLS area */ + int size; /* size of TLS area in bytes */ +}; +.Ed +.Pp +The actual implementation of the area is machine-dependant. If the kernel +is unable to accomodate the supplied size it may create a larger area. +If the kernel is unable to accomodate the supplied base address an error +will be returned. + +.Sh RETURN VALUES +A return value of 0 is returned on success, -1 on error. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er ERANGE +The specified facility index, +.Fa which , +is not supported. +.It Bq Er EINVAL +An invalid parameter has been specified. +.It Bq Er ENOENT +(sys_get_tls_area) The specified facility has not been initialized with +.Fn sys_set_tls_area . +.Sh EXAMPLE +.Bd -literal -compact + +/* + * Pseudo example showing how the TLS system calls work on IA32. + */ +#include +#include +#include +#include +#include + +int X; + +static int getdata(int offset); + +int +main(int ac, char **av) +{ + int i; + int gs; + struct tls_info info; + + info.base = &X; + info.size = sizeof(X); + if ((gs = sys_set_tls_area(0, &info, sizeof(info))) < 0) { + perror("setarea"); + exit(1); + } + printf("gs = %04x\n", gs); + __asm __volatile("movl %0,%%eax; movl %%eax,%%gs" : "=m" (gs) : : "ax"); + + if (sys_get_tls_area(0, &info, sizeof(info)) < 0) { + perror("getarea"); + exit(1); + } + printf("%p/%d\n", info.base, info.size); + + X = 1; + printf("should be 1: %d\n", getdata(0)); + X = 2; + printf("should be 2: %d\n", getdata(0)); + + printf("this should fault:\n"); + fflush(stdout); + getdata(4); + + return(0); +} + +static int +getdata(int offset) +{ + int rv; + __asm __volatile("movl %%gs:(%0),%%eax; movl %%eax,%1" : "+r" (offset) : "m" + (rv) : "ax"); + return (rv); +} + +.Ed + +.Sh SEE ALSO +.Xr umtx 2 +.Sh HISTORY +The +.Fn sys_set_tls_area , +and +.Fn sys_get_tls_area +function calls first appeared in DragonFly 1.1 . diff --git a/lib/libc/sys/umtx.2 b/lib/libc/sys/umtx.2 index e766729ddd..3e94500736 100644 --- a/lib/libc/sys/umtx.2 +++ b/lib/libc/sys/umtx.2 @@ -30,7 +30,7 @@ .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $DragonFly: src/lib/libc/sys/umtx.2,v 1.1 2005/02/21 19:53:34 dillon Exp $ +.\" $DragonFly: src/lib/libc/sys/umtx.2,v 1.2 2005/02/21 21:41:01 dillon Exp $ .\" .Dd February 21, 2005 .Dt UMTX 2 @@ -201,6 +201,8 @@ userland_rel_mutex(struct umtx *mtx) .Ed .Sh SEE ALSO +.Xr tls 2 + .Sh HISTORY The .Fn umtx_sleep , diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 6621f3affe..b061f06377 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -2,7 +2,7 @@ # files marked standard are always included. # # $FreeBSD: src/sys/conf/files.i386,v 1.307.2.38 2003/01/02 20:41:33 kan Exp $ -# $DragonFly: src/sys/conf/Attic/files.i386,v 1.27 2005/01/31 23:44:34 joerg Exp $ +# $DragonFly: src/sys/conf/Attic/files.i386,v 1.28 2005/02/21 21:40:47 dillon Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -166,6 +166,7 @@ i386/i386/identcpu.c standard i386/i386/in_cksum2.s optional inet i386/i386/initcpu.c standard i386/i386/k6_mem.c standard +i386/i386/tls.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard diff --git a/sys/cpu/i386/include/segments.h b/sys/cpu/i386/include/segments.h index b91d7bdb8e..81ea2dd835 100644 --- a/sys/cpu/i386/include/segments.h +++ b/sys/cpu/i386/include/segments.h @@ -36,7 +36,7 @@ * * from: @(#)segments.h 7.1 (Berkeley) 5/9/91 * $FreeBSD: src/sys/i386/include/segments.h,v 1.24 1999/12/29 04:33:07 peter Exp $ - * $DragonFly: src/sys/cpu/i386/include/segments.h,v 1.7 2004/08/12 19:59:30 eirikn Exp $ + * $DragonFly: src/sys/cpu/i386/include/segments.h,v 1.8 2005/02/21 21:40:55 dillon Exp $ */ #ifndef _MACHINE_SEGMENTS_H_ @@ -220,11 +220,14 @@ struct region_descriptor { #define GBIOSDATA_SEL 12 /* BIOS interface (Data) */ #define GBIOSUTIL_SEL 13 /* BIOS interface (Utility) */ #define GBIOSARGS_SEL 14 /* BIOS interface (Arguments) */ +#define GTLS_START 15 /* Thread TLS Descriptor */ +#define GTLS_END 17 /* Thread TLS Descriptor */ +#define NGTLS (GTLS_END - GTLS_START + 1) #ifdef BDE_DEBUGGER -#define NGDT 18 /* some of 11-17 are reserved for debugger */ +#define NGDT 21 /* some of 11-17 are reserved for debugger */ #else -#define NGDT 15 +#define NGDT 18 #endif /* diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 149e3c7336..d19b0e81e8 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -36,7 +36,7 @@ * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $ - * $DragonFly: src/sys/i386/i386/Attic/machdep.c,v 1.69 2005/02/07 20:38:58 dillon Exp $ + * $DragonFly: src/sys/i386/i386/Attic/machdep.c,v 1.70 2005/02/21 21:40:53 dillon Exp $ */ #include "use_apm.h" @@ -1175,6 +1175,33 @@ struct soft_segment_descriptor gdt_segs[] = { 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, +/* GTLS_START 15 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, +/* GTLS_START+1 16 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, +/* GTLS_END 17 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { diff --git a/sys/i386/i386/swtch.s b/sys/i386/i386/swtch.s index 9ecf49d49a..bab9998b42 100644 --- a/sys/i386/i386/swtch.s +++ b/sys/i386/i386/swtch.s @@ -66,7 +66,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $ - * $DragonFly: src/sys/i386/i386/Attic/swtch.s,v 1.36 2004/12/20 09:21:18 dillon Exp $ + * $DragonFly: src/sys/i386/i386/Attic/swtch.s,v 1.37 2005/02/21 21:40:53 dillon Exp $ */ #include "use_npx.h" @@ -374,6 +374,12 @@ ENTRY(cpu_heavy_restore) call set_user_ldt popl %edx 2: + /* + * Restore the user TLS if we have one + */ + pushl %edx + call set_user_TLS + popl %edx /* * Restore the %gs segment register, which must be done after * loading the user LDT. Since user processes can modify the diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c index 303d523363..3f468679b6 100644 --- a/sys/i386/i386/sys_machdep.c +++ b/sys/i386/i386/sys_machdep.c @@ -32,7 +32,7 @@ * * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 * $FreeBSD: src/sys/i386/i386/sys_machdep.c,v 1.47.2.3 2002/10/07 17:20:00 jhb Exp $ - * $DragonFly: src/sys/i386/i386/Attic/sys_machdep.c,v 1.15 2004/12/20 13:58:02 joerg Exp $ + * $DragonFly: src/sys/i386/i386/Attic/sys_machdep.c,v 1.16 2005/02/21 21:40:53 dillon Exp $ * */ @@ -75,6 +75,7 @@ static int i386_get_ldt (struct proc *, char *, int *); static int i386_set_ldt (struct proc *, char *, int *); static int i386_get_ioperm (struct proc *, char *); static int i386_set_ioperm (struct proc *, char *); +static int check_descs(union descriptor *, int); int i386_extend_pcb (struct proc *); /* @@ -91,7 +92,6 @@ sysarch(struct sysarch_args *uap) case I386_GET_LDT: error = i386_get_ldt(p, uap->parms, &uap->sysmsg_result); break; - case I386_SET_LDT: error = i386_set_ldt(p, uap->parms, &uap->sysmsg_result); break; @@ -233,9 +233,32 @@ done: return (error); } +/* + * Update the TLS entries for the process. Used by assembly, do not staticize. + * + * Must be called from a critical section (else an interrupt thread preemption + * may cause %gs to fault). Normally called from the low level swtch.s code. + */ +void +set_user_TLS(void) +{ + struct thread *td = curthread; + int i; +#ifdef SMP + int off = GTLS_START + mycpu->gd_cpuid * NGDT; +#else + const int off = GTLS_START; +#endif + for (i = 0; i < NGTLS; ++i) + gdt[off + i].sd = td->td_tls[i]; +} + /* * Update the GDT entry pointing to the LDT to point to the LDT of the - * current process. Do not staticize. + * current process. Used by assembly, do not staticize. + * + * Must be called from a critical section (else an interrupt thread preemption + * may cause %gs to fault). Normally called from the low level swtch.s code. */ void set_user_ldt(struct pcb *pcb) @@ -358,12 +381,12 @@ i386_get_ldt(struct proc *p, char *args, int *res) static int i386_set_ldt(struct proc *p, char *args, int *res) { - int error = 0, i, n; + int error = 0; int largest_ld; struct pcb *pcb = p->p_thread->td_pcb; struct pcb_ldt *pcb_ldt = pcb->pcb_ldt; union descriptor *descs; - int descs_size, s; + int descs_size; struct i386_ldt_args ua, *uap = &ua; if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0) @@ -396,10 +419,15 @@ i386_set_ldt(struct proc *p, char *args, int *res) pcb_ldt->ldt_base = new_ldt->ldt_base; pcb_ldt->ldt_len = new_ldt->ldt_len; FREE(new_ldt, M_SUBPROC); - } else + } else { pcb->pcb_ldt = pcb_ldt = new_ldt; + } + /* + * Since the LDT may be shared, we must signal other cpus to + * reload it. XXX we need to track which cpus might be + * using the shared ldt and only signal those. + */ #ifdef SMP - /* signal other cpus to reload ldt */ smp_rendezvous(NULL, (void (*)(void *))set_user_ldt, NULL, pcb); #else set_user_ldt(pcb); @@ -416,7 +444,36 @@ i386_set_ldt(struct proc *p, char *args, int *res) return (error); } /* Check descriptors for access violations */ - for (i = 0, n = uap->start; i < uap->num; i++, n++) { + error = check_descs(descs, uap->num); + if (error) { + kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + return (error); + } + + /* + * Fill in the actual ldt entries. Since %fs might point to one of + * these entries a critical section is required to prevent an + * interrupt thread from preempting us, switch back, and faulting + * on the load of %fs due to a half-formed descriptor. + */ + crit_enter(); + bcopy(descs, + &((union descriptor *)(pcb_ldt->ldt_base))[uap->start], + uap->num * sizeof(union descriptor)); + *res = uap->start; + + crit_exit(); + kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + return (0); +} + +static int +check_descs(union descriptor *descs, int num) +{ + int i; + + /* Check descriptors for access violations */ + for (i = 0; i < num; i++) { union descriptor *dp; dp = &descs[i]; @@ -443,7 +500,6 @@ i386_set_ldt(struct proc *p, char *args, int *res) * to create a segment of these types. They are * for OS use only. */ - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); return EACCES; /* memory segment types */ @@ -452,11 +508,8 @@ i386_set_ldt(struct proc *p, char *args, int *res) case SDT_MEMERC: /* memory execute read conforming */ case SDT_MEMERAC: /* memory execute read accessed conforming */ /* Must be "present" if executable and conforming. */ - if (dp->sd.sd_p == 0) { - kmem_free(kernel_map, (vm_offset_t)descs, - descs_size); + if (dp->sd.sd_p == 0) return (EACCES); - } break; case SDT_MEMRO: /* memory read only */ case SDT_MEMROA: /* memory read only accessed */ @@ -472,27 +525,13 @@ i386_set_ldt(struct proc *p, char *args, int *res) case SDT_MEMERA: /* memory execute read accessed */ break; default: - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); return(EINVAL); /*NOTREACHED*/ } /* Only user (ring-3) descriptors may be present. */ - if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) { - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) return (EACCES); - } } - - s = splhigh(); - - /* Fill in range */ - bcopy(descs, - &((union descriptor *)(pcb_ldt->ldt_base))[uap->start], - uap->num * sizeof(union descriptor)); - *res = uap->start; - - splx(s); - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); return (0); } diff --git a/sys/i386/i386/tls.c b/sys/i386/i386/tls.c new file mode 100644 index 0000000000..61ae12b4da --- /dev/null +++ b/sys/i386/i386/tls.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by David Xu and Matthew Dillon + * + * 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. Neither the name of The DragonFly Project 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/i386/i386/Attic/tls.c,v 1.1 2005/02/21 21:40:53 dillon Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include /* pcb.h included via sys/user.h */ +#include /* CPU_prvspace */ +#ifdef SMP +#include +#endif + +/* + * set a TLS descriptor and resync the GDT. A descriptor may be cleared + * by passing info=NULL and infosize=0. Note that hardware limitations may + * cause the size passed in tls_info to be approximated. + * + * Returns the value userland needs to load into %gs representing the + * TLS descriptor or -1 on error. + * + * (struct tls_info *info, int infosize, int which) + */ +int +sys_set_tls_area(struct sys_set_tls_area_args *uap) +{ + struct tls_info info; + struct segment_descriptor *desc; + int error; + int i; + + /* + * Sanity checks + */ + i = uap->which; + if (i < 0 || i >= NGTLS) + return (ERANGE); + if (uap->infosize < 0) + return (EINVAL); + + /* + * Maintain forwards compatibility with future extensions. + */ + if (uap->infosize != sizeof(info)) { + bzero(&info, sizeof(info)); + error = copyin(uap->info, &info, + min(sizeof(info), uap->infosize)); + } else { + error = copyin(uap->info, &info, sizeof(info)); + } + if (error) + return (error); + if (info.size < 0) + return (EINVAL); + if (info.size > (1 << 20)) + info.size = (info.size + PAGE_MASK) & ~PAGE_MASK; + + /* + * Load the descriptor. A critical section is required in case + * an interrupt thread comes along and switches us out and then back + * in. + */ + desc = &curthread->td_tls[i]; + crit_enter(); + if (info.size == 0) { + bzero(desc, sizeof(*desc)); + } else { + desc->sd_lobase = (intptr_t)info.base; + desc->sd_hibase = (intptr_t)info.base >> 24; + desc->sd_def32 = 1; + desc->sd_type = SDT_MEMRWA; + desc->sd_dpl = SEL_UPL; + desc->sd_xx = 0; + desc->sd_p = 1; + if (info.size >= (1 << 20)) { + desc->sd_lolimit = info.size >> PAGE_SHIFT; + desc->sd_hilimit = info.size >> (PAGE_SHIFT + 16); + desc->sd_gran = 1; + } else { + desc->sd_lolimit = info.size; + desc->sd_hilimit = info.size >> 16; + desc->sd_gran = 0; + } + } + crit_exit(); + uap->sysmsg_result = GSEL(GTLS_START + i, SEL_UPL); + set_user_TLS(); + return(0); +} + +/* + * Return the specified TLS descriptor to userland. + * + * Returns the value userland needs to load into %gs representing the + * TLS descriptor or -1 on error. + * + * (struct tls_info *info, int infosize, int which) + */ +int +sys_get_tls_area(struct sys_get_tls_area_args *uap) +{ + struct tls_info info; + struct segment_descriptor *desc; + int error; + int i; + + /* + * Sanity checks + */ + i = uap->which; + if (i < 0 || i >= NGTLS) + return (ERANGE); + if (uap->infosize < 0) + return (EINVAL); + + /* + * unpack the descriptor, ENOENT is returned for any descriptor + * which has not been loaded. uap->info may be NULL. + */ + desc = &curthread->td_tls[i]; + if (desc->sd_p) { + if (uap->info && uap->infosize > 0) { + bzero(&info, sizeof(info)); + info.base = (void *)(intptr_t) + ((desc->sd_hibase << 24) | desc->sd_lobase); + info.size = (desc->sd_hilimit << 16) | desc->sd_lolimit; + if (desc->sd_gran) + info.size <<= PAGE_SHIFT; + error = copyout(&info, uap->info, + min(sizeof(info), uap->infosize)); + } else { + error = 0; + } + uap->sysmsg_result = GSEL(GTLS_START + i, SEL_UPL); + } else { + error = ENOENT; + } + return(error); +} + diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index dd4038932b..983abc5693 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -39,7 +39,7 @@ * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91 * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$ * $FreeBSD: src/sys/i386/i386/vm_machdep.c,v 1.132.2.9 2003/01/25 19:02:23 dillon Exp $ - * $DragonFly: src/sys/i386/i386/Attic/vm_machdep.c,v 1.32 2005/02/01 22:41:24 dillon Exp $ + * $DragonFly: src/sys/i386/i386/Attic/vm_machdep.c,v 1.33 2005/02/21 21:40:53 dillon Exp $ */ #include "use_npx.h" @@ -194,7 +194,8 @@ cpu_fork(p1, p2, flags) pcb2->pcb_ldt->ldt_len); } } - + bcopy(&p1->p_thread->td_tls, &p2->p_thread->td_tls, + sizeof(p2->p_thread->td_tls)); /* * Now, cpu_switch() can schedule the new process. * pcb_esp is loaded pointing to the cpu_switch() stack frame diff --git a/sys/i386/include/pcb_ext.h b/sys/i386/include/pcb_ext.h index 7a241bc3e5..f5bc9f2f9b 100644 --- a/sys/i386/include/pcb_ext.h +++ b/sys/i386/include/pcb_ext.h @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/include/pcb_ext.h,v 1.4 1999/12/29 04:33:04 peter Exp $ - * $DragonFly: src/sys/i386/include/Attic/pcb_ext.h,v 1.5 2003/12/20 05:52:27 dillon Exp $ + * $DragonFly: src/sys/i386/include/Attic/pcb_ext.h,v 1.6 2005/02/21 21:40:55 dillon Exp $ */ #ifndef _I386_PCB_EXT_H_ @@ -57,6 +57,7 @@ struct pcb_ldt { void set_user_ldt (struct pcb *); struct pcb_ldt *user_ldt_alloc (struct pcb *, int); void user_ldt_free (struct pcb *); +void set_user_TLS (void); #endif diff --git a/sys/i386/include/segments.h b/sys/i386/include/segments.h index 9e0d822857..4a63d8e869 100644 --- a/sys/i386/include/segments.h +++ b/sys/i386/include/segments.h @@ -36,7 +36,7 @@ * * from: @(#)segments.h 7.1 (Berkeley) 5/9/91 * $FreeBSD: src/sys/i386/include/segments.h,v 1.24 1999/12/29 04:33:07 peter Exp $ - * $DragonFly: src/sys/i386/include/Attic/segments.h,v 1.7 2004/08/12 19:59:30 eirikn Exp $ + * $DragonFly: src/sys/i386/include/Attic/segments.h,v 1.8 2005/02/21 21:40:55 dillon Exp $ */ #ifndef _MACHINE_SEGMENTS_H_ @@ -220,11 +220,14 @@ struct region_descriptor { #define GBIOSDATA_SEL 12 /* BIOS interface (Data) */ #define GBIOSUTIL_SEL 13 /* BIOS interface (Utility) */ #define GBIOSARGS_SEL 14 /* BIOS interface (Arguments) */ +#define GTLS_START 15 /* Thread TLS Descriptor */ +#define GTLS_END 17 /* Thread TLS Descriptor */ +#define NGTLS (GTLS_END - GTLS_START + 1) #ifdef BDE_DEBUGGER -#define NGDT 18 /* some of 11-17 are reserved for debugger */ +#define NGDT 21 /* some of 11-17 are reserved for debugger */ #else -#define NGDT 15 +#define NGDT 18 #endif /* diff --git a/sys/i386/include/thread.h b/sys/i386/include/thread.h index 05e84b8fc5..3f88444ddd 100644 --- a/sys/i386/include/thread.h +++ b/sys/i386/include/thread.h @@ -33,23 +33,27 @@ * * Machine independant code should not directly include this file. * - * $DragonFly: src/sys/i386/include/Attic/thread.h,v 1.7 2004/07/16 05:48:46 dillon Exp $ + * $DragonFly: src/sys/i386/include/Attic/thread.h,v 1.8 2005/02/21 21:40:55 dillon Exp $ */ #ifndef _MACHINE_THREAD_H_ #define _MACHINE_THREAD_H_ +#include "segments.h" + union savefpu; struct md_thread { unsigned int mtd_cpl; union savefpu *mtd_savefpu; + struct segment_descriptor mtd_tls[NGTLS]; }; #ifdef _KERNEL #define td_cpl td_mach.mtd_cpl #define td_savefpu td_mach.mtd_savefpu +#define td_tls td_mach.mtd_tls /* * mycpu() retrieves the base of the current cpu's globaldata structure. diff --git a/sys/platform/pc32/i386/machdep.c b/sys/platform/pc32/i386/machdep.c index 81290056a8..0b7133b635 100644 --- a/sys/platform/pc32/i386/machdep.c +++ b/sys/platform/pc32/i386/machdep.c @@ -36,7 +36,7 @@ * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $ - * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.69 2005/02/07 20:38:58 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.70 2005/02/21 21:40:53 dillon Exp $ */ #include "use_apm.h" @@ -1175,6 +1175,33 @@ struct soft_segment_descriptor gdt_segs[] = { 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, +/* GTLS_START 15 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, +/* GTLS_START+1 16 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, +/* GTLS_END 17 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { diff --git a/sys/platform/pc32/i386/swtch.s b/sys/platform/pc32/i386/swtch.s index 73f2f362a3..0ee217ac9e 100644 --- a/sys/platform/pc32/i386/swtch.s +++ b/sys/platform/pc32/i386/swtch.s @@ -66,7 +66,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $ - * $DragonFly: src/sys/platform/pc32/i386/swtch.s,v 1.36 2004/12/20 09:21:18 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/i386/swtch.s,v 1.37 2005/02/21 21:40:53 dillon Exp $ */ #include "use_npx.h" @@ -374,6 +374,12 @@ ENTRY(cpu_heavy_restore) call set_user_ldt popl %edx 2: + /* + * Restore the user TLS if we have one + */ + pushl %edx + call set_user_TLS + popl %edx /* * Restore the %gs segment register, which must be done after * loading the user LDT. Since user processes can modify the diff --git a/sys/platform/pc32/i386/sys_machdep.c b/sys/platform/pc32/i386/sys_machdep.c index 19dfa24639..5938df1dde 100644 --- a/sys/platform/pc32/i386/sys_machdep.c +++ b/sys/platform/pc32/i386/sys_machdep.c @@ -32,7 +32,7 @@ * * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 * $FreeBSD: src/sys/i386/i386/sys_machdep.c,v 1.47.2.3 2002/10/07 17:20:00 jhb Exp $ - * $DragonFly: src/sys/platform/pc32/i386/sys_machdep.c,v 1.15 2004/12/20 13:58:02 joerg Exp $ + * $DragonFly: src/sys/platform/pc32/i386/sys_machdep.c,v 1.16 2005/02/21 21:40:53 dillon Exp $ * */ @@ -75,6 +75,7 @@ static int i386_get_ldt (struct proc *, char *, int *); static int i386_set_ldt (struct proc *, char *, int *); static int i386_get_ioperm (struct proc *, char *); static int i386_set_ioperm (struct proc *, char *); +static int check_descs(union descriptor *, int); int i386_extend_pcb (struct proc *); /* @@ -91,7 +92,6 @@ sysarch(struct sysarch_args *uap) case I386_GET_LDT: error = i386_get_ldt(p, uap->parms, &uap->sysmsg_result); break; - case I386_SET_LDT: error = i386_set_ldt(p, uap->parms, &uap->sysmsg_result); break; @@ -233,9 +233,32 @@ done: return (error); } +/* + * Update the TLS entries for the process. Used by assembly, do not staticize. + * + * Must be called from a critical section (else an interrupt thread preemption + * may cause %gs to fault). Normally called from the low level swtch.s code. + */ +void +set_user_TLS(void) +{ + struct thread *td = curthread; + int i; +#ifdef SMP + int off = GTLS_START + mycpu->gd_cpuid * NGDT; +#else + const int off = GTLS_START; +#endif + for (i = 0; i < NGTLS; ++i) + gdt[off + i].sd = td->td_tls[i]; +} + /* * Update the GDT entry pointing to the LDT to point to the LDT of the - * current process. Do not staticize. + * current process. Used by assembly, do not staticize. + * + * Must be called from a critical section (else an interrupt thread preemption + * may cause %gs to fault). Normally called from the low level swtch.s code. */ void set_user_ldt(struct pcb *pcb) @@ -358,12 +381,12 @@ i386_get_ldt(struct proc *p, char *args, int *res) static int i386_set_ldt(struct proc *p, char *args, int *res) { - int error = 0, i, n; + int error = 0; int largest_ld; struct pcb *pcb = p->p_thread->td_pcb; struct pcb_ldt *pcb_ldt = pcb->pcb_ldt; union descriptor *descs; - int descs_size, s; + int descs_size; struct i386_ldt_args ua, *uap = &ua; if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0) @@ -396,10 +419,15 @@ i386_set_ldt(struct proc *p, char *args, int *res) pcb_ldt->ldt_base = new_ldt->ldt_base; pcb_ldt->ldt_len = new_ldt->ldt_len; FREE(new_ldt, M_SUBPROC); - } else + } else { pcb->pcb_ldt = pcb_ldt = new_ldt; + } + /* + * Since the LDT may be shared, we must signal other cpus to + * reload it. XXX we need to track which cpus might be + * using the shared ldt and only signal those. + */ #ifdef SMP - /* signal other cpus to reload ldt */ smp_rendezvous(NULL, (void (*)(void *))set_user_ldt, NULL, pcb); #else set_user_ldt(pcb); @@ -416,7 +444,36 @@ i386_set_ldt(struct proc *p, char *args, int *res) return (error); } /* Check descriptors for access violations */ - for (i = 0, n = uap->start; i < uap->num; i++, n++) { + error = check_descs(descs, uap->num); + if (error) { + kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + return (error); + } + + /* + * Fill in the actual ldt entries. Since %fs might point to one of + * these entries a critical section is required to prevent an + * interrupt thread from preempting us, switch back, and faulting + * on the load of %fs due to a half-formed descriptor. + */ + crit_enter(); + bcopy(descs, + &((union descriptor *)(pcb_ldt->ldt_base))[uap->start], + uap->num * sizeof(union descriptor)); + *res = uap->start; + + crit_exit(); + kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + return (0); +} + +static int +check_descs(union descriptor *descs, int num) +{ + int i; + + /* Check descriptors for access violations */ + for (i = 0; i < num; i++) { union descriptor *dp; dp = &descs[i]; @@ -443,7 +500,6 @@ i386_set_ldt(struct proc *p, char *args, int *res) * to create a segment of these types. They are * for OS use only. */ - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); return EACCES; /* memory segment types */ @@ -452,11 +508,8 @@ i386_set_ldt(struct proc *p, char *args, int *res) case SDT_MEMERC: /* memory execute read conforming */ case SDT_MEMERAC: /* memory execute read accessed conforming */ /* Must be "present" if executable and conforming. */ - if (dp->sd.sd_p == 0) { - kmem_free(kernel_map, (vm_offset_t)descs, - descs_size); + if (dp->sd.sd_p == 0) return (EACCES); - } break; case SDT_MEMRO: /* memory read only */ case SDT_MEMROA: /* memory read only accessed */ @@ -472,27 +525,13 @@ i386_set_ldt(struct proc *p, char *args, int *res) case SDT_MEMERA: /* memory execute read accessed */ break; default: - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); return(EINVAL); /*NOTREACHED*/ } /* Only user (ring-3) descriptors may be present. */ - if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) { - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) return (EACCES); - } } - - s = splhigh(); - - /* Fill in range */ - bcopy(descs, - &((union descriptor *)(pcb_ldt->ldt_base))[uap->start], - uap->num * sizeof(union descriptor)); - *res = uap->start; - - splx(s); - kmem_free(kernel_map, (vm_offset_t)descs, descs_size); return (0); } diff --git a/sys/platform/pc32/i386/tls.c b/sys/platform/pc32/i386/tls.c new file mode 100644 index 0000000000..1dd3e4ff63 --- /dev/null +++ b/sys/platform/pc32/i386/tls.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by David Xu and Matthew Dillon + * + * 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. Neither the name of The DragonFly Project 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/platform/pc32/i386/tls.c,v 1.1 2005/02/21 21:40:53 dillon Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include /* pcb.h included via sys/user.h */ +#include /* CPU_prvspace */ +#ifdef SMP +#include +#endif + +/* + * set a TLS descriptor and resync the GDT. A descriptor may be cleared + * by passing info=NULL and infosize=0. Note that hardware limitations may + * cause the size passed in tls_info to be approximated. + * + * Returns the value userland needs to load into %gs representing the + * TLS descriptor or -1 on error. + * + * (struct tls_info *info, int infosize, int which) + */ +int +sys_set_tls_area(struct sys_set_tls_area_args *uap) +{ + struct tls_info info; + struct segment_descriptor *desc; + int error; + int i; + + /* + * Sanity checks + */ + i = uap->which; + if (i < 0 || i >= NGTLS) + return (ERANGE); + if (uap->infosize < 0) + return (EINVAL); + + /* + * Maintain forwards compatibility with future extensions. + */ + if (uap->infosize != sizeof(info)) { + bzero(&info, sizeof(info)); + error = copyin(uap->info, &info, + min(sizeof(info), uap->infosize)); + } else { + error = copyin(uap->info, &info, sizeof(info)); + } + if (error) + return (error); + if (info.size < 0) + return (EINVAL); + if (info.size > (1 << 20)) + info.size = (info.size + PAGE_MASK) & ~PAGE_MASK; + + /* + * Load the descriptor. A critical section is required in case + * an interrupt thread comes along and switches us out and then back + * in. + */ + desc = &curthread->td_tls[i]; + crit_enter(); + if (info.size == 0) { + bzero(desc, sizeof(*desc)); + } else { + desc->sd_lobase = (intptr_t)info.base; + desc->sd_hibase = (intptr_t)info.base >> 24; + desc->sd_def32 = 1; + desc->sd_type = SDT_MEMRWA; + desc->sd_dpl = SEL_UPL; + desc->sd_xx = 0; + desc->sd_p = 1; + if (info.size >= (1 << 20)) { + desc->sd_lolimit = info.size >> PAGE_SHIFT; + desc->sd_hilimit = info.size >> (PAGE_SHIFT + 16); + desc->sd_gran = 1; + } else { + desc->sd_lolimit = info.size; + desc->sd_hilimit = info.size >> 16; + desc->sd_gran = 0; + } + } + crit_exit(); + uap->sysmsg_result = GSEL(GTLS_START + i, SEL_UPL); + set_user_TLS(); + return(0); +} + +/* + * Return the specified TLS descriptor to userland. + * + * Returns the value userland needs to load into %gs representing the + * TLS descriptor or -1 on error. + * + * (struct tls_info *info, int infosize, int which) + */ +int +sys_get_tls_area(struct sys_get_tls_area_args *uap) +{ + struct tls_info info; + struct segment_descriptor *desc; + int error; + int i; + + /* + * Sanity checks + */ + i = uap->which; + if (i < 0 || i >= NGTLS) + return (ERANGE); + if (uap->infosize < 0) + return (EINVAL); + + /* + * unpack the descriptor, ENOENT is returned for any descriptor + * which has not been loaded. uap->info may be NULL. + */ + desc = &curthread->td_tls[i]; + if (desc->sd_p) { + if (uap->info && uap->infosize > 0) { + bzero(&info, sizeof(info)); + info.base = (void *)(intptr_t) + ((desc->sd_hibase << 24) | desc->sd_lobase); + info.size = (desc->sd_hilimit << 16) | desc->sd_lolimit; + if (desc->sd_gran) + info.size <<= PAGE_SHIFT; + error = copyout(&info, uap->info, + min(sizeof(info), uap->infosize)); + } else { + error = 0; + } + uap->sysmsg_result = GSEL(GTLS_START + i, SEL_UPL); + } else { + error = ENOENT; + } + return(error); +} + diff --git a/sys/platform/pc32/i386/vm_machdep.c b/sys/platform/pc32/i386/vm_machdep.c index fd5ddfaa3d..a059dded78 100644 --- a/sys/platform/pc32/i386/vm_machdep.c +++ b/sys/platform/pc32/i386/vm_machdep.c @@ -39,7 +39,7 @@ * from: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91 * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$ * $FreeBSD: src/sys/i386/i386/vm_machdep.c,v 1.132.2.9 2003/01/25 19:02:23 dillon Exp $ - * $DragonFly: src/sys/platform/pc32/i386/vm_machdep.c,v 1.32 2005/02/01 22:41:24 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/i386/vm_machdep.c,v 1.33 2005/02/21 21:40:53 dillon Exp $ */ #include "use_npx.h" @@ -194,7 +194,8 @@ cpu_fork(p1, p2, flags) pcb2->pcb_ldt->ldt_len); } } - + bcopy(&p1->p_thread->td_tls, &p2->p_thread->td_tls, + sizeof(p2->p_thread->td_tls)); /* * Now, cpu_switch() can schedule the new process. * pcb_esp is loaded pointing to the cpu_switch() stack frame diff --git a/sys/platform/pc32/include/pcb_ext.h b/sys/platform/pc32/include/pcb_ext.h index 38e3486657..77c3a4c19f 100644 --- a/sys/platform/pc32/include/pcb_ext.h +++ b/sys/platform/pc32/include/pcb_ext.h @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/include/pcb_ext.h,v 1.4 1999/12/29 04:33:04 peter Exp $ - * $DragonFly: src/sys/platform/pc32/include/pcb_ext.h,v 1.5 2003/12/20 05:52:27 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/include/pcb_ext.h,v 1.6 2005/02/21 21:40:55 dillon Exp $ */ #ifndef _I386_PCB_EXT_H_ @@ -57,6 +57,7 @@ struct pcb_ldt { void set_user_ldt (struct pcb *); struct pcb_ldt *user_ldt_alloc (struct pcb *, int); void user_ldt_free (struct pcb *); +void set_user_TLS (void); #endif diff --git a/sys/platform/pc32/include/thread.h b/sys/platform/pc32/include/thread.h index b32cb9f23a..99d8c5891c 100644 --- a/sys/platform/pc32/include/thread.h +++ b/sys/platform/pc32/include/thread.h @@ -33,23 +33,27 @@ * * Machine independant code should not directly include this file. * - * $DragonFly: src/sys/platform/pc32/include/thread.h,v 1.7 2004/07/16 05:48:46 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/include/thread.h,v 1.8 2005/02/21 21:40:55 dillon Exp $ */ #ifndef _MACHINE_THREAD_H_ #define _MACHINE_THREAD_H_ +#include "segments.h" + union savefpu; struct md_thread { unsigned int mtd_cpl; union savefpu *mtd_savefpu; + struct segment_descriptor mtd_tls[NGTLS]; }; #ifdef _KERNEL #define td_cpl td_mach.mtd_cpl #define td_savefpu td_mach.mtd_savefpu +#define td_tls td_mach.mtd_tls /* * mycpu() retrieves the base of the current cpu's globaldata structure. diff --git a/sys/sys/syscall-args b/sys/sys/syscall-args index 4529ebd8f7..cf0bafd0a9 100644 --- a/sys/sys/syscall-args +++ b/sys/sys/syscall-args @@ -1,6 +1,6 @@ # System call argument table. # DO NOT EDIT-- this file is automatically generated. -# $DragonFly: src/sys/sys/Attic/syscall-args,v 1.9 2005/02/20 03:24:47 davidxu Exp $ +# $DragonFly: src/sys/sys/Attic/syscall-args,v 1.10 2005/02/21 21:40:58 dillon Exp $ # Created from DragonFly: src/sys/kern/syscalls.master,v 1.19 2005/02/20 01:17:44 davidxu Exp @@ -261,3 +261,5 @@ int mountctl mountctl mountctl_args const char * path int op int fd const void * int umtx_sleep umtx_sleep umtx_sleep_args volatile const int * ptr int value int timeout int umtx_wakeup umtx_wakeup umtx_wakeup_args volatile const int * ptr int count int jail_attach jail_attach jail_attach_args int jid +int sys_set_tls_area sys_set_tls_area sys_set_tls_area_args int which struct tls_info * info int infosize +int sys_get_tls_area sys_get_tls_area sys_get_tls_area_args int which struct tls_info * info int infosize diff --git a/sys/sys/syscall-hide.h b/sys/sys/syscall-hide.h index 4cb8d7410d..0a9b55335a 100644 --- a/sys/sys/syscall-hide.h +++ b/sys/sys/syscall-hide.h @@ -2,7 +2,7 @@ * System call hiders. * * DO NOT EDIT-- this file is automatically generated. - * $DragonFly: src/sys/sys/syscall-hide.h,v 1.26 2005/02/20 03:24:47 davidxu Exp $ + * $DragonFly: src/sys/sys/syscall-hide.h,v 1.27 2005/02/21 21:40:58 dillon Exp $ * created from DragonFly: src/sys/kern/syscalls.master,v 1.19 2005/02/20 01:17:44 davidxu Exp */ @@ -299,3 +299,5 @@ HIDE_BSD(mountctl) HIDE_BSD(umtx_sleep) HIDE_BSD(umtx_wakeup) HIDE_BSD(jail_attach) +HIDE_BSD(sys_set_tls_area) +HIDE_BSD(sys_get_tls_area) diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 953fbdd6f6..1f8550b53b 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -2,7 +2,7 @@ * System call numbers. * * DO NOT EDIT-- this file is automatically generated. - * $DragonFly: src/sys/sys/syscall.h,v 1.26 2005/02/20 03:24:47 davidxu Exp $ + * $DragonFly: src/sys/sys/syscall.h,v 1.27 2005/02/21 21:40:58 dillon Exp $ * created from DragonFly: src/sys/kern/syscalls.master,v 1.19 2005/02/20 01:17:44 davidxu Exp */ @@ -310,4 +310,6 @@ #define SYS_umtx_sleep 469 #define SYS_umtx_wakeup 470 #define SYS_jail_attach 471 -#define SYS_MAXSYSCALL 472 +#define SYS_sys_set_tls_area 472 +#define SYS_sys_get_tls_area 473 +#define SYS_MAXSYSCALL 474 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index e61dccc271..727f198e35 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -1,6 +1,6 @@ # DragonFly system call names. # DO NOT EDIT-- this file is automatically generated. -# $DragonFly: src/sys/sys/syscall.mk,v 1.26 2005/02/20 03:24:47 davidxu Exp $ +# $DragonFly: src/sys/sys/syscall.mk,v 1.27 2005/02/21 21:40:58 dillon Exp $ # created from DragonFly: src/sys/kern/syscalls.master,v 1.19 2005/02/20 01:17:44 davidxu Exp MIASM = \ syscall.o \ @@ -259,4 +259,6 @@ MIASM = \ mountctl.o \ umtx_sleep.o \ umtx_wakeup.o \ - jail_attach.o + jail_attach.o \ + sys_set_tls_area.o \ + sys_get_tls_area.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index 7b3e3e2490..dd6f834e21 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -2,7 +2,7 @@ * System call prototypes. * * DO NOT EDIT-- this file is automatically generated. - * $DragonFly: src/sys/sys/sysproto.h,v 1.26 2005/02/20 03:24:47 davidxu Exp $ + * $DragonFly: src/sys/sys/sysproto.h,v 1.27 2005/02/21 21:40:58 dillon Exp $ * created from DragonFly: src/sys/kern/syscalls.master,v 1.19 2005/02/20 01:17:44 davidxu Exp */ @@ -2176,6 +2176,24 @@ struct jail_attach_args { union usrmsg usrmsg; int jid; char jid_[PAD_(int)]; }; +struct sys_set_tls_area_args { +#ifdef _KERNEL + struct sysmsg sysmsg; +#endif + union usrmsg usrmsg; + int which; char which_[PAD_(int)]; + struct tls_info * info; char info_[PAD_(struct tls_info *)]; + int infosize; char infosize_[PAD_(int)]; +}; +struct sys_get_tls_area_args { +#ifdef _KERNEL + struct sysmsg sysmsg; +#endif + union usrmsg usrmsg; + int which; char which_[PAD_(int)]; + struct tls_info * info; char info_[PAD_(struct tls_info *)]; + int infosize; char infosize_[PAD_(int)]; +}; #ifdef _KERNEL @@ -2434,6 +2452,8 @@ int mountctl (struct mountctl_args *); int umtx_sleep (struct umtx_sleep_args *); int umtx_wakeup (struct umtx_wakeup_args *); int jail_attach (struct jail_attach_args *); +int sys_set_tls_area (struct sys_set_tls_area_args *); +int sys_get_tls_area (struct sys_get_tls_area_args *); #endif /* _KERNEL */ diff --git a/sys/sys/sysunion.h b/sys/sys/sysunion.h index e7104f88d6..159bc000ec 100644 --- a/sys/sys/sysunion.h +++ b/sys/sys/sysunion.h @@ -2,7 +2,7 @@ * Union of syscall args for messaging. * * DO NOT EDIT-- this file is automatically generated. - * $DragonFly: src/sys/sys/sysunion.h,v 1.23 2005/02/20 03:24:47 davidxu Exp $ + * $DragonFly: src/sys/sys/sysunion.h,v 1.24 2005/02/21 21:40:58 dillon Exp $ * created from DragonFly: src/sys/kern/syscalls.master,v 1.19 2005/02/20 01:17:44 davidxu Exp */ @@ -344,4 +344,6 @@ union sysunion { struct umtx_sleep_args umtx_sleep; struct umtx_wakeup_args umtx_wakeup; struct jail_attach_args jail_attach; + struct sys_set_tls_area_args sys_set_tls_area; + struct sys_get_tls_area_args sys_get_tls_area; }; diff --git a/sys/sys/tls.h b/sys/sys/tls.h new file mode 100644 index 0000000000..c61e7a02ae --- /dev/null +++ b/sys/sys/tls.h @@ -0,0 +1,17 @@ +/* + * SYS/TLS.H + * + * Implements the architecture independant TLS info structure. + * + * $DragonFly: src/sys/sys/tls.h,v 1.1 2005/02/21 21:40:58 dillon Exp $ + */ + +#ifndef _SYS_TLS_H_ +#define _SYS_TLS_H_ + +struct tls_info { + void *base; + int size; +}; + +#endif -- 2.41.0