From 350b7a3d3e02de377ccac39a44b0e7645a237a06 Mon Sep 17 00:00:00 2001 In-Reply-To: <1383928417-38009-1-git-send-email-sson@FreeBSD.org> References: <1383928417-38009-1-git-send-email-sson@FreeBSD.org> From: Stacey Son Date: Mon, 7 Oct 2013 20:08:17 -0500 Subject: [PATCH v3 14/19] bsd-user: add support for thread related system calls 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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(" %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 . + */ +#ifndef __FREEBSD_OS_THREAD_H_ +#define __FREEBSD_OS_THREAD_H_ + +#include +#include +#include + +#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 -#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