diff options
author | 2011-04-05 15:44:40 +0000 | |
---|---|---|
committer | 2011-04-05 15:44:40 +0000 | |
commit | 3595609c45e61ad71974f17cd71ac52905aef42c (patch) | |
tree | f0e0cf6790fa13a8c9f90fe1d95cbfc870ceda03 /sys/compat/linux/linux_sched.c | |
parent | Bye bye, kern.emul.freebsd (diff) | |
download | wireguard-openbsd-3595609c45e61ad71974f17cd71ac52905aef42c.tar.xz wireguard-openbsd-3595609c45e61ad71974f17cd71ac52905aef42c.zip |
Add set_tid_address() syscall. Lots of help from and okay guenther@.
This is more than a simple syscall.This expands TLS support quite a bit.
Also linux_sys_clone() handles CLONE_CHILD_CLEARTID, CLONE_CHILD_SETTID,
CLONE_PARENT_SETTID flags as well as the CLONE_SETTLS by doing what
set_thread_area() is doing.
Next on the list is futex support which should allow compat to cope with
newer Linux kernels.
Diffstat (limited to 'sys/compat/linux/linux_sched.c')
-rw-r--r-- | sys/compat/linux/linux_sched.c | 93 |
1 files changed, 83 insertions, 10 deletions
diff --git a/sys/compat/linux/linux_sched.c b/sys/compat/linux/linux_sched.c index 6eda8123e60..61a4b5b98ad 100644 --- a/sys/compat/linux/linux_sched.c +++ b/sys/compat/linux/linux_sched.c @@ -1,4 +1,4 @@ -/* $OpenBSD: linux_sched.c,v 1.9 2011/04/04 23:24:58 pirofti Exp $ */ +/* $OpenBSD: linux_sched.c,v 1.10 2011/04/05 15:44:40 pirofti Exp $ */ /* $NetBSD: linux_sched.c,v 1.6 2000/05/28 05:49:05 thorpej Exp $ */ /*- @@ -41,36 +41,41 @@ #include <sys/proc.h> #include <sys/systm.h> #include <sys/syscallargs.h> +#include <sys/signalvar.h> #include <machine/cpu.h> +#include <machine/pcb.h> +#include <machine/linux_machdep.h> +#include <compat/linux/linux_emuldata.h> #include <compat/linux/linux_types.h> #include <compat/linux/linux_sched.h> #include <compat/linux/linux_signal.h> #include <compat/linux/linux_syscallargs.h> +void linux_child_return(void *); + int linux_sys_clone(struct proc *p, void *v, register_t *retval) { - struct linux_sys_clone_args /* { - syscallarg(int) flags; - syscallarg(void *) stack; - } */ *uap = v; + struct linux_sys_clone_args *uap = v; + struct linux_emuldata *emul = p->p_emuldata; int cflags = SCARG(uap, flags); int flags = FORK_RFORK, sig; + int error = 0; /* * We only support certain bits. The Linux crew keep adding more, * so let's test for anything outside of what we support and complain * about them. Not everything in this list is completely supported, * they just aren't _always_ an error. - * To make nptl threads work we need to add support for at least - * CLONE_SETTLS, CLONE_PARENT_SETTID, and CLONE_CHILD_CLEARTID. */ if (cflags & ~(LINUX_CLONE_CSIGNAL | LINUX_CLONE_VM | LINUX_CLONE_FS | LINUX_CLONE_FILES | LINUX_CLONE_SIGHAND | LINUX_CLONE_VFORK | LINUX_CLONE_PARENT | LINUX_CLONE_THREAD | LINUX_CLONE_SYSVSEM | - LINUX_CLONE_DETACHED | LINUX_CLONE_UNTRACED)) + LINUX_CLONE_DETACHED | LINUX_CLONE_UNTRACED | LINUX_CLONE_SETTLS | + LINUX_CLONE_PARENT_SETTID | LINUX_CLONE_CHILD_CLEARTID | + LINUX_CLONE_CHILD_SETTID)) return (EINVAL); if (cflags & LINUX_CLONE_VM) @@ -137,14 +142,51 @@ linux_sys_clone(struct proc *p, void *v, register_t *retval) return (EINVAL); sig = linux_to_bsd_sig[sig]; + if (cflags & LINUX_CLONE_CHILD_SETTID) + emul->child_set_tid = SCARG(uap, child_tidptr); + else + emul->child_set_tid = NULL; + + if (cflags & LINUX_CLONE_CHILD_CLEARTID) + emul->child_clear_tid = SCARG(uap, child_tidptr); + else + emul->child_clear_tid = NULL; + + if (cflags & LINUX_CLONE_PARENT_SETTID) + if (SCARG(uap, parent_tidptr) == NULL) + return (EINVAL); + + if (cflags & LINUX_CLONE_SETTLS) { + struct l_segment_descriptor ldesc; + + error = copyin(SCARG(uap, tls), &ldesc, sizeof(ldesc)); + if (error) + return (error); + + if (ldesc.entry_number != GUGS_SEL) + return (EINVAL); + emul->child_tls_base = ldesc.base_addr; + } + else + emul->child_tls_base = 0; + /* * Note that Linux does not provide a portable way of specifying * the stack area; the caller must know if the stack grows up * or down. So, we pass a stack size of 0, so that the code * that makes this adjustment is a noop. */ - return (fork1(p, sig, flags, SCARG(uap, stack), 0, NULL, NULL, retval, - NULL)); + error = fork1(p, sig, flags, SCARG(uap, stack), 0, linux_child_return, + p, retval, NULL); + if (error) + return error; + + if (cflags & LINUX_CLONE_PARENT_SETTID) { + pid_t pid = retval[0]; + + error = copyout(&pid, SCARG(uap, parent_tidptr), sizeof(pid)); + } + return (error); } int @@ -346,3 +388,34 @@ linux_sys_sched_get_priority_min(struct proc *cp, void *v, register_t *retval) *retval = 0; return (0); } + +int +linux_sys_set_tid_address(struct proc *p, void *v, register_t *retval) +{ + struct linux_sys_set_tid_address_args *uap = v; + struct linux_emuldata *emul = p->p_emuldata; + + emul->my_clear_tid = SCARG(uap, tidptr); + + *retval = p->p_p->ps_pid; + return 0; +} + +void +linux_child_return(void *arg) +{ + struct proc *p = (struct proc *)arg; + struct linux_emuldata *emul = p->p_emuldata; + + if (i386_set_threadbase(p, &emul->my_tls_base, TSEG_GS)) + return; + + if (emul->my_set_tid) { + pid_t pid = p->p_pid + THREAD_PID_OFFSET; + + if (copyout(&pid, emul->my_set_tid, sizeof(pid))) + psignal(p, SIGSEGV); + } + + child_return(p); +} |