[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 14/19] bsd-user: add support for thread related s
From: |
Stacey Son |
Subject: |
[Qemu-devel] [PATCH v3 14/19] bsd-user: add support for thread related system calls |
Date: |
Tue, 17 Dec 2013 05:52:33 -0600 |
This change adds support or stubs for thread related system calls
including thr_create(), thr_new(), thr_set_name(), thr_self(),
thr_suspend(), thr_wake(), thr_kill(), thr_kill2(),
rtprio_thread(2), getcontext(2), setcontext(2), swapcontext(2),
_umtx_lock(), _umtx_unlock(), and _umtx_op().
---
bsd-user/Makefile.objs | 2 +-
bsd-user/freebsd/os-thread.c | 1001 ++++++++++++++++++++++++++++++++++++++++++
bsd-user/freebsd/os-thread.h | 511 +++++++++++++++++++++
bsd-user/freebsd/qemu-os.h | 6 +
bsd-user/netbsd/os-thread.c | 1 +
bsd-user/netbsd/os-thread.h | 133 ++++++
bsd-user/openbsd/os-thread.c | 1 +
bsd-user/openbsd/os-thread.h | 133 ++++++
bsd-user/qemu.h | 59 +++-
bsd-user/syscall.c | 71 +++-
include/qemu/tls.h | 2 +-
11 files changed, 1908 insertions(+), 12 deletions(-)
create mode 100644 bsd-user/freebsd/os-thread.c
create mode 100644 bsd-user/freebsd/os-thread.h
create mode 100644 bsd-user/netbsd/os-thread.c
create mode 100644 bsd-user/netbsd/os-thread.h
create mode 100644 bsd-user/openbsd/os-thread.c
create mode 100644 bsd-user/openbsd/os-thread.h
diff --git a/bsd-user/Makefile.objs b/bsd-user/Makefile.objs
index 635d879..fdc1c9a 100644
--- a/bsd-user/Makefile.objs
+++ b/bsd-user/Makefile.objs
@@ -2,5 +2,5 @@ obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o
syscall.o \
uaccess.o bsd-mem.o bsd-proc.o bsd-socket.o \
$(HOST_VARIANT_DIR)/os-proc.o \
$(HOST_VARIANT_DIR)/os-socket.o
$(HOST_VARIANT_DIR)/os-stat.o \
- $(HOST_VARIANT_DIR)/os-sys.o \
+ $(HOST_VARIANT_DIR)/os-sys.o
$(HOST_VARIANT_DIR)/os-thread.o \
$(HOST_VARIANT_DIR)/os-time.o
$(TARGET_ABI_DIR)/target_arch_cpu.o
diff --git a/bsd-user/freebsd/os-thread.c b/bsd-user/freebsd/os-thread.c
new file mode 100644
index 0000000..6bf2a9f
--- /dev/null
+++ b/bsd-user/freebsd/os-thread.c
@@ -0,0 +1,1001 @@
+/*
+ * FreeBSD thr emulation support code
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/thr.h>
+#include <sys/umtx.h>
+#include <sys/rtprio.h>
+
+#include <machine/atomic.h>
+
+#include <time.h>
+
+#include "qemu.h"
+#include "qemu-os.h"
+#include "target_arch_cpu.h"
+#include "target_arch_thread.h"
+
+// #define DEBUG_UMTX(...) fprintf(stderr, __VA_ARGS__)
+#define DEBUG_UMTX(...)
+
+#define NEW_STACK_SIZE 0x40000
+
+/* sys/_umtx.h */
+struct target_umtx {
+ abi_ulong u_owner; /* Owner of the mutex. */
+};
+
+struct target_umutex {
+ uint32_t m_owner; /* Owner of the mutex */
+ uint32_t m_flags; /* Flags of the mutex */
+ uint32_t m_ceiling[2]; /* Priority protect ceiling */
+ uint32_t m_spare[4];
+};
+
+struct target_ucond {
+ uint32_t c_has_waiters; /* Has waiters in kernel */
+ uint32_t c_flags; /* Flags of the condition variable */
+ uint32_t c_clockid; /* Clock id */
+ uint32_t c_spare[1];
+};
+
+struct target_urwlock {
+ uint32_t rw_state;
+ uint32_t rw_flags;
+ uint32_t rw_blocked_readers;
+ uint32_t rw_blocked_writers;
+ uint32_t rw_spare[4];
+};
+
+struct target__usem {
+ uint32_t _has_waiters;
+ uint32_t _count;
+ uint32_t _flags;
+};
+
+static pthread_mutex_t new_thread_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t *new_freebsd_thread_lock_ptr = &new_thread_lock;
+
+static pthread_mutex_t umtx_wait_lck = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t umtx_wait_cv = PTHREAD_COND_INITIALIZER;
+static abi_ulong umtx_wait_addr;
+
+static void rtp_to_schedparam(const struct rtprio *rtp, int *policy,
+ struct sched_param *param)
+{
+
+ switch (rtp->type) {
+ case RTP_PRIO_REALTIME:
+ *policy = SCHED_RR;
+ param->sched_priority = RTP_PRIO_MAX - rtp->prio;
+ break;
+
+ case RTP_PRIO_FIFO:
+ *policy = SCHED_FIFO;
+ param->sched_priority = RTP_PRIO_MAX - rtp->prio;
+ break;
+
+ default:
+ *policy = SCHED_OTHER;
+ param->sched_priority = 0;
+ break;
+ }
+}
+
+void *new_freebsd_thread_start(void *arg)
+{
+ new_freebsd_thread_info_t *info = arg;
+ CPUArchState *env;
+ CPUState *cpu;
+ // TaskState *ts;
+ long tid;
+
+ env = info->env;
+ cpu = ENV_GET_CPU(env);
+ thread_cpu = cpu;
+
+ // ts = (TaskState *)env->opaque;
+ (void)thr_self(&tid);
+ cpu->host_tid = tid;
+ // ts->ts_tid = tid;
+
+ /* copy out the TID info */
+ if (info->param.child_tid) {
+ put_user(tid, info->param.child_tid, abi_long);
+ }
+ if (info->param.parent_tid) {
+ put_user(info->parent_tid, info->param.parent_tid, abi_long);
+ }
+
+ /* Set arch dependent registers to start thread. */
+ target_thread_set_upcall(env, info->param.start_func, info->param.arg,
+ info->param.stack_base, info->param.stack_size);
+
+ /* Enable signals */
+ sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
+ /* Signal to the parent that we're ready. */
+ pthread_mutex_lock(&info->mutex);
+ pthread_cond_broadcast(&info->cond);
+ pthread_mutex_unlock(&info->mutex);
+ /* Wait until the parent has finished initializing the TLS state. */
+ pthread_mutex_lock(new_freebsd_thread_lock_ptr);
+ pthread_mutex_unlock(new_freebsd_thread_lock_ptr);
+
+ cpu_loop(env);
+ /* never exits */
+
+ return NULL;
+}
+
+/*
+ * FreeBSD user mutex (_umtx) emulation
+ */
+static int tcmpset_al(abi_ulong *addr, abi_ulong a, abi_ulong b)
+{
+ abi_ulong current = tswapal(a);
+ abi_ulong new = tswapal(b);
+
+#ifdef TARGET_ABI32
+ return atomic_cmpset_acq_32(addr, current, new);
+#else
+ return atomic_cmpset_acq_64(addr, current, new);
+#endif
+}
+
+static int tcmpset_32(uint32_t *addr, uint32_t a, uint32_t b)
+{
+ uint32_t current = tswap32(a);
+ uint32_t new = tswap32(b);
+
+ return atomic_cmpset_acq_32(addr, current, new);
+}
+
+abi_long freebsd_umtx_wait_uint(abi_ulong obj, uint32_t val,
+ struct timespec *timeout)
+{
+
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x, NULL, %p)\n",
+ __func__, g2h(obj), UMTX_OP_WAIT_UINT, val, timeout);
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_WAIT_UINT, val, NULL,
timeout));
+}
+
+abi_long freebsd_umtx_wait_uint_private(abi_ulong obj, uint32_t val,
+ struct timespec *timeout)
+{
+
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x, NULL, %p)\n",
+ __func__, g2h(obj), UMTX_OP_WAIT_UINT_PRIVATE, val, timeout);
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_WAIT_UINT_PRIVATE, val, NULL,
+ timeout));
+}
+
+abi_long freebsd_umtx_wake_private(abi_ulong obj, uint32_t val)
+{
+
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(obj), UMTX_OP_WAKE_PRIVATE, val);
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_WAKE_PRIVATE, val, NULL,
NULL));
+}
+
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+#if defined(UMTX_OP_NWAKE_PRIVATE)
+abi_long freebsd_umtx_nwake_private(abi_ulong obj, uint32_t val)
+{
+
+ DEBUG_UMTX("<NWAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(obj), UMTX_OP_NWAKE_PRIVATE, val);
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_NWAKE_PRIVATE, val, NULL,
+ NULL));
+}
+#endif /* UMTX_OP_NWAKE_PRIVATE */
+
+#if defined(UMTX_OP_MUTEX_WAKE2)
+abi_long freebsd_umtx_mutex_wake2(abi_ulong target_addr,
+ __unused uint32_t flags)
+{
+ abi_long ret = 0;
+
+ pthread_mutex_lock(&umtx_wait_lck);
+ DEBUG_UMTX("<WAKE2 CV> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_addr), UMTX_OP_MUTEX_WAKE2, flags);
+ umtx_wait_addr = target_addr;
+ ret = get_errno(pthread_cond_broadcast(&umtx_wait_cv));
+ pthread_mutex_unlock(&umtx_wait_lck);
+
+ return ret;
+}
+#endif /* UMTX_OP_MUTEX_WAKE2 */
+
+abi_long freebsd_umtx_sem_wait(abi_ulong obj, struct timespec *timeout)
+{
+
+ /* XXX Assumes struct _usem is opauque to the user */
+ if (!access_ok(VERIFY_WRITE, obj, sizeof(struct target__usem))) {
+ return -TARGET_EFAULT;
+ }
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_SEM_WAIT, 0, NULL, timeout));
+}
+
+abi_long freebsd_umtx_sem_wake(abi_ulong obj, uint32_t val)
+{
+
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_SEM_WAKE, val, NULL, NULL));
+}
+#endif
+
+abi_long t2h_freebsd_rtprio(struct rtprio *host_rtp, abi_ulong target_addr)
+{
+ struct target_freebsd_rtprio *target_rtp;
+
+ if (!lock_user_struct(VERIFY_READ, target_rtp, target_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(host_rtp->type, &target_rtp->type);
+ __get_user(host_rtp->prio, &target_rtp->prio);
+ unlock_user_struct(target_rtp, target_addr, 0);
+ return 0;
+}
+
+abi_long h2t_freebsd_rtprio(abi_ulong target_addr, struct rtprio *host_rtp)
+{
+ struct target_freebsd_rtprio *target_rtp;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_rtp, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __put_user(host_rtp->type, &target_rtp->type);
+ __put_user(host_rtp->prio, &target_rtp->prio);
+ unlock_user_struct(target_rtp, target_addr, 1);
+ return 0;
+}
+
+abi_long freebsd_lock_umtx(abi_ulong target_addr, abi_long id,
+ struct timespec *timeout)
+{
+ abi_long ret;
+ abi_long owner;
+
+ /*
+ * XXX Note that memory at umtx_addr can change and so we need to be
+ * careful and check for faults.
+ */
+ for (;;) {
+ struct target_umtx *target_umtx;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umtx, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ /* Check the simple uncontested case. */
+ if (tcmpset_al(&target_umtx->u_owner,
+ TARGET_UMTX_UNOWNED, id)) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return 0;
+ }
+ /* Check to see if the lock is contested but free. */
+ __get_user(owner, &target_umtx->u_owner);
+
+ if (TARGET_UMTX_CONTESTED == owner) {
+ if (tcmpset_al(&target_umtx->u_owner, TARGET_UMTX_CONTESTED,
+ id | TARGET_UMTX_CONTESTED)) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return 0;
+ }
+ /* We failed because it changed on us, restart. */
+ unlock_user_struct(target_umtx, target_addr, 1);
+ continue;
+ }
+
+ /* Set the contested bit and sleep. */
+ do {
+ __get_user(owner, &target_umtx->u_owner);
+ if (owner & TARGET_UMTX_CONTESTED) {
+ break;
+ }
+ } while (!tcmpset_al(&target_umtx->u_owner, owner,
+ owner | TARGET_UMTX_CONTESTED));
+
+ __get_user(owner, &target_umtx->u_owner);
+ unlock_user_struct(target_umtx, target_addr, 1);
+
+ /* Byte swap, if needed, to match what is stored in user mem. */
+ owner = tswapal(owner);
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%llx, NULL, NULL)\n",
+ __func__, g2h(target_addr), UMTX_OP_WAIT, (long long)owner);
+#ifdef TARGET_ABI32
+ ret = get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAIT_UINT, owner,
+ NULL, timeout));
+#else
+ ret = get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAIT, owner,
+ NULL, timeout));
+#endif
+ if (is_error(ret)) {
+ return ret;
+ }
+ }
+}
+
+abi_long freebsd_unlock_umtx(abi_ulong target_addr, abi_long id)
+{
+ abi_ulong owner;
+ struct target_umtx *target_umtx;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umtx, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(owner, &target_umtx->u_owner);
+ if ((owner & ~TARGET_UMTX_CONTESTED) != id) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ /* Check the simple uncontested case. */
+ if ((owner & ~TARGET_UMTX_CONTESTED) == 0) {
+ if (tcmpset_al(&target_umtx->u_owner, owner,
+ TARGET_UMTX_UNOWNED)) {
+ unlock_user_struct(target_umtx, target_addr, 1);
+ return 0;
+ }
+ }
+ /* This is a contested lock. Unlock it. */
+ __put_user(TARGET_UMTX_UNOWNED, &target_umtx->u_owner);
+ unlock_user_struct(target_umtx, target_addr, 1);
+
+ /* Wake up all those contesting it. */
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_addr), UMTX_OP_WAKE, 0);
+ _umtx_op(g2h(target_addr), UMTX_OP_WAKE, 0, 0, 0);
+
+ return 0;
+}
+
+abi_long freebsd_umtx_wait(abi_ulong targ_addr, abi_ulong id,
+ struct timespec *ts)
+{
+
+ /* We want to check the user memory but not lock it. We might sleep. */
+ if (!access_ok(VERIFY_READ, targ_addr, sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%llx, NULL, NULL)\n",
+ __func__, g2h(targ_addr), UMTX_OP_WAIT, (long long)id);
+#ifdef TARGET_ABI32
+ return get_errno(_umtx_op(g2h(targ_addr), UMTX_OP_WAIT_UINT, id, NULL,
ts));
+#else
+ return get_errno(_umtx_op(g2h(targ_addr), UMTX_OP_WAIT, id, NULL, ts));
+#endif
+}
+
+abi_long freebsd_umtx_wake(abi_ulong target_addr, uint32_t n_wake)
+{
+
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_addr), UMTX_OP_WAKE, n_wake);
+ return get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAKE, n_wake, NULL,
0));
+}
+
+abi_long freebsd_umtx_mutex_wake(abi_ulong obj, abi_long val)
+{
+
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%llx, NULL, NULL)\n",
+ __func__, g2h(obj), UMTX_OP_WAKE, (long long)val);
+ return get_errno(_umtx_op(g2h(obj), UMTX_OP_MUTEX_WAKE, val, NULL, NULL));
+}
+
+abi_long freebsd_lock_umutex(abi_ulong target_addr, uint32_t id,
+ struct timespec *ts, int mode)
+{
+ uint32_t owner, flags;
+ int ret = 0;
+
+ for (;;) {
+ struct target_umutex *target_umutex;
+
+ pthread_mutex_lock(&umtx_wait_lck);
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) {
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(owner, &target_umutex->m_owner);
+
+ if (TARGET_UMUTEX_WAIT == mode) {
+ if (TARGET_UMUTEX_UNOWNED == owner ||
+ TARGET_UMUTEX_CONTESTED == owner) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return 0;
+ }
+ } else {
+ if (tcmpset_32(&target_umutex->m_owner, TARGET_UMUTEX_UNOWNED,
+ id)) {
+ /* The acquired succeeded. */
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return 0;
+ }
+
+ /* If no one owns it but it is contested try to acquire it. */
+ if (TARGET_UMUTEX_CONTESTED == owner) {
+ if (tcmpset_32(&target_umutex->m_owner,
TARGET_UMUTEX_CONTESTED,
+ id | TARGET_UMUTEX_CONTESTED)) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return 0;
+ }
+ /* The lock changed so restart. */
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ continue;
+ }
+ }
+
+ __get_user(flags, &target_umutex->m_flags);
+ if ((flags & TARGET_UMUTEX_ERROR_CHECK) != 0 &&
+ (owner & ~TARGET_UMUTEX_CONTESTED) == id) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return -TARGET_EDEADLK;
+ }
+
+ if (TARGET_UMUTEX_TRY == mode) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return -TARGET_EBUSY;
+ }
+
+ /*
+ * If we caught a signal, we have retried and now
+ * exit immediately.
+ */
+ if (is_error(ret)) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ return ret;
+ }
+
+ /* Set the contested bit and sleep. */
+ if (!tcmpset_32(&target_umutex->m_owner, owner,
+ owner | TARGET_UMUTEX_CONTESTED)) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ pthread_mutex_unlock(&umtx_wait_lck);
+ continue;
+ }
+
+ DEBUG_UMTX("<WAIT CV> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_addr), UMTX_OP_WAIT_UINT,
+ tswap32(target_umutex->m_owner));
+ unlock_user_struct(target_umutex, target_addr, 1);
+
+again:
+ if (ts == NULL) {
+ ret = get_errno(pthread_cond_wait(&umtx_wait_cv,
+ &umtx_wait_lck));
+ } else {
+ ret = get_errno(pthread_cond_timedwait(&umtx_wait_cv,
+ &umtx_wait_lck, ts));
+ }
+ if (ret != 0) {
+ pthread_mutex_unlock(&umtx_wait_lck);
+ break;
+ }
+ if (target_addr != umtx_wait_addr) {
+ goto again;
+ }
+ pthread_mutex_unlock(&umtx_wait_lck);
+ }
+
+ return ret;
+}
+
+abi_long freebsd_unlock_umutex(abi_ulong target_addr, uint32_t id)
+{
+ struct target_umutex *target_umutex;
+ uint32_t owner;
+
+
+ if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ /* Make sure we own this mutex. */
+ __get_user(owner, &target_umutex->m_owner);
+ if ((owner & ~TARGET_UMUTEX_CONTESTED) != id) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ if ((owner & TARGET_UMUTEX_CONTESTED) == 0) {
+ if (tcmpset_32(&target_umutex->m_owner, owner, TARGET_UMTX_UNOWNED)) {
+ unlock_user_struct(target_umutex, target_addr, 1);
+ return 0;
+ }
+ }
+ /* This is a contested lock. Unlock it. */
+ __put_user(TARGET_UMUTEX_UNOWNED, &target_umutex->m_owner);
+ unlock_user_struct(target_umutex, target_addr, 1);
+
+ /* And wake up all those contesting it. */
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_addr), UMTX_OP_WAKE, 0);
+ return get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAKE, 0, 0, 0));
+}
+
+/*
+ * _cv_mutex is keeps other threads from doing a signal or broadcast until
+ * the thread is actually asleep and ready. This is a global mutex for all
+ * condition vars so I am sure performance may be a problem if there are lots
+ * of CVs.
+ */
+static struct umutex _cv_mutex = { 0, 0, { 0, 0 }, { 0, 0, 0, 0 } };
+
+
+/*
+ * wflags CVWAIT_CHECK_UNPARKING, CVWAIT_ABSTIME, CVWAIT_CLOCKID
+ */
+abi_long freebsd_cv_wait(abi_ulong target_ucond_addr,
+ abi_ulong target_umtx_addr, struct timespec *ts, int wflags)
+{
+ abi_long ret;
+ long tid;
+
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ /* Check the clock ID if needed. */
+ if ((wflags & TARGET_CVWAIT_CLOCKID) != 0) {
+ struct target_ucond *target_ucond;
+ uint32_t clockid;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_ucond, target_ucond_addr,
+ 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(clockid, &target_ucond->c_clockid);
+ unlock_user_struct(target_ucond, target_ucond_addr, 1);
+ if (clockid >= CLOCK_THREAD_CPUTIME_ID) {
+ /* Only HW clock id will work. */
+ return -TARGET_EINVAL;
+ }
+ }
+
+ thr_self(&tid);
+
+ /* Lock the _cv_mutex so we can safely unlock the user mutex */
+ _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL);
+
+ /* unlock the user mutex */
+ ret = freebsd_unlock_umutex(target_umtx_addr, tid);
+ if (is_error(ret)) {
+ _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+ return ret;
+ }
+
+ /* UMTX_OP_CV_WAIT unlocks _cv_mutex */
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x, %p, NULL)\n",
+ __func__, g2h(target_ucond_addr), UMTX_OP_CV_WAIT, wflags,
+ &_cv_mutex);
+ ret = get_errno(_umtx_op(g2h(target_ucond_addr), UMTX_OP_CV_WAIT, wflags,
+ &_cv_mutex, ts));
+
+ return ret;
+}
+
+abi_long freebsd_cv_signal(abi_ulong target_ucond_addr)
+{
+ abi_long ret;
+
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ /* Lock the _cv_mutex to prevent a race in do_cv_wait(). */
+ _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL);
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_ucond_addr), UMTX_OP_CV_SIGNAL, 0);
+ ret = get_errno(_umtx_op(g2h(target_ucond_addr), UMTX_OP_CV_SIGNAL, 0,
+ NULL, NULL));
+ _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+
+ return ret;
+}
+
+abi_long freebsd_cv_broadcast(abi_ulong target_ucond_addr)
+{
+ int ret;
+
+ if (!access_ok(VERIFY_WRITE, target_ucond_addr,
+ sizeof(struct target_ucond))) {
+ return -TARGET_EFAULT;
+ }
+
+ /* Lock the _cv_mutex to prevent a race in do_cv_wait(). */
+ _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL);
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, g2h(target_ucond_addr), UMTX_OP_CV_BROADCAST, 0);
+ ret = get_errno(_umtx_op(g2h(target_ucond_addr), UMTX_OP_CV_BROADCAST, 0,
+ NULL, NULL));
+ _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL);
+
+ return ret;
+}
+
+abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag,
+ struct timespec *ts)
+{
+ struct target_urwlock *target_urwlock;
+ uint32_t flags, wrflags;
+ uint32_t state;
+ uint32_t blocked_readers;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(flags, &target_urwlock->rw_flags);
+ wrflags = TARGET_URWLOCK_WRITE_OWNER;
+ if (!(fflag & TARGET_URWLOCK_PREFER_READER) &&
+ !(flags & TARGET_URWLOCK_PREFER_READER)) {
+ wrflags |= TARGET_URWLOCK_WRITE_WAITERS;
+ }
+ for (;;) {
+ __get_user(state, &target_urwlock->rw_state);
+ /* try to lock it */
+ while (!(state & wrflags)) {
+ if (TARGET_URWLOCK_READER_COUNT(state) ==
+ TARGET_URWLOCK_MAX_READERS) {
+ unlock_user_struct(target_urwlock,
+ target_addr, 1);
+ return -TARGET_EAGAIN;
+ }
+ if (tcmpset_32(&target_urwlock->rw_state, state,
+ (state + 1))) {
+ /* The acquired succeeded. */
+ unlock_user_struct(target_urwlock,
+ target_addr, 1);
+ return 0;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+ /* set read contention bit */
+ if (!tcmpset_32(&target_urwlock->rw_state, state,
+ state | TARGET_URWLOCK_READ_WAITERS)) {
+ /* The state has changed. Start over. */
+ continue;
+ }
+
+ /* contention bit is set, increase read waiter count */
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_readers,
+ blocked_readers, blocked_readers + 1)) {
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ }
+
+ while (state & wrflags) {
+ /* sleep/wait */
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x (0x%x), NULL,
NULL)\n",
+ __func__, &target_urwlock->rw_state,
+ UMTX_OP_WAIT_UINT, tswap32(state),
+ target_urwlock->rw_state);
+ ret = get_errno(_umtx_op(&target_urwlock->rw_state,
+ UMTX_OP_WAIT_UINT, tswap32(state), NULL, ts));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock,
+ target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ /* decrease read waiter count */
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_readers,
+ blocked_readers, (blocked_readers - 1))) {
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ }
+ if (blocked_readers == 1) {
+ /* clear read contention bit */
+ __get_user(state, &target_urwlock->rw_state);
+ while (!tcmpset_32(&target_urwlock->rw_state, state,
+ state & ~TARGET_URWLOCK_READ_WAITERS)) {
+ __get_user(state, &target_urwlock->rw_state);
+ }
+ }
+ }
+}
+
+abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag,
+ struct timespec *ts)
+{
+ struct target_urwlock *target_urwlock;
+ uint32_t blocked_readers, blocked_writers;
+ uint32_t state;
+ abi_long ret;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+ blocked_readers = 0;
+ for (;;) {
+ __get_user(state, &target_urwlock->rw_state);
+ while (!(state & TARGET_URWLOCK_WRITE_OWNER) &&
+ TARGET_URWLOCK_READER_COUNT(state) == 0) {
+ if (tcmpset_32(&target_urwlock->rw_state, state,
+ state | TARGET_URWLOCK_WRITE_OWNER)) {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ return 0;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ if (!(state & (TARGET_URWLOCK_WRITE_OWNER |
+ TARGET_URWLOCK_WRITE_WAITERS)) &&
+ blocked_readers != 0) {
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, &target_urwlock->rw_state, UMTX_OP_WAKE,
+ tswap32(state));
+ ret = get_errno(_umtx_op(&target_urwlock->rw_state,
+ UMTX_OP_WAKE, INT_MAX, NULL, NULL));
+ return ret;
+ }
+ /* re-read the state */
+ __get_user(state, &target_urwlock->rw_state);
+
+ /* and set TARGET_URWLOCK_WRITE_WAITERS */
+ while (((state & TARGET_URWLOCK_WRITE_OWNER) ||
+ TARGET_URWLOCK_READER_COUNT(state) != 0) &&
+ (state & TARGET_URWLOCK_WRITE_WAITERS) == 0) {
+ if (tcmpset_32(&target_urwlock->rw_state, state,
+ state | TARGET_URWLOCK_WRITE_WAITERS)) {
+ break;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ /* contention bit is set, increase write waiter count */
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_writers,
+ blocked_writers, blocked_writers + 1)) {
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ }
+
+ /* sleep */
+ while ((state & TARGET_URWLOCK_WRITE_OWNER) ||
+ (TARGET_URWLOCK_READER_COUNT(state) != 0)) {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ DEBUG_UMTX("<WAIT> %s: _umtx_op(%p, %d, 0x%x(0x%x), NULL, NULL)\n",
+ __func__, &target_urwlock->rw_blocked_writers,
+ UMTX_OP_WAIT_UINT, tswap32(state),
+ target_urwlock->rw_state);
+ ret = get_errno(_umtx_op(&target_urwlock->rw_state,
+ UMTX_OP_WAIT_UINT, tswap32(state), NULL, ts));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr,
+ 0)) {
+ return -TARGET_EFAULT;
+ }
+ __get_user(state, &target_urwlock->rw_state);
+ }
+
+ /* decrease the write waiter count */
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ while (!tcmpset_32(&target_urwlock->rw_blocked_writers,
+ blocked_writers, (blocked_writers - 1))) {
+ __get_user(blocked_writers, &target_urwlock->rw_blocked_writers);
+ }
+ if (blocked_writers == 1) {
+ /* clear write contention bit */
+ __get_user(state, &target_urwlock->rw_state);
+ while (!tcmpset_32(&target_urwlock->rw_state, state,
+ state & ~TARGET_URWLOCK_WRITE_WAITERS)) {
+ __get_user(state, &target_urwlock->rw_state);
+ }
+ __get_user(blocked_readers, &target_urwlock->rw_blocked_readers);
+ } else {
+ blocked_readers = 0;
+ }
+ }
+}
+
+abi_long freebsd_rw_unlock(abi_ulong target_addr)
+{
+ struct target_urwlock *target_urwlock;
+ uint32_t flags, state, count = 0;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) {
+ return -TARGET_EFAULT;
+ }
+
+ __get_user(flags, &target_urwlock->rw_flags);
+ __get_user(state, &target_urwlock->rw_state);
+
+ if (state & TARGET_URWLOCK_WRITE_OWNER) {
+ for (;;) {
+ if (!tcmpset_32(&target_urwlock->rw_state, state,
+ state & ~TARGET_URWLOCK_WRITE_OWNER)) {
+ __get_user(state, &target_urwlock->rw_state);
+ if (!(state & TARGET_URWLOCK_WRITE_OWNER)) {
+ unlock_user_struct(target_urwlock,
+ target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ } else {
+ break;
+ }
+ }
+ } else if (TARGET_URWLOCK_READER_COUNT(state) != 0) {
+ /* decrement reader count */
+ for (;;) {
+ if (!tcmpset_32(&target_urwlock->rw_state, state, (state - 1))) {
+ if (TARGET_URWLOCK_READER_COUNT(state) == 0) {
+ unlock_user_struct(target_urwlock,
+ target_addr, 1);
+ return -TARGET_EPERM;
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ return -TARGET_EPERM;
+ }
+
+ if (!(flags & TARGET_URWLOCK_PREFER_READER)) {
+ if (state & TARGET_URWLOCK_WRITE_WAITERS) {
+ count = 1;
+ } else if (state & TARGET_URWLOCK_READ_WAITERS) {
+ count = INT_MAX;
+ }
+ } else {
+ if (state & TARGET_URWLOCK_READ_WAITERS) {
+ count = INT_MAX;
+ } else if (state & TARGET_URWLOCK_WRITE_WAITERS) {
+ count = 1;
+ }
+ }
+
+ unlock_user_struct(target_urwlock, target_addr, 1);
+ if (count != 0) {
+ DEBUG_UMTX("<WAKE> %s: _umtx_op(%p, %d, 0x%x, NULL, NULL)\n",
+ __func__, &target_urwlock->rw_state, UMTX_OP_WAKE, count);
+ return get_errno(_umtx_op(&target_urwlock->rw_state, UMTX_OP_WAKE,
+ count, NULL, NULL));
+ } else {
+ return 0;
+ }
+}
+
+abi_long do_freebsd_thr_new(CPUArchState *env,
+ abi_ulong target_param_addr, int32_t param_size)
+{
+ new_freebsd_thread_info_t info;
+ pthread_attr_t attr;
+ TaskState *ts;
+ CPUArchState *new_env;
+ struct target_freebsd_thr_param *target_param;
+ abi_ulong target_rtp_addr;
+ struct target_freebsd_rtprio *target_rtp;
+ struct rtprio *rtp_ptr, rtp;
+ TaskState *parent_ts = (TaskState *)env->opaque;
+ sigset_t sigmask;
+ struct sched_param sched_param;
+ int sched_policy;
+ int ret = 0;
+
+ memset(&info, 0, sizeof(info));
+
+ if (!lock_user_struct(VERIFY_READ, target_param, target_param_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ info.param.start_func = tswapal(target_param->start_func);
+ info.param.arg = tswapal(target_param->arg);
+ info.param.stack_base = tswapal(target_param->stack_base);
+ info.param.stack_size = tswapal(target_param->stack_size);
+ info.param.tls_base = tswapal(target_param->tls_base);
+ info.param.tls_size = tswapal(target_param->tls_size);
+ info.param.child_tid = tswapal(target_param->child_tid);
+ info.param.parent_tid = tswapal(target_param->parent_tid);
+ info.param.flags = tswap32(target_param->flags);
+ target_rtp_addr = info.param.rtp = tswapal(target_param->rtp);
+ unlock_user(target_param, target_param_addr, 0);
+
+ thr_self(&info.parent_tid);
+
+ if (target_rtp_addr) {
+ if (!lock_user_struct(VERIFY_READ, target_rtp, target_rtp_addr, 1)) {
+ return -TARGET_EFAULT;
+ }
+ rtp.type = tswap16(target_rtp->type);
+ rtp.prio = tswap16(target_rtp->prio);
+ unlock_user(target_rtp, target_rtp_addr, 0);
+ rtp_ptr = &rtp;
+ } else {
+ rtp_ptr = NULL;
+ }
+
+ /* Create a new CPU instance. */
+ ts = g_malloc0(sizeof(TaskState));
+ init_task_state(ts);
+ new_env = cpu_copy(env);
+ //target_cpu_reset(new_env); /* XXX called in cpu_copy()? */
+
+ /* init regs that differ from the parent thread. */
+ target_cpu_clone_regs(new_env, info.param.stack_base);
+ new_env->opaque = ts;
+ ts->bprm = parent_ts->bprm;
+ ts->info = parent_ts->info;
+
+ target_cpu_set_tls(new_env, info.param.tls_base);
+
+ /* Grab a mutex so that thread setup appears atomic. */
+ pthread_mutex_lock(new_freebsd_thread_lock_ptr);
+
+ pthread_mutex_init(&info.mutex, NULL);
+ pthread_mutex_lock(&info.mutex);
+ pthread_cond_init(&info.cond, NULL);
+ info.env = new_env;
+
+ /* XXX check return values... */
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (rtp_ptr) {
+ rtp_to_schedparam(&rtp, &sched_policy, &sched_param);
+ pthread_attr_setschedpolicy(&attr, sched_policy);
+ pthread_attr_setschedparam(&attr, &sched_param);
+ }
+
+ /*
+ * It is not safe to deliver signals until the child has finished
+ * initializing, so temporarily block all signals.
+ */
+ sigfillset(&sigmask);
+ sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
+
+ ret = pthread_create(&info.thread, &attr, new_freebsd_thread_start, &info);
+ /* XXX Free new CPU state if thread creation fails. */
+
+ sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
+ pthread_attr_destroy(&attr);
+ if (ret == 0) {
+ /* Wait for the child to initialize. */
+ pthread_cond_wait(&info.cond, &info.mutex);
+ } else {
+ /* Creation of new thread failed. */
+ ret = -host_to_target_errno(errno);
+ }
+
+ pthread_mutex_unlock(&info.mutex);
+ pthread_cond_destroy(&info.cond);
+ pthread_mutex_destroy(&info.mutex);
+ pthread_mutex_unlock(new_freebsd_thread_lock_ptr);
+
+ return ret;
+}
diff --git a/bsd-user/freebsd/os-thread.h b/bsd-user/freebsd/os-thread.h
new file mode 100644
index 0000000..5e24852
--- /dev/null
+++ b/bsd-user/freebsd/os-thread.h
@@ -0,0 +1,511 @@
+/*
+ * FreeBSD thread and user mutex related system call shims
+ *
+ * Copyright (c) 2013 Stacey D. Son
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __FREEBSD_OS_THREAD_H_
+#define __FREEBSD_OS_THREAD_H_
+
+#include <sys/thr.h>
+#include <sys/rtprio.h>
+#include <sys/umtx.h>
+
+#include "qemu-os.h"
+
+static abi_long do_freebsd_thr_create(CPUArchState *env, abi_ulong target_ctx,
+ abi_ulong target_id, int flags)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_create()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_self(abi_ulong target_id)
+{
+ abi_long ret;
+ long tid;
+
+ ret = get_errno(thr_self(&tid));
+ if (!is_error(ret)) {
+ if (put_user_sal(tid, target_id)) {
+ return -TARGET_EFAULT;
+ }
+ }
+ return ret;
+}
+
+static abi_long do_freebsd_thr_exit(CPUArchState *cpu_env, abi_ulong tid_addr)
+{
+ CPUState *cpu = ENV_GET_CPU(cpu_env);
+ TaskState *ts;
+
+ /*
+ * XXX This probably breaks if a signal arrives.
+ * We should disable signals.
+ */
+ cpu_list_lock();
+ /* Remove the CPU from the list. */
+ QTAILQ_REMOVE(&cpus, cpu, node);
+ cpu_list_unlock();
+ if (tid_addr) {
+ /* Signal target userland that it can free the stack. */
+ if (!put_user_sal(1, tid_addr)) {
+ freebsd_umtx_wake(tid_addr, INT_MAX);
+ }
+ }
+ thread_cpu = NULL;
+ object_unref(OBJECT(ENV_GET_CPU(cpu_env)));
+ ts = ((CPUArchState *)cpu_env)->opaque;
+ g_free(ts);
+ pthread_exit(NULL);
+ /* Doesn't return */
+ return 0;
+}
+
+static abi_long do_freebsd_thr_kill(long id, int sig)
+{
+
+ return get_errno(thr_kill(id, sig));
+}
+
+static abi_long do_freebsd_thr_kill2(pid_t pid, long id, int sig)
+{
+
+ return get_errno(thr_kill2(pid, id, sig));
+}
+
+static abi_long do_freebsd_thr_suspend(abi_ulong target_ts)
+{
+ abi_long ret;
+ struct timespec ts;
+
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = thr_suspend(&ts);
+ } else {
+ ret = thr_suspend(NULL);
+ }
+ return ret;
+}
+
+static abi_long do_freebsd_thr_wake(long tid)
+{
+
+ return get_errno(thr_wake(tid));
+}
+
+static abi_long do_freebsd_thr_set_name(long tid, abi_ulong target_name)
+{
+ abi_long ret;
+ void *p;
+
+ p = lock_user_string(target_name);
+ if (p == NULL) {
+ return -TARGET_EFAULT;
+ }
+ ret = thr_set_name(tid, p);
+ unlock_user(p, target_name, 0);
+
+ return ret;
+}
+
+static abi_long do_freebsd_rtprio_thread(int function, lwpid_t lwpid,
+ abi_ulong target_addr)
+{
+ int ret;
+ struct rtprio rtp;
+
+ ret = t2h_freebsd_rtprio(&rtp, target_addr);
+ if (!is_error(ret)) {
+ ret = get_errno(rtprio_thread(function, lwpid, &rtp));
+ }
+ if (!is_error(ret)) {
+ ret = h2t_freebsd_rtprio(target_addr, &rtp);
+ }
+ return ret;
+}
+
+static abi_long do_freebsd_getcontext(void *cpu_env, abi_ulong arg1)
+{
+ abi_long ret;
+ target_ucontext_t *ucp;
+ sigset_t sigmask;
+
+ if (arg1 == 0) {
+ return -TARGET_EINVAL;
+ }
+ ret = get_errno(sigprocmask(0, NULL, &sigmask));
+ if (!is_error(ret)) {
+ ucp = lock_user(VERIFY_WRITE, arg1, sizeof(target_ucontext_t), 0);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_mcontext(cpu_env, &ucp->uc_mcontext,
TARGET_MC_GET_CLEAR_RET);
+ host_to_target_sigset(&ucp->uc_sigmask, &sigmask);
+ memset(ucp->__spare__, 0, sizeof(ucp->__spare__));
+ unlock_user(ucp, arg1, sizeof(target_ucontext_t));
+ }
+ return ret;
+}
+
+static abi_long do_freebsd_setcontext(void *cpu_env, abi_ulong arg1)
+{
+ abi_long ret;
+ target_ucontext_t *ucp;
+ sigset_t sigmask;
+ if (arg1 == 0) {
+ return -TARGET_EINVAL;
+ }
+ ucp = lock_user(VERIFY_READ, arg1, sizeof(target_ucontext_t), 1);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = set_mcontext(cpu_env, &ucp->uc_mcontext, 0);
+ target_to_host_sigset(&sigmask, &ucp->uc_sigmask);
+ unlock_user(ucp, arg1, sizeof(target_ucontext_t));
+ if (!is_error(ret)) {
+ (void)sigprocmask(SIG_SETMASK, &sigmask, NULL);
+ }
+ return ret;
+}
+
+/* swapcontext(2) */
+static abi_long do_freebsd_swapcontext(void *cpu_env, abi_ulong arg1,
+ abi_ulong arg2)
+{
+ abi_long ret;
+ target_ucontext_t *ucp;
+ sigset_t sigmask;
+
+ if (arg1 == 0 || arg2 == 0) {
+ return -TARGET_EINVAL;
+ }
+ /* Save current context in arg1. */
+ ret = get_errno(sigprocmask(0, NULL, &sigmask));
+ if (!is_error(ret)) {
+ ucp = lock_user(VERIFY_WRITE, arg1, sizeof(target_ucontext_t), 0);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_mcontext(cpu_env, &ucp->uc_mcontext,
TARGET_MC_GET_CLEAR_RET);
+ host_to_target_sigset(&ucp->uc_sigmask, &sigmask);
+ memset(ucp->__spare__, 0, sizeof(ucp->__spare__));
+ unlock_user(ucp, arg1, sizeof(target_ucontext_t));
+ }
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ /* Restore the context in arg2 to the current context. */
+ ucp = lock_user(VERIFY_READ, arg2, sizeof(target_ucontext_t), 1);
+ if (ucp == 0) {
+ return -TARGET_EFAULT;
+ }
+ ret = set_mcontext(cpu_env, &ucp->uc_mcontext, 0);
+ target_to_host_sigset(&sigmask, &ucp->uc_sigmask);
+ unlock_user(ucp, arg2, sizeof(target_ucontext_t));
+ if (!is_error(ret)) {
+ (void)sigprocmask(SIG_SETMASK, &sigmask, NULL);
+ }
+ return ret;
+}
+
+
+/* undocumented _umtx_lock() */
+static inline abi_long do_freebsd__umtx_lock(abi_ulong target_addr)
+{
+ abi_long ret;
+ long tid;
+
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ return freebsd_lock_umtx(target_addr, tid, NULL);
+}
+
+/* undocumented _umtx_unlock() */
+static inline abi_long do_freebsd__umtx_unlock(abi_ulong target_addr)
+{
+ abi_long ret;
+ long tid;
+
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ return freebsd_unlock_umtx(target_addr, tid);
+}
+
+/* undocumented _umtx_op(void *obj, int op, u_long val, void *uaddr,
+ void *target_ts); */
+static inline abi_long do_freebsd__umtx_op(abi_ulong obj, int op, abi_ulong
val,
+ abi_ulong uaddr, abi_ulong target_ts)
+{
+ abi_long ret;
+ struct timespec ts;
+ long tid;
+
+ switch (op) {
+ case TARGET_UMTX_OP_LOCK:
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_lock_umtx(obj, tid, &ts);
+ } else {
+ ret = freebsd_lock_umtx(obj, tid, NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_UNLOCK:
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_unlock_umtx(obj, tid);
+ break;
+
+ case TARGET_UMTX_OP_WAIT:
+ /* args: obj *, val, ts * */
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_wait(obj, tswapal(val), &ts);
+ } else {
+ ret = freebsd_umtx_wait(obj, tswapal(val), NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_WAKE:
+ /* args: obj *, nr_wakeup */
+ ret = freebsd_umtx_wake(obj, val);
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_LOCK:
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (target_ts) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_lock_umutex(obj, tid, &ts, 0);
+ } else {
+ ret = freebsd_lock_umutex(obj, tid, NULL, 0);
+ }
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_UNLOCK:
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_unlock_umutex(obj, tid);
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_TRYLOCK:
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ ret = freebsd_lock_umutex(obj, tid, NULL, TARGET_UMUTEX_TRY);
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_WAIT:
+ ret = get_errno(thr_self(&tid));
+ if (is_error(ret)) {
+ return ret;
+ }
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_lock_umutex(obj, tid, &ts, TARGET_UMUTEX_WAIT);
+ } else {
+ ret = freebsd_lock_umutex(obj, tid, NULL, TARGET_UMUTEX_WAIT);
+ }
+ break;
+
+ case TARGET_UMTX_OP_MUTEX_WAKE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_mutex_wake(obj, val);
+ break;
+
+ case TARGET_UMTX_OP_SET_CEILING:
+ ret = 0; /* XXX quietly ignore these things for now */
+ break;
+
+ case TARGET_UMTX_OP_CV_WAIT:
+ /*
+ * Initialization of the struct conv is done by
+ * bzero'ing everything in userland.
+ */
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_cv_wait(obj, uaddr, &ts, val);
+ } else {
+ ret = freebsd_cv_wait(obj, uaddr, NULL, val);
+ }
+ break;
+
+ case TARGET_UMTX_OP_CV_SIGNAL:
+ /*
+ * XXX
+ * User code may check if c_has_waiters is zero. Other
+ * than that it is assume that user code doesn't do
+ * much with the struct conv fields and is pretty
+ * much opauque to userland.
+ */
+ ret = freebsd_cv_signal(obj);
+ break;
+
+ case TARGET_UMTX_OP_CV_BROADCAST:
+ /*
+ * XXX
+ * User code may check if c_has_waiters is zero. Other
+ * than that it is assume that user code doesn't do
+ * much with the struct conv fields and is pretty
+ * much opauque to userland.
+ */
+ ret = freebsd_cv_broadcast(obj);
+ break;
+
+ case TARGET_UMTX_OP_WAIT_UINT:
+ if (!access_ok(VERIFY_READ, obj, sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_wait_uint(obj, tswap32((uint32_t)val), &ts);
+ } else {
+ ret = freebsd_umtx_wait_uint(obj, tswap32((uint32_t)val), NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_WAIT_UINT_PRIVATE:
+ if (!access_ok(VERIFY_READ, obj, sizeof(abi_ulong))) {
+ return -TARGET_EFAULT;
+ }
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_wait_uint_private(obj, tswap32((uint32_t)val),
+ &ts);
+ } else {
+ ret = freebsd_umtx_wait_uint_private(obj, tswap32((uint32_t)val),
+ NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_WAKE_PRIVATE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_wake_private(obj, val);
+ break;
+
+ case TARGET_UMTX_OP_RW_RDLOCK:
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_rw_rdlock(obj, val, &ts);
+ } else {
+ ret = freebsd_rw_rdlock(obj, val, NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_RW_WRLOCK:
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_rw_wrlock(obj, val, &ts);
+ } else {
+ ret = freebsd_rw_wrlock(obj, val, NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_RW_UNLOCK:
+ ret = freebsd_rw_unlock(obj);
+ break;
+
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+#ifdef UMTX_OP_MUTEX_WAKE2
+ case TARGET_UMTX_OP_MUTEX_WAKE2:
+ ret = freebsd_umtx_mutex_wake2(obj, val);
+ break;
+#endif /* UMTX_OP_MUTEX_WAKE2 */
+
+#ifdef UMTX_OP_NWAKE_PRIVATE
+ case TARGET_UMTX_OP_NWAKE_PRIVATE:
+ {
+ int i;
+ abi_ulong *uaddr;
+ uint32_t imax = tswap32(INT_MAX);
+
+ if (!access_ok(VERIFY_READ, obj, val * sizeof(uint32_t))) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_nwake_private(obj, val);
+
+ uaddr = (abi_ulong *)g2h(obj);
+ ret = 0;
+ for (i = 0; i < (int32_t)val; i++) {
+ ret = freebsd_umtx_wake_private(tswapal(uaddr[i]), imax);
+ if (is_error(ret)) {
+ break;
+ }
+ }
+ }
+ break;
+#endif /* UMTX_OP_NWAKE_PRIVATE */
+
+ case TARGET_UMTX_OP_SEM_WAIT:
+ if (target_ts != 0) {
+ if (t2h_freebsd_timespec(&ts, target_ts)) {
+ return -TARGET_EFAULT;
+ }
+ ret = freebsd_umtx_sem_wait(obj, &ts);
+ } else {
+ ret = freebsd_umtx_sem_wait(obj, NULL);
+ }
+ break;
+
+ case TARGET_UMTX_OP_SEM_WAKE:
+ /* Don't need to do access_ok(). */
+ ret = freebsd_umtx_sem_wake(obj, val);
+ break;
+#endif
+ default:
+ return -TARGET_EINVAL;
+ }
+ return ret;
+}
+
+#endif /* !__FREEBSD_OS_THREAD_H_ */
diff --git a/bsd-user/freebsd/qemu-os.h b/bsd-user/freebsd/qemu-os.h
index 90d8eb4..b5510dc 100644
--- a/bsd-user/freebsd/qemu-os.h
+++ b/bsd-user/freebsd/qemu-os.h
@@ -64,4 +64,10 @@ abi_long h2t_freebsd_fhandle(abi_ulong target_addr,
fhandle_t *host_fh);
abi_long h2t_freebsd_statfs(abi_ulong target_addr, struct statfs *host_statfs);
abi_long target_to_host_fcntl_cmd(int cmd);
+/* os-thread.c */
+abi_long t2h_freebsd_rtprio(struct rtprio *host_rtp, abi_ulong target_addr);
+abi_long h2t_freebsd_rtprio(abi_ulong target_addr, struct rtprio *host_rtp);
+abi_long do_freebsd_thr_new(CPUArchState *env, abi_ulong target_param_addr,
+ int32_t param_size);
+
#endif /* !_QEMU_OS_H_ */
diff --git a/bsd-user/netbsd/os-thread.c b/bsd-user/netbsd/os-thread.c
new file mode 100644
index 0000000..a4af765
--- /dev/null
+++ b/bsd-user/netbsd/os-thread.c
@@ -0,0 +1 @@
+/* XXX NetBSD thread related helpers */
diff --git a/bsd-user/netbsd/os-thread.h b/bsd-user/netbsd/os-thread.h
new file mode 100644
index 0000000..073b0a0
--- /dev/null
+++ b/bsd-user/netbsd/os-thread.h
@@ -0,0 +1,133 @@
+#ifndef __NETBSD_OS_THREAD_H_
+#define __NETBSD_OS_THREAD_H_
+
+/*
+ * XXX To support FreeBSD binaries on NetBSD these syscalls will need to
+ * be emulated.
+ */
+static abi_long do_freebsd_thr_create(CPUArchState *env, abi_ulong target_ctx,
+ abi_ulong target_id, int flags)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_create()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_create(CPUArchState *env, abi_ulong thread_ctx,
+ abi_ulong target_id, int flags)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_create()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_new(CPUArchState *env,
+ abi_ulong target_param_addr, int32_t param_size)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_new()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_self(abi_ulong target_id)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_self()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_exit(CPUArchState *cpu_env, abi_ulong tid_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_exit()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_kill(long id, int sig)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_kill()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_kill2(pid_t pid, long id, int sig)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_kill2()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_suspend(abi_ulong target_ts)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_suspend()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_wake(long tid)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_wake()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_set_name(long tid, abi_ulong target_name)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_set_name()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_rtprio_thread(int function, lwpid_t lwpid,
+ abi_ulong target_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall rtprio_thread()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_getcontext(void *cpu_env, abi_ulong arg1)
+{
+
+ qemu_log("qemu: Unsupported syscall getcontext()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_setcontext(void *cpu_env, abi_ulong arg1)
+{
+
+ qemu_log("qemu: Unsupported syscall setcontext()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_swapcontext(void *cpu_env, abi_ulong arg1,
+ abi_ulong arg2)
+{
+
+ qemu_log("qemu: Unsupported syscall swapcontext()\n");
+ return -TARGET_ENOSYS;
+}
+
+static inline abi_long do_freebsd__umtx_lock(abi_ulong target_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall _umtx_lock()\n");
+ return -TARGET_ENOSYS;
+}
+
+static inline abi_long do_freebsd__umtx_unlock(abi_ulong target_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall _umtx_unlock()\n");
+ return -TARGET_ENOSYS;
+}
+
+static inline abi_long do_freebsd__umtx_op(abi_ulong obj, int op, abi_ulong
val,
+ abi_ulong uaddr, abi_ulong target_ts)
+{
+
+ qemu_log("qemu: Unsupported syscall _umtx_op()\n");
+ return -TARGET_ENOSYS;
+}
+
+#endif /* ! __NETBSD_OS_THREAD_H_ */
diff --git a/bsd-user/openbsd/os-thread.c b/bsd-user/openbsd/os-thread.c
new file mode 100644
index 0000000..d125281
--- /dev/null
+++ b/bsd-user/openbsd/os-thread.c
@@ -0,0 +1 @@
+/* XXX OpenBSD thread related helpers */
diff --git a/bsd-user/openbsd/os-thread.h b/bsd-user/openbsd/os-thread.h
new file mode 100644
index 0000000..962a769
--- /dev/null
+++ b/bsd-user/openbsd/os-thread.h
@@ -0,0 +1,133 @@
+#ifndef __OPENBSD_OS_THREAD_H_
+#define __OPENBSD_OS_THREAD_H_
+
+/*
+ * XXX To support FreeBSD binaries on OpenBSD these syscalls will need to
+ * be emulated.
+ */
+static abi_long do_freebsd_thr_create(CPUArchState *env, abi_ulong target_ctx,
+ abi_ulong target_id, int flags)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_create()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_create(CPUArchState *env, abi_ulong thread_ctx,
+ abi_ulong target_id, int flags)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_create()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_new(CPUArchState *env,
+ abi_ulong target_param_addr, int32_t param_size)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_new()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_self(abi_ulong target_id)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_self()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_exit(CPUArchState *cpu_env, abi_ulong tid_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_exit()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_kill(long id, int sig)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_kill()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_kill2(pid_t pid, long id, int sig)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_kill2()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_suspend(abi_ulong target_ts)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_suspend()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_wake(long tid)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_wake()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_thr_set_name(long tid, abi_ulong target_name)
+{
+
+ qemu_log("qemu: Unsupported syscall thr_set_name()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_rtprio_thread(int function, lwpid_t lwpid,
+ abi_ulong target_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall rtprio_thread()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_getcontext(void *cpu_env, abi_ulong arg1)
+{
+
+ qemu_log("qemu: Unsupported syscall getcontext()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_setcontext(void *cpu_env, abi_ulong arg1)
+{
+
+ qemu_log("qemu: Unsupported syscall setcontext()\n");
+ return -TARGET_ENOSYS;
+}
+
+static abi_long do_freebsd_swapcontext(void *cpu_env, abi_ulong arg1,
+ abi_ulong arg2)
+{
+
+ qemu_log("qemu: Unsupported syscall swapcontext()\n");
+ return -TARGET_ENOSYS;
+}
+
+static inline abi_long do_freebsd__umtx_lock(abi_ulong target_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall _umtx_lock()\n");
+ return -TARGET_ENOSYS;
+}
+
+static inline abi_long do_freebsd__umtx_unlock(abi_ulong target_addr)
+{
+
+ qemu_log("qemu: Unsupported syscall _umtx_unlock()\n");
+ return -TARGET_ENOSYS;
+}
+
+static inline abi_long do_freebsd__umtx_op(abi_ulong obj, int op, abi_ulong
val,
+ abi_ulong uaddr, abi_ulong target_ts)
+{
+
+ qemu_log("qemu: Unsupported syscall _umtx_op()\n");
+ return -TARGET_ENOSYS;
+}
+
+#endif /* ! __OPENBSD_OS_THREAD_H_ */
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 613a89e..4b2add2 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -42,11 +42,7 @@ extern enum BSDType bsd_type;
#include "target_os_signal.h"
#include "exec/gdbstub.h"
-#if defined(CONFIG_USE_NPTL)
#define THREAD __thread
-#else
-#define THREAD
-#endif
/* This struct is used to hold certain information about the image.
* Basically, it replicates in user space what would be certain
@@ -67,6 +63,8 @@ struct image_info {
abi_ulong entry;
abi_ulong code_offset;
abi_ulong data_offset;
+ abi_ulong arg_start;
+ abi_ulong arg_end;
int personality;
};
@@ -89,6 +87,15 @@ struct emulated_sigtable {
typedef struct TaskState {
struct TaskState *next;
int used; /* non zero if used */
+#ifdef TARGET_ARM
+ int swi_errno;
+#endif
+#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32)
+ /* Extra fields for semihosted binaries. */
+ uint32_t heap_base;
+ uint32_t heap_limit;
+ uint32_t stack_base;
+#endif
struct image_info *info;
struct bsd_binprm *bprm;
@@ -231,10 +238,8 @@ void mmap_unlock(void);
abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size);
void cpu_list_lock(void);
void cpu_list_unlock(void);
-#if defined(CONFIG_USE_NPTL)
void mmap_fork_start(void);
void mmap_fork_end(int child);
-#endif
/* main.c */
extern unsigned long target_maxtsiz;
@@ -244,10 +249,15 @@ extern unsigned long target_dflssiz;
extern unsigned long target_maxssiz;
extern unsigned long target_sgrowsiz;
extern char qemu_proc_pathname[];
+void start_exclusive(void);
+void end_exclusive(void);
+void cpu_exec_start(CPUState *cpu);
+void cpu_exec_end(CPUState *cpu);
/* syscall.c */
abi_long get_errno(abi_long ret);
int is_error(abi_long ret);
+int host_to_target_errno(int err);
/* os-proc.c */
abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
@@ -258,6 +268,41 @@ abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong
namep, int32_t namelen,
abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen);
abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2);
+/* os-thread.c */
+extern pthread_mutex_t *new_freebsd_thread_lock_ptr;
+void *new_freebsd_thread_start(void *arg);
+abi_long freebsd_lock_umtx(abi_ulong target_addr, abi_long tid,
+ struct timespec *timeout);
+abi_long freebsd_unlock_umtx(abi_ulong target_addr, abi_long id);
+abi_long freebsd_umtx_wait(abi_ulong targ_addr, abi_ulong id,
+ struct timespec *ts);
+abi_long freebsd_umtx_wake(abi_ulong target_addr, uint32_t n_wake);
+abi_long freebsd_umtx_mutex_wake(abi_ulong target_addr, abi_long val);
+abi_long freebsd_umtx_wait_uint(abi_ulong obj, uint32_t val,
+ struct timespec *timeout);
+abi_long freebsd_umtx_wait_uint_private(abi_ulong obj, uint32_t val,
+ struct timespec *timeout);
+abi_long freebsd_umtx_wake_private(abi_ulong obj, uint32_t val);
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+abi_long freebsd_umtx_nwake_private(abi_ulong obj, uint32_t val);
+abi_long freebsd_umtx_mutex_wake2(abi_ulong obj, uint32_t val);
+abi_long freebsd_umtx_sem_wait(abi_ulong obj, struct timespec *timeout);
+abi_long freebsd_umtx_sem_wake(abi_ulong obj, uint32_t val);
+#endif
+abi_long freebsd_lock_umutex(abi_ulong target_addr, uint32_t id,
+ struct timespec *ts, int mode);
+abi_long freebsd_unlock_umutex(abi_ulong target_addr, uint32_t id);
+abi_long freebsd_cv_wait(abi_ulong target_ucond_addr,
+ abi_ulong target_umtx_addr, struct timespec *ts, int wflags);
+abi_long freebsd_cv_signal(abi_ulong target_ucond_addr);
+abi_long freebsd_cv_broadcast(abi_ulong target_ucond_addr);
+abi_long freebsd_rw_rdlock(abi_ulong target_addr, long fflag,
+ struct timespec *ts);
+abi_long freebsd_rw_wrlock(abi_ulong target_addr, long fflag,
+ struct timespec *ts);
+abi_long freebsd_rw_unlock(abi_ulong target_addr);
+
+
/* user access */
#define VERIFY_READ 0
@@ -483,8 +528,6 @@ static inline int regpairs_aligned(void *cpu_env)
}
#endif
-#if defined(CONFIG_USE_NPTL)
#include <pthread.h>
-#endif
#endif /* QEMU_H */
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index 286c71e..0a851fe 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -32,7 +32,6 @@
#include "qemu-common.h"
#define target_to_host_bitmask(x, tbl) (x)
-static int host_to_target_errno(int err);
/* BSD independent syscall shims */
#include "bsd-file.h"
@@ -47,6 +46,7 @@ static int host_to_target_errno(int err);
#include "os-signal.h"
#include "os-socket.h"
#include "os-stat.h"
+#include "os-thread.h"
/* #define DEBUG */
@@ -64,7 +64,7 @@ abi_long get_errno(abi_long ret)
}
}
-static int host_to_target_errno(int err)
+int host_to_target_errno(int err)
{
/* XXX need to translate host errnos here */
return err;
@@ -1110,6 +1110,73 @@ abi_long do_freebsd_syscall(void *cpu_env, int num,
abi_long arg1,
break;
/*
+ * thread system calls
+ */
+ case TARGET_FREEBSD_NR_thr_create: /* thr_create(2) */
+ ret = do_freebsd_thr_create(cpu_env, arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_new: /* thr_new(2) */
+ ret = do_freebsd_thr_new(cpu_env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_set_name: /* thr_set_name(2) */
+ ret = do_freebsd_thr_set_name(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_self: /* thr_self(2) */
+ ret = do_freebsd_thr_self(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_suspend: /* thr_suspend(2) */
+ ret = do_freebsd_thr_suspend(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_wake: /* thr_wake(2) */
+ ret = do_freebsd_thr_wake(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_kill: /* thr_kill(2) */
+ ret = do_freebsd_thr_kill(arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_kill2: /* thr_kill2(2) */
+ ret = do_freebsd_thr_kill2(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_thr_exit: /* thr_exit(2) */
+ ret = do_freebsd_thr_exit(cpu_env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_rtprio_thread: /* rtprio_thread(2) */
+ ret = do_freebsd_rtprio_thread(arg1, arg2, arg3);
+ break;
+
+ case TARGET_FREEBSD_NR_getcontext: /* getcontext(2) */
+ ret = do_freebsd_getcontext(cpu_env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_setcontext: /* setcontext(2) */
+ ret = do_freebsd_setcontext(cpu_env, arg1);
+ break;
+
+ case TARGET_FREEBSD_NR_swapcontext: /* swapcontext(2) */
+ ret = do_freebsd_swapcontext(cpu_env, arg1, arg2);
+ break;
+
+ case TARGET_FREEBSD_NR__umtx_lock: /* undocumented */
+ ret = do_freebsd__umtx_lock(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR__umtx_unlock: /* undocumented */
+ ret = do_freebsd__umtx_unlock(arg1);
+ break;
+
+ case TARGET_FREEBSD_NR__umtx_op: /* undocumented */
+ ret = do_freebsd__umtx_op(arg1, arg2, arg3, arg4, arg5);
+ break;
+
+ /*
* sys{ctl, arch, call}
*/
case TARGET_FREEBSD_NR___sysctl: /* sysctl(3) */
diff --git a/include/qemu/tls.h b/include/qemu/tls.h
index b92ea9d..ae7d79d 100644
--- a/include/qemu/tls.h
+++ b/include/qemu/tls.h
@@ -38,7 +38,7 @@
* TODO: proper implementations via Win32 .tls sections and
* POSIX pthread_getspecific.
*/
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
#define DECLARE_TLS(type, x) extern DEFINE_TLS(type, x)
#define DEFINE_TLS(type, x) __thread __typeof__(type) tls__##x
#define tls_var(x) tls__##x
--
1.7.8
- [Qemu-devel] [PATCH v3 00/19] bsd-user: Add system call and mips/arm support., Stacey Son, 2013/12/17
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- [Qemu-devel] [PATCH v3 14/19] bsd-user: add support for thread related system calls,
Stacey Son <=
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available
- Message not available