summaryrefslogtreecommitdiffstats
path: root/sys/compat
diff options
context:
space:
mode:
authornetchild <netchild@FreeBSD.org>2006-08-15 12:20:59 +0000
committernetchild <netchild@FreeBSD.org>2006-08-15 12:20:59 +0000
commit2122144749c4e2d78be8c3aea038117dc798cff5 (patch)
tree0469cf9d01c3a37ebca70b1fa40bc8d84db3895b /sys/compat
parent3a1395fb4377f633170a21cf970e9731a8f75a9a (diff)
downloadFreeBSD-src-2122144749c4e2d78be8c3aea038117dc798cff5.zip
FreeBSD-src-2122144749c4e2d78be8c3aea038117dc798cff5.tar.gz
Add some new files needed for linux 2.6.x compatibility.
Please don't style(9) the NetBSD code, we want to stay in sync. Not imported on a vendor branch since we need local changes. Sponsored by: Google SoC 2006 Submitted by: rdivacky With help from: manu@NetBSD.org Obtained from: NetBSD (linux_{futex,time}.*)
Diffstat (limited to 'sys/compat')
-rw-r--r--sys/compat/linux/linux_emul.c297
-rw-r--r--sys/compat/linux/linux_emul.h74
-rw-r--r--sys/compat/linux/linux_futex.c500
-rw-r--r--sys/compat/linux/linux_futex.h61
-rw-r--r--sys/compat/linux/linux_time.c208
5 files changed, 1140 insertions, 0 deletions
diff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c
new file mode 100644
index 0000000..f3398b9
--- /dev/null
+++ b/sys/compat/linux/linux_emul.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 2006 Roman Divacky
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/proc.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/unistd.h>
+
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_futex.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+struct sx emul_shared_lock;
+struct sx emul_lock;
+
+/* this returns locked reference to the emuldata entry (if found) */
+struct linux_emuldata *
+em_find(struct proc *p, int locked)
+{
+ struct linux_emuldata *em;
+
+ if (locked == EMUL_UNLOCKED)
+ EMUL_LOCK(&emul_lock);
+
+ em = p->p_emuldata;
+
+ if (em == NULL && locked == EMUL_UNLOCKED)
+ EMUL_UNLOCK(&emul_lock);
+
+ return (em);
+}
+
+int
+linux_proc_init(struct thread *td, pid_t child, int flags)
+{
+ struct linux_emuldata *em, *p_em;
+ struct proc *p;
+
+ if (child != 0) {
+ /* non-exec call */
+ MALLOC(em, struct linux_emuldata *, sizeof *em, M_LINUX, M_WAITOK | M_ZERO);
+ em->pid = child;
+ if (flags & CLONE_VM) {
+ /* handled later in the code */
+ } else {
+ struct linux_emuldata_shared *s;
+
+ MALLOC(s, struct linux_emuldata_shared *, sizeof *s, M_LINUX, M_WAITOK | M_ZERO);
+ em->shared = s;
+ s->refs = 1;
+ s->group_pid = child;
+
+ LIST_INIT(&s->threads);
+ }
+ p = pfind(child);
+ if (p == NULL)
+ panic("process not found in proc_init\n");
+ p->p_emuldata = em;
+ PROC_UNLOCK(p);
+ } else {
+ /* lookup the old one */
+ em = em_find(td->td_proc, EMUL_UNLOCKED);
+ KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n"));
+ }
+
+ em->child_clear_tid = NULL;
+ em->child_set_tid = NULL;
+
+ /* allocate the shared struct only in clone()/fork cases
+ * in the case of clone() td = calling proc and child = pid of
+ * the newly created proc
+ */
+ if (child != 0) {
+ if (flags & CLONE_VM) {
+ /* lookup the parent */
+ p_em = em_find(td->td_proc, EMUL_LOCKED);
+ KASSERT(p_em != NULL, ("proc_init: parent emuldata not found for CLONE_VM\n"));
+ em->shared = p_em->shared;
+ em->shared->refs++;
+ } else {
+ /* handled earlier to avoid malloc(M_WAITOK) with rwlock held */
+ }
+ }
+
+
+ if (child != 0) {
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ LIST_INSERT_HEAD(&em->shared->threads, em, threads);
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+
+ p = pfind(child);
+ PROC_UNLOCK(p);
+ /* we might have a sleeping linux_schedtail */
+ wakeup(&p->p_emuldata);
+ } else
+ EMUL_UNLOCK(&emul_lock);
+
+ return (0);
+}
+
+void
+linux_proc_exit(void *arg __unused, struct proc *p)
+{
+ struct linux_emuldata *em;
+ int error;
+ struct thread *td = FIRST_THREAD_IN_PROC(p);
+ int *child_clear_tid;
+
+ if (__predict_true(p->p_sysent != &elf_linux_sysvec))
+ return;
+
+ /* find the emuldata */
+ em = em_find(p, EMUL_UNLOCKED);
+
+ KASSERT(em != NULL, ("proc_exit: emuldata not found.\n"));
+
+ child_clear_tid = em->child_clear_tid;
+
+ EMUL_UNLOCK(&emul_lock);
+
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ LIST_REMOVE(em, threads);
+
+ PROC_LOCK(p);
+ p->p_emuldata = NULL;
+ PROC_UNLOCK(p);
+
+ em->shared->refs--;
+ if (em->shared->refs == 0)
+ FREE(em->shared, M_LINUX);
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+
+ if (child_clear_tid != NULL) {
+ struct linux_sys_futex_args cup;
+ int null = 0;
+
+ error = copyout(&null, child_clear_tid, sizeof(null));
+ if (error)
+ return;
+
+ /* futexes stuff */
+ cup.uaddr = child_clear_tid;
+ cup.op = LINUX_FUTEX_WAKE;
+ cup.val = 0x7fffffff; /* Awake everyone */
+ cup.timeout = NULL;
+ cup.uaddr2 = NULL;
+ cup.val3 = 0;
+ error = linux_sys_futex(FIRST_THREAD_IN_PROC(p), &cup);
+ /* this cannot happen at the moment and if this happens
+ * it probably mean there is a userspace bug
+ */
+ if (error)
+ printf(LMSG("futex stuff in proc_exit failed.\n"));
+ }
+
+ /* clean the stuff up */
+ FREE(em, M_LINUX);
+}
+
+/* This is used in a case of transition from FreeBSD binary execing to linux binary
+ * in this case we create linux emuldata proc entry with the pid of the currently running
+ * process.
+ */
+void linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
+{
+ if (__predict_false(imgp->sysent == &elf_linux_sysvec
+ && p->p_sysent != &elf_linux_sysvec))
+ linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0);
+ if (__predict_false(imgp->sysent != &elf_linux_sysvec
+ && p->p_sysent == &elf_linux_sysvec)) {
+ struct linux_emuldata *em;
+
+ em = em_find(p, EMUL_UNLOCKED);
+
+ KASSERT(em != NULL, ("proc_exec: emuldata not found.\n"));
+
+ EMUL_UNLOCK(&emul_lock);
+
+ EMUL_SHARED_WLOCK(&emul_shared_lock);
+ LIST_REMOVE(em, threads);
+
+ PROC_LOCK(p);
+ p->p_emuldata = NULL;
+ PROC_UNLOCK(p);
+
+ em->shared->refs--;
+ if (em->shared->refs == 0)
+ FREE(em->shared, M_LINUX);
+ EMUL_SHARED_WUNLOCK(&emul_shared_lock);
+
+ FREE(em, M_LINUX);
+ }
+}
+
+extern int hz; /* in subr_param.c */
+
+void
+linux_schedtail(void *arg __unused, struct proc *p)
+{
+ struct linux_emuldata *em;
+ int error = 0;
+#ifdef DEBUG
+ struct thread *td = FIRST_THREAD_IN_PROC(p);
+#endif
+ int *child_set_tid;
+
+ if (p->p_sysent != &elf_linux_sysvec)
+ return;
+
+retry:
+ /* find the emuldata */
+ em = em_find(p, EMUL_UNLOCKED);
+
+ if (em == NULL) {
+ /* We might have been called before proc_init for this process so
+ * tsleep and be woken up by it. We use p->p_emuldata for this
+ */
+
+ error = tsleep(&p->p_emuldata, PLOCK, "linux_schedtail", hz);
+ if (error == 0)
+ goto retry;
+ panic("no emuldata found for userreting process.\n");
+ }
+ child_set_tid = em->child_set_tid;
+ EMUL_UNLOCK(&emul_lock);
+
+ if (child_set_tid != NULL)
+ error = copyout(&p->p_pid, (int *) child_set_tid, sizeof(p->p_pid));
+
+ return;
+}
+
+int
+linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args)
+{
+ struct linux_emuldata *em;
+
+#ifdef DEBUG
+ if (ldebug(set_tid_address))
+ printf(ARGS(set_tid_address, "%p"), args->tidptr);
+#endif
+
+ /* find the emuldata */
+ em = em_find(td->td_proc, EMUL_UNLOCKED);
+
+ KASSERT(em != NULL, ("set_tid_address: emuldata not found.\n"));
+
+ em->child_clear_tid = args->tidptr;
+ td->td_retval[0] = td->td_proc->p_pid;
+
+ EMUL_UNLOCK(&emul_lock);
+ return 0;
+}
diff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h
new file mode 100644
index 0000000..2eafb83
--- /dev/null
+++ b/sys/compat/linux/linux_emul.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2006 Roman Divacky
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_EMUL_H_
+#define _LINUX_EMUL_H_
+
+struct linux_emuldata_shared {
+ int refs;
+ pid_t group_pid;
+
+ LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */
+};
+
+/* modeled after similar structure in NetBSD
+ * this will be extended as we need more functionality
+ */
+struct linux_emuldata {
+ pid_t pid;
+
+ int *child_set_tid; /* in clone(): Child's TID to set on clone */
+ int *child_clear_tid; /* in clone(): Child's TID to clear on exit */
+
+ struct linux_emuldata_shared *shared;
+
+ LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */
+};
+
+struct linux_emuldata *em_find(struct proc *, int locked);
+
+#define EMUL_LOCK(l) sx_xlock(l)
+#define EMUL_UNLOCK(l) sx_xunlock(l)
+
+#define EMUL_SHARED_RLOCK(l) sx_slock(l)
+#define EMUL_SHARED_RUNLOCK(l) sx_sunlock(l)
+#define EMUL_SHARED_WLOCK(l) sx_xlock(l)
+#define EMUL_SHARED_WUNLOCK(l) sx_xunlock(l)
+
+/* for em_find use */
+#define EMUL_LOCKED 1
+#define EMUL_UNLOCKED 0
+
+int linux_proc_init(struct thread *, pid_t, int);
+void linux_proc_exit(void *, struct proc *);
+void linux_schedtail(void *, struct proc *);
+void linux_proc_exec(void *, struct proc *, struct image_params *);
+
+#endif /* !_LINUX_EMUL_H_ */
diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c
new file mode 100644
index 0000000..889d29d
--- /dev/null
+++ b/sys/compat/linux/linux_futex.c
@@ -0,0 +1,500 @@
+/* $NetBSD: linux_futex.c,v 1.5 2005/11/23 16:14:57 manu Exp $ */
+
+/*-
+ * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Emmanuel Dreyfus
+ * 4. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#if 0
+ __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.5 2005/11/23 16:14:57 manu Exp $");
+#endif
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+#include <compat/linux/linux_futex.h>
+
+struct futex;
+
+struct waiting_proc {
+ struct thread *wp_t;
+ struct futex *wp_new_futex;
+ TAILQ_ENTRY(waiting_proc) wp_list;
+};
+struct futex {
+ void *f_uaddr;
+ int f_refcount;
+ LIST_ENTRY(futex) f_list;
+ TAILQ_HEAD(lf_waiting_proc, waiting_proc) f_waiting_proc;
+};
+
+LIST_HEAD(futex_list, futex) futex_list;
+struct mtx futex_mtx; /* this protects the LIST of futexes */
+
+#define FUTEX_LOCK mtx_lock(&futex_mtx)
+#define FUTEX_UNLOCK mtx_unlock(&futex_mtx)
+
+#define FUTEX_LOCKED 1
+#define FUTEX_UNLOCKED 0
+
+#define FUTEX_SYSTEM_LOCK mtx_lock(&Giant)
+#define FUTEX_SYSTEM_UNLOCK mtx_unlock(&Giant)
+
+static struct futex *futex_get(void *, int);
+static void futex_put(struct futex *);
+static int futex_sleep(struct futex *, struct thread *, unsigned long);
+static int futex_wake(struct futex *, int, struct futex *);
+#ifdef __i386__
+static int futex_atomic_op(struct thread *td, int encoded_op, caddr_t uaddr);
+#endif
+
+/* support.s */
+int futex_xchgl(int oparg, caddr_t uaddr, int *oldval);
+int futex_addl(int oparg, caddr_t uaddr, int *oldval);
+int futex_orl(int oparg, caddr_t uaddr, int *oldval);
+int futex_andnl(int oparg, caddr_t uaddr, int *oldval);
+int futex_xorl(int oparg, caddr_t uaddr, int *oldval);
+
+int
+linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
+{
+ int val;
+ int ret;
+ struct l_timespec timeout = { 0, 0 };
+ int error = 0;
+ struct futex *f;
+ struct futex *newf;
+ int timeout_hz;
+ struct timeval tv = {0, 0};
+#ifdef __i386__
+ struct futex *f2;
+ int op_ret;
+#endif
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf(ARGS(futex,"%p, %i, %i"), args->uaddr, args->op, args->val);
+#endif
+
+ switch (args->op) {
+ case LINUX_FUTEX_WAIT:
+ FUTEX_SYSTEM_LOCK;
+
+ if ((error = copyin(args->uaddr,
+ &val, sizeof(val))) != 0) {
+ FUTEX_SYSTEM_UNLOCK;
+ return error;
+ }
+
+ if (val != args->val) {
+ FUTEX_SYSTEM_UNLOCK;
+ return EWOULDBLOCK;
+ }
+
+ if (args->timeout != NULL) {
+ if ((error = copyin(args->timeout,
+ &timeout, sizeof(timeout))) != 0) {
+ FUTEX_SYSTEM_UNLOCK;
+ return error;
+ }
+ }
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX_WAIT %d: val = %d, uaddr = %p, "
+ "*uaddr = %d, timeout = %d.%09ld\n",
+ td->td_proc->p_pid, args->val,
+ args->uaddr, val, timeout.tv_sec, timeout.tv_nsec);
+#endif
+ tv.tv_usec = timeout.tv_sec * 1000000 + timeout.tv_nsec / 1000;
+ timeout_hz = tvtohz(&tv);
+
+ if (timeout.tv_sec == 0 && timeout.tv_nsec == 0)
+ timeout_hz = 0;
+ /*
+ * If the user process requests a non null timeout,
+ * make sure we do not turn it into an infinite
+ * timeout because timeout_hz gets null.
+ *
+ * We use a minimal timeout of 1/hz. Maybe it would
+ * make sense to just return ETIMEDOUT without sleeping.
+ */
+ if (((timeout.tv_sec != 0) || (timeout.tv_nsec != 0)) &&
+ (timeout_hz == 0))
+ timeout_hz = 1;
+
+
+ f = futex_get(args->uaddr, FUTEX_UNLOCKED);
+ ret = futex_sleep(f, td, timeout_hz);
+ futex_put(f);
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX_WAIT %d: uaddr = %p, "
+ "ret = %d\n", td->td_proc->p_pid, args->uaddr, ret);
+#endif
+
+ FUTEX_SYSTEM_UNLOCK;
+ switch (ret) {
+ case EWOULDBLOCK: /* timeout */
+ return ETIMEDOUT;
+ break;
+ case EINTR: /* signal */
+ return EINTR;
+ break;
+ case 0: /* FUTEX_WAKE received */
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX_WAIT %d: uaddr = %p, got FUTEX_WAKE\n",
+ td->td_proc->p_pid, args->uaddr);
+#endif
+ return 0;
+ break;
+ default:
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX_WAIT: unexpected ret = %d\n", ret);
+#endif
+ break;
+ }
+
+ /* NOTREACHED */
+ break;
+
+ case LINUX_FUTEX_WAKE:
+ FUTEX_SYSTEM_LOCK;
+
+ /*
+ * XXX: Linux is able cope with different addresses
+ * corresponding to the same mapped memory in the sleeping
+ * and the waker process.
+ */
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX_WAKE %d: uaddr = %p, val = %d\n",
+ td->td_proc->p_pid, args->uaddr, args->val);
+#endif
+ f = futex_get(args->uaddr, FUTEX_UNLOCKED);
+ td->td_retval[0] = futex_wake(f, args->val, NULL);
+ futex_put(f);
+
+ FUTEX_SYSTEM_UNLOCK;
+ break;
+
+ case LINUX_FUTEX_CMP_REQUEUE:
+ FUTEX_SYSTEM_LOCK;
+
+ if ((error = copyin(args->uaddr,
+ &val, sizeof(val))) != 0) {
+ FUTEX_SYSTEM_UNLOCK;
+ return error;
+ }
+
+ if (val != args->val3) {
+ FUTEX_SYSTEM_UNLOCK;
+ return EAGAIN;
+ }
+
+ f = futex_get(args->uaddr, FUTEX_UNLOCKED);
+ newf = futex_get(args->uaddr2, FUTEX_UNLOCKED);
+ td->td_retval[0] = futex_wake(f, args->val, newf);
+ futex_put(f);
+ futex_put(newf);
+
+ FUTEX_SYSTEM_UNLOCK;
+ break;
+
+ case LINUX_FUTEX_REQUEUE:
+ FUTEX_SYSTEM_LOCK;
+
+ f = futex_get(args->uaddr, FUTEX_UNLOCKED);
+ newf = futex_get(args->uaddr2, FUTEX_UNLOCKED);
+ td->td_retval[0] = futex_wake(f, args->val, newf);
+ futex_put(f);
+ futex_put(newf);
+
+ FUTEX_SYSTEM_UNLOCK;
+ break;
+
+ case LINUX_FUTEX_FD:
+ printf("linux_sys_futex: unimplemented op %d\n",
+ args->op);
+ break;
+
+ case LINUX_FUTEX_WAKE_OP:
+#ifdef __i386__
+ FUTEX_SYSTEM_LOCK;
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX_WAKE_OP: %d: uaddr = %p, op = %d, val = %d, uaddr2 = %p, val3 = %d\n",
+ td->td_proc->p_pid, args->uaddr, args->op, args->val, args->uaddr2, args->val3);
+#endif
+ f = futex_get(args->uaddr, FUTEX_UNLOCKED);
+ f2 = futex_get(args->uaddr2, FUTEX_UNLOCKED);
+
+ /* This function returns positive number as results
+ * and negative as errors
+ */
+ op_ret = futex_atomic_op(td, args->val3, args->uaddr2);
+ if (op_ret < 0) {
+
+ /* XXX: we dont handle the EFAULT yet */
+ if (op_ret != -EFAULT) {
+ futex_put(f);
+ futex_put(f2);
+ FUTEX_SYSTEM_UNLOCK;
+ return (-op_ret);
+ }
+
+ futex_put(f);
+ futex_put(f2);
+
+ FUTEX_SYSTEM_UNLOCK;
+ return (EFAULT);
+
+ }
+
+ ret = futex_wake(f, args->val, NULL);
+ futex_put(f);
+ if (op_ret > 0) {
+ printf("second wakeup\n");
+ op_ret = 0;
+ /* Linux always puts there 0 retries */
+ op_ret += futex_wake(f2, 0, NULL);
+ ret += op_ret;
+ }
+ futex_put(f2);
+ td->td_retval[0] = ret;
+
+ FUTEX_SYSTEM_UNLOCK;
+#else
+ printf("linux_sys_futex: wake_op not implemented");
+#endif
+ break;
+
+ default:
+ printf("linux_sys_futex: unknown op %d\n",
+ args->op);
+ break;
+ }
+ return 0;
+}
+
+static struct futex *
+futex_get(void *uaddr, int locked)
+{
+ struct futex *f;
+
+ if (locked == FUTEX_UNLOCKED)
+ FUTEX_LOCK;
+ LIST_FOREACH(f, &futex_list, f_list) {
+ if (f->f_uaddr == uaddr) {
+ f->f_refcount++;
+ if (locked == FUTEX_UNLOCKED)
+ FUTEX_UNLOCK;
+ return f;
+ }
+ }
+ if (locked == FUTEX_UNLOCKED)
+ FUTEX_UNLOCK;
+
+ /* Not found, create it */
+ f = malloc(sizeof(*f), M_LINUX, M_WAITOK);
+ f->f_uaddr = uaddr;
+ f->f_refcount = 1;
+ TAILQ_INIT(&f->f_waiting_proc);
+ if (locked == FUTEX_UNLOCKED)
+ FUTEX_LOCK;
+ LIST_INSERT_HEAD(&futex_list, f, f_list);
+ if (locked == FUTEX_UNLOCKED)
+ FUTEX_UNLOCK;
+
+ return f;
+}
+
+static void
+futex_put(f)
+ struct futex *f;
+{
+ FUTEX_LOCK;
+ f->f_refcount--;
+ if (f->f_refcount == 0) {
+ LIST_REMOVE(f, f_list);
+ free(f, M_LINUX);
+ }
+ FUTEX_UNLOCK;
+
+ return;
+}
+
+static int
+futex_sleep(struct futex *f, struct thread *td, unsigned long timeout)
+{
+ struct waiting_proc *wp;
+ int ret;
+
+ wp = malloc(sizeof(*wp), M_LINUX, M_WAITOK);
+ wp->wp_t = td;
+ wp->wp_new_futex = NULL;
+ FUTEX_LOCK;
+ TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list);
+ FUTEX_UNLOCK;
+
+#ifdef DEBUG
+ if (ldebug(sys_futex))
+ printf("FUTEX --> %d tlseep timeout = %ld\n", td->td_proc->p_pid,
+ timeout);
+#endif
+ ret = tsleep(wp, PCATCH|PZERO, "linuxfutex", timeout);
+
+ FUTEX_LOCK;
+ TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
+ FUTEX_UNLOCK;
+
+ if ((ret == 0) && (wp->wp_new_futex != NULL)) {
+ ret = futex_sleep(wp->wp_new_futex, td, timeout);
+ futex_put(wp->wp_new_futex); /* futex_get called in wakeup */
+ }
+
+ free(wp, M_LINUX);
+
+ return ret;
+}
+
+static int
+futex_wake(struct futex *f, int n, struct futex *newf)
+{
+ struct waiting_proc *wp;
+ int count = 0;
+
+ FUTEX_LOCK;
+ TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) {
+ if (count <= n) {
+ wakeup(wp);
+ count++;
+ } else {
+ if (newf != NULL) {
+ /* futex_put called after tsleep */
+ wp->wp_new_futex = futex_get(newf->f_uaddr, FUTEX_LOCKED);
+ wakeup(wp);
+ }
+ }
+ }
+ FUTEX_UNLOCK;
+
+ return count;
+}
+
+#ifdef __i386__
+static int
+futex_atomic_op(struct thread *td, int encoded_op, caddr_t uaddr)
+{
+ int op = (encoded_op >> 28) & 7;
+ int cmp = (encoded_op >> 24) & 15;
+ int oparg = (encoded_op << 8) >> 20;
+ int cmparg = (encoded_op << 20) >> 20;
+ int oldval = 0, ret;
+
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
+ oparg = 1 << oparg;
+
+#ifdef DEBUG
+ printf("futex_atomic_op: op = %d, cmp = %d, oparg = %d, cmparg = %d, uaddr = %p\n",
+ op, cmp, oparg, cmparg, uaddr);
+#endif
+ /* XXX: linux verifies access here and returns EFAULT */
+
+ critical_enter();
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ ret = futex_xchgl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_ADD:
+ ret = futex_addl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_OR:
+ ret = futex_orl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_ANDN:
+ ret = futex_andnl(oparg, uaddr, &oldval);
+ break;
+ case FUTEX_OP_XOR:
+ ret = futex_xorl(oparg, uaddr, &oldval);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ critical_exit();
+
+ if (!ret)
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ:
+ ret = (oldval == cmparg);
+ break;
+ case FUTEX_OP_CMP_NE:
+ ret = (oldval != cmparg);
+ break;
+ case FUTEX_OP_CMP_LT:
+ ret = (oldval < cmparg);
+ break;
+ case FUTEX_OP_CMP_GE:
+ ret = (oldval >= cmparg);
+ break;
+ case FUTEX_OP_CMP_LE:
+ ret = (oldval <= cmparg);
+ break;
+ case FUTEX_OP_CMP_GT:
+ ret = (oldval > cmparg);
+ break;
+ default: ret = -ENOSYS;
+ }
+
+ return (ret);
+}
+#endif
diff --git a/sys/compat/linux/linux_futex.h b/sys/compat/linux/linux_futex.h
new file mode 100644
index 0000000..8eafee1
--- /dev/null
+++ b/sys/compat/linux/linux_futex.h
@@ -0,0 +1,61 @@
+/* $NetBSD: linux_futex.h,v 1.2 2005/12/11 12:20:19 christos Exp $ */
+
+/*-
+ * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Emmanuel Dreyfus
+ * 4. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_FUTEX_H
+#define _LINUX_FUTEX_H
+
+#define LINUX_FUTEX_WAIT 0
+#define LINUX_FUTEX_WAKE 1
+#define LINUX_FUTEX_FD 2
+#define LINUX_FUTEX_REQUEUE 3
+#define LINUX_FUTEX_CMP_REQUEUE 4
+#define LINUX_FUTEX_WAKE_OP 5
+
+#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */
+#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */
+#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */
+#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */
+#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */
+
+#define FUTEX_OP_OPARG_SHIFT 8 /* Use (1 << OPARG) instead of OPARG. */
+
+#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */
+#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */
+#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */
+#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */
+#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */
+#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */
+
+#endif /* !_LINUX_FUTEX_H */
diff --git a/sys/compat/linux/linux_time.c b/sys/compat/linux/linux_time.c
new file mode 100644
index 0000000..aee0554
--- /dev/null
+++ b/sys/compat/linux/linux_time.c
@@ -0,0 +1,208 @@
+/* $NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Emmanuel Dreyfus.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#if 0
+__KERNEL_RCSID(0, "$NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $");
+#endif
+
+#include "opt_compat.h"
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/signal.h>
+#include <sys/stdint.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#ifdef COMPAT_LINUX32
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <machine/../linux/linux.h>
+#include <machine/../linux/linux_proto.h>
+#endif
+
+static void native_to_linux_timespec(struct l_timespec *,
+ struct timespec *);
+static void linux_to_native_timespec(struct timespec *,
+ struct l_timespec *);
+static int linux_to_native_clockid(clockid_t *, clockid_t);
+
+static void
+native_to_linux_timespec(struct l_timespec *ltp, struct timespec *ntp)
+{
+ ltp->tv_sec = ntp->tv_sec;
+ ltp->tv_nsec = ntp->tv_nsec;
+}
+
+static void
+linux_to_native_timespec(struct timespec *ntp, struct l_timespec *ltp)
+{
+ ntp->tv_sec = ltp->tv_sec;
+ ntp->tv_nsec = ltp->tv_nsec;
+}
+
+static int
+linux_to_native_clockid(clockid_t *n, clockid_t l)
+{
+ switch (l) {
+ case LINUX_CLOCK_REALTIME:
+ *n = CLOCK_REALTIME;
+ break;
+ case LINUX_CLOCK_MONOTONIC:
+ *n = CLOCK_MONOTONIC;
+ break;
+ case LINUX_CLOCK_PROCESS_CPUTIME_ID:
+ case LINUX_CLOCK_THREAD_CPUTIME_ID:
+ case LINUX_CLOCK_REALTIME_HR:
+ case LINUX_CLOCK_MONOTONIC_HR:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int
+linux_clock_gettime(struct thread *td, struct linux_clock_gettime_args *args)
+{
+ struct l_timespec lts;
+ int error;
+ clockid_t nwhich = 0; /* XXX: GCC */
+ struct timespec tp;
+
+ error = linux_to_native_clockid(&nwhich, args->which);
+ if (error != 0)
+ return error;
+
+ error = kern_clock_gettime(td, nwhich, &tp);
+ if (error != 0)
+ return error;
+
+ native_to_linux_timespec(&lts, &tp);
+
+ return copyout(&lts, args->tp, sizeof lts);
+}
+
+int
+linux_clock_settime(struct thread *td, struct linux_clock_settime_args *args)
+{
+ struct timespec ts;
+ struct l_timespec lts;
+ int error;
+ clockid_t nwhich = 0; /* XXX: GCC */
+
+ error = linux_to_native_clockid(&nwhich, args->which);
+ if (error != 0)
+ return error;
+
+ error = copyin(args->tp, &lts, sizeof lts);
+ if (error != 0)
+ return error;
+
+ linux_to_native_timespec(&ts, &lts);
+
+ return kern_clock_settime(td, nwhich, &ts);
+}
+
+int
+linux_clock_getres(struct thread *td, struct linux_clock_getres_args *args)
+{
+ struct timespec ts;
+ struct l_timespec lts;
+ int error;
+ clockid_t nwhich = 0; /* XXX: GCC */
+
+ if (args->tp == NULL)
+ return (0);
+
+ error = linux_to_native_clockid(&nwhich, args->which);
+ if (error != 0)
+ return error;
+
+ error = kern_clock_getres(td, nwhich, &ts);
+ if (error != 0)
+ return error;
+
+ native_to_linux_timespec(&lts, &ts);
+
+ return copyout(&lts, args->tp, sizeof lts);
+}
+
+int
+linux_clock_nanosleep(struct thread *td, struct linux_clock_nanosleep_args *args)
+{
+ struct timespec *rmtp;
+ struct l_timespec lrqts, lrmts;
+ struct timespec rqts, rmts;
+ int error;
+
+ if (args->flags != 0)
+ return EINVAL; /* XXX deal with TIMER_ABSTIME */
+
+ if (args->which != LINUX_CLOCK_REALTIME)
+ return EINVAL;
+
+ error = copyin(args->rqtp, &lrqts, sizeof lrqts);
+ if (error != 0)
+ return error;
+
+ if (args->rmtp != NULL)
+ rmtp = &rmts;
+ else
+ rmtp = NULL;
+
+ linux_to_native_timespec(&rqts, &lrqts);
+
+ error = kern_nanosleep(td, &rqts, rmtp);
+ if (error != 0)
+ return error;
+ if (args->rmtp != NULL) {
+ native_to_linux_timespec(&lrmts, rmtp);
+ error = copyout(&lrmts, args->rmtp, sizeof lrmts );
+ if (error != 0)
+ return error;
+ }
+
+ return 0;
+}
OpenPOWER on IntegriCloud