diff options
author | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-10-15 23:35:30 +0000 |
---|---|---|
committer | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-10-15 23:35:30 +0000 |
commit | 67874eb615d7ff9c8ac1f262fa286e41e2e0e32f (patch) | |
tree | a4438f1bc677ce643f078f2bfaef615b6264844d | |
parent | ae0a9206a9212a9f09180c0e6c63b4973377fc50 (diff) |
"TLS-lite": add kernel support for a per-thread userspace pointer,
for pointing to the thread-control-block. Support for mapping this
to the correct hardware register can be added as it's finished;
start with support for amd64, sparc, and sparc64. Includes syscalls
for getting and setting it (for a portable __errno implementation) as
well as creating a new thread with an initial value for it.
discussed with miod@, kettenis@, deraadt@; committing to get the syscalls
in with the impending libc bump and do further refinements in tree
-rw-r--r-- | sys/arch/amd64/include/proc.h | 8 | ||||
-rw-r--r-- | sys/arch/amd64/include/tcb.h | 56 | ||||
-rw-r--r-- | sys/arch/sparc/include/proc.h | 4 | ||||
-rw-r--r-- | sys/arch/sparc/include/tcb.h | 75 | ||||
-rw-r--r-- | sys/arch/sparc64/include/proc.h | 2 | ||||
-rw-r--r-- | sys/arch/sparc64/include/tcb.h | 75 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 55 | ||||
-rw-r--r-- | sys/kern/kern_prot.c | 30 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 5 | ||||
-rw-r--r-- | sys/sys/proc.h | 10 | ||||
-rw-r--r-- | sys/sys/unistd.h | 9 |
11 files changed, 317 insertions, 12 deletions
diff --git a/sys/arch/amd64/include/proc.h b/sys/arch/amd64/include/proc.h index b56cdb7e50f..733c93ea5cc 100644 --- a/sys/arch/amd64/include/proc.h +++ b/sys/arch/amd64/include/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.6 2011/03/23 16:54:34 pirofti Exp $ */ +/* $OpenBSD: proc.h,v 1.7 2011/10/15 23:35:29 guenther Exp $ */ /* $NetBSD: proc.h,v 1.1 2003/04/26 18:39:46 fvdl Exp $ */ /* @@ -35,11 +35,10 @@ #ifndef _MACHINE_PROC_H_ #define _MACHINE_PROC_H_ -#include <machine/frame.h> - /* * Machine-dependent part of the proc structure for amd64. */ +struct trapframe; struct mdproc { struct trapframe *md_regs; /* registers on current frame */ int md_flags; @@ -49,5 +48,8 @@ struct mdproc { /* md_flags */ #define MDP_USEDFPU 0x0001 /* has used the FPU */ #define MDP_IRET 0x0002 /* return via iret, not sysret */ + /* (iret can restore r11 and rcx) */ + +#define __HAVE_MD_TCB #endif /* _MACHINE_PROC_H_ */ diff --git a/sys/arch/amd64/include/tcb.h b/sys/arch/amd64/include/tcb.h new file mode 100644 index 00000000000..f6a0ed87a7f --- /dev/null +++ b/sys/arch/amd64/include/tcb.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Philip Guenther <guenther@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_TCB_H_ +#define _MACHINE_TCB_H_ + +#ifdef _KERNEL + +#include <machine/pcb.h> + +#define TCB_GET(p) \ + ((void *)((struct pcb *)(p)->p_addr)->pcb_fsbase) +#define TCB_SET(p, addr) \ + (((struct pcb *)(p)->p_addr)->pcb_fsbase = (u_int64_t)(addr)) + +#else /* _KERNEL */ + +#include <stddef.h> /* for offsetof */ + +/* ELF TLS ABI calls for big TCB, with static TLS data at negative offsets */ +#define TLS_VARIANT 2 + +/* Read a slot from the TCB */ +static inline void * +__amd64_read_tcb(long offset) +{ + void *val; + __asm__ ("movq %%fs:(%1),%0" : "=r" (val) : "r" (offset)); + return val; +} + +/* Get a pointer to the TCB itself */ +#define TCB_GET() __amd64_read_tcb(0) + +/* Get the value of a specific member in the TCB */ +#define TCB_GET_MEMBER(member) \ + __amd64_read_tcb(offsetof(struct thread_control_block, member)) + +/* Setting the TCB pointer can only be done via syscall, so no TCB_SET() */ + +#endif /* _KERNEL */ + +#endif /* _MACHINE_TCB_H_ */ diff --git a/sys/arch/sparc/include/proc.h b/sys/arch/sparc/include/proc.h index 419d945ac75..d8d5f867904 100644 --- a/sys/arch/sparc/include/proc.h +++ b/sys/arch/sparc/include/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.4 2010/11/27 19:41:45 miod Exp $ */ +/* $OpenBSD: proc.h,v 1.5 2011/10/15 23:35:29 guenther Exp $ */ /* $NetBSD: proc.h,v 1.3 1996/09/26 18:51:17 christos Exp $ */ /* @@ -48,3 +48,5 @@ struct mdproc { struct trapframe *md_tf; /* trap/syscall registers */ struct fpstate *md_fpstate; /* fpu state, if any; always resident */ }; + +#define __HAVE_MD_TCB diff --git a/sys/arch/sparc/include/tcb.h b/sys/arch/sparc/include/tcb.h new file mode 100644 index 00000000000..b445918b925 --- /dev/null +++ b/sys/arch/sparc/include/tcb.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Philip Guenther <guenther@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_TCB_H_ +#define _MACHINE_TCB_H_ + +#ifdef _KERNEL + +#include <machine/reg.h> + +/* + * In userspace, register %g7 contains the address of the thread's TCB + */ +#define TCB_GET(p) \ + ((void *)(p)->p_md.md_tf->tf_global[7]) +#define TCB_SET(p, addr) \ + ((p)->p_md.md_tf->tf_global[7] = (int)(addr)) + +#else /* _KERNEL */ + +/* ELF TLS ABI calls for big TCB, with static TLS data at negative offsets */ +#define TLS_VARIANT 2 + +#if 0 /* XXX perhaps use the gcc global register extension? */ +struct thread_control_block; +__register__ struct thread_control_block *__tcb __asm__ ("%g7"); +#define TCB_GET() (__tcb) +#define TCB_GET_MEMBER(member) ((void *)(__tcb->member)) +#define TCB_SET(tcb) ((__tcb) = (tcb)) + +#else + +#include <stddef.h> /* for offsetof */ + +/* Get a pointer to the TCB itself */ +static inline void * +__sparc_get_tcb(void) +{ + void *val; + __asm__ ("mov %%g7, %0" : "=r" (val)); + return val; +} +#define TCB_GET() __sparc_get_tcb() + +/* Get the value of a specific member in the TCB */ +static inline void * +__sparc_get_tcb(int offset) +{ + void *val; + __asm__ ("ld [%%g7 + %1], %0" : "=r" (val) : "r" (offset)); + return val; +} +#define TCB_GET_MEMBER(member) \ + __sparc_get_tcb(offsetof(struct thread_control_block, member)) + +#define TCB_SET(tcb) (__asm __volatile("mov %0, %%g7" : : "r" (tcb))) + +#endif /* 0 */ + +#endif /* _KERNEL */ + +#endif /* _MACHINE_TCB_H_ */ diff --git a/sys/arch/sparc64/include/proc.h b/sys/arch/sparc64/include/proc.h index 0559c74bcc9..6ae7daa48d7 100644 --- a/sys/arch/sparc64/include/proc.h +++ b/sys/arch/sparc64/include/proc.h @@ -48,3 +48,5 @@ struct mdproc { struct fpstate64 *md_fpstate; /* fpu state, if any; always resident */ __volatile int md_astpending; }; + +#define __HAVE_MD_TCB diff --git a/sys/arch/sparc64/include/tcb.h b/sys/arch/sparc64/include/tcb.h new file mode 100644 index 00000000000..6dedb858c05 --- /dev/null +++ b/sys/arch/sparc64/include/tcb.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Philip Guenther <guenther@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_TCB_H_ +#define _MACHINE_TCB_H_ + +#ifdef _KERNEL + +#include <machine/reg.h> + +/* + * In userspace, register %g7 contains the address of the thread's TCB + */ +#define TCB_GET(p) \ + ((void *)(p)->p_md.md_tf->tf_global[7]) +#define TCB_SET(p, addr) \ + ((p)->p_md.md_tf->tf_global[7] = (int64_t)(addr)) + +#else /* _KERNEL */ + +/* ELF TLS ABI calls for big TCB, with static TLS data at negative offsets */ +#define TLS_VARIANT 2 + +#if 0 /* XXX perhaps use the gcc global register extension? */ +struct thread_control_block; +__register__ struct thread_control_block *__tcb __asm__ ("%g7"); +#define TCB_GET() (__tcb) +#define TCB_GET_MEMBER(member) ((void *)(__tcb->member)) +#define TCB_SET(tcb) ((__tcb) = (tcb)) + +#else + +#include <stddef.h> /* for offsetof */ + +/* Get a pointer to the TCB itself */ +static inline void * +__sparc64_get_tcb(void) +{ + void *val; + __asm__ ("mov %%g7, %0" : "=r" (val)); + return val; +} +#define TCB_GET() __sparc64_get_tcb() + +/* Get the value of a specific member in the TCB */ +static inline void * +__sparc64_get_tcb(int offset) +{ + void *val; + __asm__ ("ldx [%%g7 + %1], %0" : "=r" (val) : "r" (offset)); + return val; +} +#define TCB_GET_MEMBER(member) \ + __sparc64_get_tcb(offsetof(struct thread_control_block, member)) + +#define TCB_SET(tcb) (__asm __volatile("mov %0, %%g7" : : "r" (tcb))) + +#endif /* 0 */ + +#endif /* _KERNEL */ + +#endif /* _MACHINE_TCB_H_ */ diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 59334cfdd03..39c2249638b 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.128 2011/07/07 18:00:33 guenther Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.129 2011/10/15 23:35:29 guenther Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -65,12 +65,17 @@ #include <uvm/uvm_extern.h> #include <uvm/uvm_map.h> +#ifdef __HAVE_MD_TCB +# include <machine/tcb.h> +#endif + int nprocs = 1; /* process 0 */ int randompid; /* when set to 1, pid's go random */ pid_t lastpid; struct forkstat forkstat; void fork_return(void *); +void tfork_child_return(void *); int pidtaken(pid_t); void process_new(struct proc *, struct proc *); @@ -148,6 +153,39 @@ sys_rfork(struct proc *p, void *v, register_t *retval) return (fork1(p, SIGCHLD, flags, NULL, 0, NULL, NULL, retval, NULL)); } +int +sys___tfork(struct proc *p, void *v, register_t *retval) +{ + struct sys___tfork_args /* { + syscallarg(struct __tfork) *param; + } */ *uap = v; + struct __tfork param; + int flags; + int error; + + if ((error = copyin(SCARG(uap, param), ¶m, sizeof(param)))) + return (error); + + /* XXX will supersede rfork at some point... */ + if (param.tf_flags != 0) + return (EINVAL); + + flags = FORK_TFORK | FORK_THREAD | FORK_SIGHAND | FORK_SHAREVM + | FORK_NOZOMBIE | FORK_SHAREFILES; + + return (fork1(p, 0, flags, NULL, param.tf_tid, tfork_child_return, + param.tf_tcb, retval, NULL)); +} + +void +tfork_child_return(void *arg) +{ + struct proc *p = curproc; + + TCB_SET(p, arg); + child_return(p); +} + /* * Allocate and initialize a new process. */ @@ -196,7 +234,7 @@ process_new(struct proc *newproc, struct proc *parentproc) struct timeval fork_tfmrate = { 10, 0 }; int -fork1(struct proc *p1, int exitsig, int flags, void *stack, size_t stacksize, +fork1(struct proc *p1, int exitsig, int flags, void *stack, pid_t *tidptr, void (*func)(void *), void *arg, register_t *retval, struct proc **rnewprocp) { @@ -364,7 +402,7 @@ fork1(struct proc *p1, int exitsig, int flags, void *stack, size_t stacksize, * different path later. */ uvm_fork(p1, p2, ((flags & FORK_SHAREVM) ? TRUE : FALSE), stack, - stacksize, func ? func : child_return, arg ? arg : p2); + 0, func ? func : child_return, arg ? arg : p2); timeout_set(&p2->p_stats->p_virt_to, virttimer_trampoline, p2); timeout_set(&p2->p_stats->p_prof_to, proftimer_trampoline, p2); @@ -377,6 +415,10 @@ fork1(struct proc *p1, int exitsig, int flags, void *stack, size_t stacksize, } else if (flags & FORK_VFORK) { forkstat.cntvfork++; forkstat.sizvfork += vm->vm_dsize + vm->vm_ssize; +#if 0 + } else if (flags & FORK_TFORK) { + forkstat.cnttfork++; +#endif } else if (flags & FORK_RFORK) { forkstat.cntrfork++; forkstat.sizrfork += vm->vm_dsize + vm->vm_ssize; @@ -425,6 +467,13 @@ fork1(struct proc *p1, int exitsig, int flags, void *stack, size_t stacksize, systrace_fork(p1, p2, newstrp); #endif + if (tidptr != NULL) { + pid_t pid = p2->p_pid + THREAD_PID_OFFSET; + + if (copyout(&pid, tidptr, sizeof(pid))) + psignal(p1, SIGSEGV); + } + /* * Make child runnable, set start time, and add to run queue. */ diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 1a0f724db41..e936e44970e 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_prot.c,v 1.50 2011/07/25 20:32:06 tedu Exp $ */ +/* $OpenBSD: kern_prot.c,v 1.51 2011/10/15 23:35:29 guenther Exp $ */ /* $NetBSD: kern_prot.c,v 1.33 1996/02/09 18:59:42 christos Exp $ */ /* @@ -54,6 +54,10 @@ #include <sys/mount.h> #include <sys/syscallargs.h> +#ifdef __HAVE_MD_TCB +# include <machine/tcb.h> +#endif + /* ARGSUSED */ int sys_getpid(struct proc *p, void *v, register_t *retval) @@ -890,3 +894,27 @@ proc_cansugid(struct proc *p) /* Allow. */ return (1); } + +/* + * Set address of the proc's thread-control-block + */ +int +sys___set_tcb(struct proc *p, void *v, register_t *retval) +{ + struct sys___set_tcb_args /* { + syscallarg(void *) tcb; + } */ *uap = v; + + TCB_SET(p, SCARG(uap, tcb)); + return (0); +} + +/* + * Get address of the proc's thread-control-block + */ +int +sys___get_tcb(struct proc *p, void *v, register_t *retval) +{ + *retval = (register_t)TCB_GET(p); + return (0); +} diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index cd4a7899bcb..c8011d97d42 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -1,4 +1,4 @@ -; $OpenBSD: syscalls.master,v 1.118 2011/07/18 00:16:54 matthew Exp $ +; $OpenBSD: syscalls.master,v 1.119 2011/10/15 23:35:29 guenther Exp $ ; $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $ ; @(#)syscalls.master 8.2 (Berkeley) 1/13/94 @@ -574,3 +574,6 @@ const struct timespec *times, int flag); } 327 STD { int sys_futimens(int fd, \ const struct timespec *times); } +328 STD { int sys___tfork(struct __tfork *param); } +329 STD NOLOCK { void sys___set_tcb(void *tcb); } +330 STD NOLOCK { void *sys___get_tcb(void); } diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 3a4aa490181..bc2a43962d8 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.143 2011/09/20 10:07:37 deraadt Exp $ */ +/* $OpenBSD: proc.h,v 1.144 2011/10/15 23:35:29 guenther Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -235,6 +235,11 @@ struct proc { long p_thrslpid; /* for thrsleep syscall */ int p_sigwait; /* signal handled by sigwait() */ +#ifndef __HAVE_MD_TCB + void *p_tcb; /* user-space thread-control-block address */ +# define TCB_SET(p, addr) ((p)->p_tcb = (addr)) +# define TCB_GET(p) ((p)->p_tcb) +#endif /* scheduling */ u_int p_estcpu; /* Time averaged value of p_cpticks. */ @@ -425,6 +430,7 @@ struct uidinfo *uid_find(uid_t); #define FORK_CLEANFILES 0x00000020 #define FORK_NOZOMBIE 0x00000040 #define FORK_SHAREVM 0x00000080 +#define FORK_TFORK 0x00000100 #define FORK_SIGHAND 0x00000200 #define FORK_PTRACE 0x00000400 #define FORK_THREAD 0x00000800 @@ -483,7 +489,7 @@ void reaper(void); void exit1(struct proc *, int, int); void exit2(struct proc *); void cpu_exit(struct proc *); -int fork1(struct proc *, int, int, void *, size_t, void (*)(void *), +int fork1(struct proc *, int, int, void *, pid_t *, void (*)(void *), void *, register_t *, struct proc **); int groupmember(gid_t, struct ucred *); diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h index 7a5b3ede8af..52b8545c6a6 100644 --- a/sys/sys/unistd.h +++ b/sys/sys/unistd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: unistd.h,v 1.18 2010/10/28 02:05:59 deraadt Exp $ */ +/* $OpenBSD: unistd.h,v 1.19 2011/10/15 23:35:29 guenther Exp $ */ /* $NetBSD: unistd.h,v 1.10 1994/06/29 06:46:06 cgd Exp $ */ /* @@ -72,6 +72,13 @@ #define L_SET SEEK_SET #define L_INCR SEEK_CUR #define L_XTND SEEK_END + +/* the parameters argument passed to the __tfork() syscall */ +struct __tfork { + void *tf_tcb; + pid_t *tf_tid; + int tf_flags; +}; #endif /* configurable pathname variables */ |