From 4d6a77829fa4b24134a06f623e04a8cfed351eee Mon Sep 17 00:00:00 2001 From: Stacey Son Date: Sun, 2 Jun 2013 09:06:52 -0500 Subject: [PATCH 17/23] bsd-user: add shims for thread related system calls To: Cc: This change adds support for thread and user mutex related system call shims. Much of this change is FreeBSD dependent. Also, the thread start up code is architecture dependent and is only included for mips/mips64 for now. Stubs were added for the other architectures. Signed-off-by: Stacey Son --- bsd-user/Makefile.objs | 4 +- bsd-user/freebsd/os-thread.c | 802 +++++++++++++++++++++++++++++++++ bsd-user/freebsd/os-thread.h | 742 ++++++++++++++++++++++++++++++ bsd-user/freebsd/qemu-os.h | 5 + bsd-user/i386/target_arch_signal.h | 12 + bsd-user/i386/target_arch_thread.h | 30 ++ bsd-user/main.c | 225 +++++++++- bsd-user/mips/target_arch_signal.h | 8 +- bsd-user/mips/target_arch_thread.h | 45 ++ bsd-user/mips64/target_arch_signal.h | 8 +- bsd-user/mips64/target_arch_thread.h | 45 ++ bsd-user/qemu.h | 35 ++ bsd-user/sparc/target_arch_signal.h | 12 + bsd-user/sparc/target_arch_thread.h | 30 ++ bsd-user/sparc64/target_arch_signal.h | 12 + bsd-user/sparc64/target_arch_thread.h | 30 ++ bsd-user/syscall.c | 70 +++ bsd-user/syscall_defs.h | 91 ++++ bsd-user/x86_64/target_arch_signal.h | 12 + bsd-user/x86_64/target_arch_thread.h | 30 ++ configure | 11 + include/qemu/tls.h | 2 +- 22 files changed, 2232 insertions(+), 29 deletions(-) create mode 100644 bsd-user/freebsd/os-thread.c create mode 100644 bsd-user/freebsd/os-thread.h create mode 100644 bsd-user/i386/target_arch_thread.h create mode 100644 bsd-user/mips/target_arch_thread.h create mode 100644 bsd-user/mips64/target_arch_thread.h create mode 100644 bsd-user/sparc/target_arch_thread.h create mode 100644 bsd-user/sparc64/target_arch_thread.h create mode 100644 bsd-user/x86_64/target_arch_thread.h diff --git a/bsd-user/Makefile.objs b/bsd-user/Makefile.objs index cc7a82b..2608337 100644 --- a/bsd-user/Makefile.objs +++ b/bsd-user/Makefile.objs @@ -1,4 +1,4 @@ obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ uaccess.o bsd-mem.o bsd-proc.o $(TARGET_OS)/os-time.o \ - $(TARGET_OS)/os-proc.o bsd-socket.o $(TARGET_OS)/os-socket.o \ - $(TARGET_OS)/os-stat.o + $(TARGET_OS)/os-proc.o bsd-socket.o $(TARGET_OS)/os-socket.o \ + $(TARGET_OS)/os-stat.o $(TARGET_OS)/os-thread.o diff --git a/bsd-user/freebsd/os-thread.c b/bsd-user/freebsd/os-thread.c new file mode 100644 index 0000000..98873f3 --- /dev/null +++ b/bsd-user/freebsd/os-thread.c @@ -0,0 +1,802 @@ +/* + * 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 "qemu.h" +#include "qemu-os.h" +#include "target_arch_thread.h" + +/* 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; +}; + +#if defined(CONFIG_USE_NPTL) + +static pthread_mutex_t new_thread_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t *new_freebsd_thread_lock_ptr = &new_thread_lock; + +void *new_freebsd_thread_start(void *arg) +{ + new_freebsd_thread_info_t *info = arg; + CPUArchState *env; + TaskState *ts; + long tid; + + env = info->env; + thread_env = env; + fork_end(1); + + ts = (TaskState *)thread_env->opaque; + (void)thr_self(&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. */ + 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; +} +#endif /* CONFIG_USE_NPTL */ + +/* + * 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) +{ + + 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) +{ + + 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) +{ + + return get_errno(_umtx_op(g2h(obj), UMTX_OP_WAKE_PRIVATE, val, NULL, NULL)); +} + +#if defined(__FreeBSD_version) && __FreeBSD_version > 900000 +abi_long freebsd_umtx_nwake_private(abi_ulong obj, uint32_t val) +{ + + return get_errno(_umtx_op(g2h(obj), UMTX_OP_NWAKE_PRIVATE, val, NULL, + NULL)); +} + +abi_long freebsd_umtx_mutex_wake2(abi_ulong obj, uint32_t val) +{ + if (!access_ok(VERIFY_WRITE, obj, sizeof(struct target_ucond))) { + return -TARGET_EFAULT; + } + return get_errno(_umtx_op(g2h(obj), UMTX_OP_MUTEX_WAKE2, val, NULL, NULL)); +} + +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); +#ifdef TARGET_ABI32 + ret = get_errno(_umtx_op(target_umtx, UMTX_OP_WAIT_UINT, owner, + NULL, timeout)); +#else + ret = get_errno(_umtx_op(target_umtx, 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. */ + _umtx_op(target_umtx, 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; + } + + /* id has already been byte swapped to match what may be in user mem. */ +#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) +{ + + 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) +{ + + 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; + + if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) { + 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); + 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); + 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); + return 0; + } + /* The lock changed so restart. */ + unlock_user_struct(target_umutex, target_addr, 1); + 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); + return -TARGET_EDEADLK; + } + + if (TARGET_UMUTEX_TRY == mode) { + unlock_user_struct(target_umutex, target_addr, 1); + 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); + 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); + continue; + } + + owner = owner | TARGET_UMUTEX_CONTESTED; + unlock_user_struct(target_umutex, target_addr, 1); + + /* Byte swap, if needed, to match what is stored in user mem. */ + owner = tswap32(owner); + ret = get_errno(_umtx_op(target_umutex, UMTX_OP_WAIT_UINT, owner, NULL, + ts)); + } + + if (ts == NULL) { + /* + * In the case of no timeout do a restart on this syscall, + * if interrupted. + */ + if (ret == -TARGET_EINTR) { + ret = -TARGET_ERESTART; + } + } + 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. */ + 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_REALTIME || 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 */ + 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); + 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); + 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); + ret = get_errno(_umtx_op(&target_urwlock->rw_blocked_readers, + UMTX_OP_WAIT_UINT, blocked_readers, 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) { + ret = get_errno(_umtx_op(&target_urwlock->rw_blocked_readers, + 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); + ret = get_errno(_umtx_op(&target_urwlock->rw_blocked_writers, + UMTX_OP_WAIT_UINT, blocked_writers, 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; + void *q = NULL; + + 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; + } + + count = 0; + + if (!(flags & TARGET_URWLOCK_PREFER_READER)) { + if (state & TARGET_URWLOCK_WRITE_WAITERS) { + count = 1; + q = &target_urwlock->rw_blocked_writers; + } else if (state & TARGET_URWLOCK_READ_WAITERS) { + count = INT_MAX; + q = &target_urwlock->rw_blocked_readers; + } + } else { + if (state & TARGET_URWLOCK_READ_WAITERS) { + count = INT_MAX; + q = &target_urwlock->rw_blocked_readers; + } else if (state & TARGET_URWLOCK_WRITE_WAITERS) { + count = 1; + q = &target_urwlock->rw_blocked_writers; + } + } + + unlock_user_struct(target_urwlock, target_addr, 1); + if (q != NULL) { + return get_errno(_umtx_op(q, UMTX_OP_WAKE, count, NULL, NULL)); + } else { + return 0; + } +} + + diff --git a/bsd-user/freebsd/os-thread.h b/bsd-user/freebsd/os-thread.h new file mode 100644 index 0000000..4c8b6ce --- /dev/null +++ b/bsd-user/freebsd/os-thread.h @@ -0,0 +1,742 @@ +/* + * 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 "qemu-os.h" + +#if defined(CONFIG_USE_NPTL) + +#define NEW_STACK_SIZE 0x40000 + +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 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; + } +} + +static 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. */ + fork_start(); + ts = g_malloc0(sizeof(TaskState)); + init_task_state(ts); + new_env = cpu_copy(env); +#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) + cpu_reset(ENV_GET_CPU(new_env)); +#endif + + /* init regs that differ from the parent thread. */ + cpu_clone_regs(new_env, info.param.stack_base); + new_env->opaque = ts; + ts->bprm = parent_ts->bprm; + ts->info = parent_ts->info; + +#if defined(TARGET_MIPS) || defined(TARGET_ARM) + cpu_set_tls(env, info.param.tls_base); +#endif + + /* 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 return values should be checked... */ + 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. */ + + fork_end(0); + + 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. Free new CPU state. */ + cpu_list_lock(); + env->next_cpu = new_env->next_cpu; + cpu_list_unlock(); + g_free(ts); + object_unref(OBJECT(ENV_GET_CPU(env))); + } + + 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; +} + +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) +{ + + if (first_cpu->next_cpu) { + TaskState *ts; + CPUArchState **lastp, *p; + + /* + * *XXX This probably breaks if a signal arrives. + * We should disable signals. + */ + cpu_list_lock(); + lastp = &first_cpu; + p = first_cpu; + while (p && p != (CPUArchState *)cpu_env) { + lastp = &p->next_cpu; + p = p->next_cpu; + } + /* + * if we didn't find the CPU for this thread then something + * is horribly wrong. + */ + if (!p) { + abort(); + } + /* Remove the CPU from the list. */ + *lastp = p->next_cpu; + cpu_list_unlock(); + ts = ((CPUArchState *)cpu_env)->opaque; + + 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_env = NULL; + object_unref(OBJECT(ENV_GET_CPU(cpu_env))); + g_free(ts); + pthread_exit(NULL); + } + + gdb_exit(cpu_env, 0); /* XXX need to put in the correct exit status here? */ + _exit(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; +} + +#else /* ! CONFIG_USE_NPTL */ + +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; +} +#endif /* ! CONFIG_USE_NPTL */ + +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; + +#if defined(__FreeBSD_version) && __FreeBSD_version > 900000 + 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 + + 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; + +#ifdef UMTX_OP_MUTEX_WAKE2 + case TARGET_UMTX_OP_MUTEX_WAKE2: + ret = freebsd_umtx_mutex_wake2(obj, val); + break; +#endif + +#if defined(__FreeBSD_version) && __FreeBSD_version > 900000 + 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 0dd65f5..eac9085 100644 --- a/bsd-user/freebsd/qemu-os.h +++ b/bsd-user/freebsd/qemu-os.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -62,4 +63,8 @@ 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); + #endif /* !_QEMU_OS_H_ */ diff --git a/bsd-user/i386/target_arch_signal.h b/bsd-user/i386/target_arch_signal.h index 2e89529..7fd1399 100644 --- a/bsd-user/i386/target_arch_signal.h +++ b/bsd-user/i386/target_arch_signal.h @@ -27,6 +27,8 @@ #define TARGET_MINSIGSTKSZ (512 * 4) /* min sig stack size */ #define TARGET_SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended size */ +#define TARGET_MC_GET_CLEAR_RET 0x0001 + struct target_sigcontext { /* to be added */ }; @@ -53,9 +55,19 @@ struct target_sigframe { uint32_t __spare__[2]; }; +/* Compare to i386/i386/machdep.c get_mcontext() */ +static inline abi_long get_mcontext(CPUX86State *regs, + target_mcontext_t *mcp, int flags) +{ + /* XXX */ + return -TARGET_EOPNOTSUPP; +} + +/* Compare to i386/i386/machdep.c set_mcontext() */ static inline abi_long set_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int srflag) { + /* XXX */ return -TARGET_EOPNOTSUPP; } diff --git a/bsd-user/i386/target_arch_thread.h b/bsd-user/i386/target_arch_thread.h new file mode 100644 index 0000000..8307dce --- /dev/null +++ b/bsd-user/i386/target_arch_thread.h @@ -0,0 +1,30 @@ +/* + * i386 thread support + * + * 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 _TARGET_ARCH_THREAD_H_ +#define _TARGET_ARCH_THREAD_H_ + +/* Compare to vm_machdep.c cpu_set_upcall_kse() */ +static inline void +thread_set_upcall(CPUX86State *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + /* XXX */ +} + +#endif /* !_TARGET_ARCH_THREAD_H_ */ diff --git a/bsd-user/main.c b/bsd-user/main.c index 1fa5e7f..bcfc7ce 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -31,7 +31,6 @@ #include "qemu.h" #include "qemu-common.h" -/* For tb_lock */ #include "cpu.h" #include "tcg.h" #include "qemu/timer.h" @@ -99,41 +98,187 @@ int cpu_get_pic_interrupt(CPUX86State *env) } #endif -/* These are no-ops because we are not threadsafe. */ -static inline void cpu_exec_start(CPUArchState *env) +#if defined(CONFIG_USE_NPTL) +/* Helper routines for implementing atomic operations. */ + +/* + * To implement exclusive operations we force all cpus to synchronize. + * We don't require a full sync, only that no cpus are executing guest code. + * The alternative is to map target atomic ops onto host eqivalents, + * which requires quite a lot of per host/target work. + */ +static pthread_mutex_t cpu_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t exclusive_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t exclusive_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t exclusive_resume = PTHREAD_COND_INITIALIZER; +static int pending_cpus; + +/* Make sure everything is in a consistent state for calling fork(). */ +void fork_start(void) { + pthread_mutex_lock(&tcg_ctx.tb_ctx.tb_lock); + pthread_mutex_lock(&exclusive_lock); + mmap_fork_start(); } -static inline void cpu_exec_end(CPUArchState *env) +void fork_end(int child) { + mmap_fork_end(child); + if (child) { + /* + * Child processes created by fork() only have a single thread. + * Discard information about the parent threads. + */ + first_cpu = thread_env; + thread_env->next_cpu = NULL; + pending_cpus = 0; + pthread_mutex_init(&exclusive_lock, NULL); + pthread_mutex_init(&cpu_list_mutex, NULL); + pthread_cond_init(&exclusive_cond, NULL); + pthread_cond_init(&exclusive_resume, NULL); + pthread_mutex_init(&tcg_ctx.tb_ctx.tb_lock, NULL); + gdbserver_fork(thread_env); + } else { + pthread_mutex_unlock(&exclusive_lock); + pthread_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock); + } } -static inline void start_exclusive(void) +/* + * Wait for pending exclusive operations to complete. The exclusive lock + * must be held. + */ +static inline void +exclusive_idle(void) { + while (pending_cpus) { + pthread_cond_wait(&exclusive_resume, &exclusive_lock); + } } -static inline void end_exclusive(void) +/* Start an exclusive operation. Must only be called outside of cpu_exec. */ +static inline void +start_exclusive(void) { + CPUArchState *other; + CPUState *other_cpu; + + pthread_mutex_lock(&exclusive_lock); + exclusive_idle(); + + pending_cpus = 1; + /* Make all other cpus stop executing. */ + for (other = first_cpu; other; other = other->next_cpu) { + other_cpu = ENV_GET_CPU(other); + if (other_cpu->running) { + pending_cpus++; + cpu_exit(other); + } + } + if (pending_cpus > 1) { + pthread_cond_wait(&exclusive_cond, &exclusive_lock); + } } -void fork_start(void) +/* Finish an exclusive operation. */ +static inline void +end_exclusive(void) { + pending_cpus = 0; + pthread_cond_broadcast(&exclusive_resume); + pthread_mutex_unlock(&exclusive_lock); } -void fork_end(int child) +/* Wait for exclusive ops to finish, and begin cpu execution. */ +static inline void +cpu_exec_start(CPUState *env) +{ + pthread_mutex_lock(&exclusive_lock); + exclusive_idle(); + env->running = 1; + pthread_mutex_unlock(&exclusive_lock); +} + +/* Mark cpu as not excuting, and release pending exclusive ops. */ +static inline void +cpu_exec_end(CPUState *env) +{ + pthread_mutex_lock(&exclusive_lock); + env->running = 0; + if (pending_cpus > 1) { + pending_cpus--; + if (pending_cpus == 1) { + pthread_cond_signal(&exclusive_cond); + } + } + exclusive_idle(); + pthread_mutex_unlock(&exclusive_lock); +} + +void +cpu_list_lock(void) +{ + pthread_mutex_lock(&cpu_list_mutex); +} + +void +cpu_list_unlock(void) +{ + pthread_mutex_unlock(&cpu_list_mutex); +} + +#else /* ! CONFIG_USE_NPTL */ + +/* These are no-ops because we are not threadsafe. */ +void +fork_start(void) +{ +} + +void +fork_end(int child) { if (child) { gdbserver_fork(thread_env); } } -void cpu_list_lock(void) +static inline void +exclusive_idle(void) +{ +} + +static inline void +start_exclusive(void) +{ +} + +static inline void +end_exclusive(void) +{ +} + +static inline void +cpu_exec_start(CPUState *env) +{ +} + + +static inline void +cpu_exec_end(CPUState *env) +{ +} + +void +cpu_list_lock(void) { } -void cpu_list_unlock(void) +void +cpu_list_unlock(void) { } +#endif /* ! CONFIG_USE_NPTL */ #ifdef TARGET_I386 /***********************************************************/ @@ -422,19 +567,67 @@ void cpu_loop(CPUX86State *env) /* Compare to sys/mips/mips/trap.c */ +static int do_store_exclusive(CPUMIPSState *env) +{ + target_ulong addr; + target_ulong page_addr; + target_ulong val; + int flags; + int segv = 0; + int reg; + int d; + + addr = env->lladdr; + page_addr = addr & TARGET_PAGE_MASK; + start_exclusive(); + mmap_lock(); + flags = page_get_flags(page_addr); + if ((flags & PAGE_READ) == 0) { + segv = 1; + } else { + reg = env->llreg & 0x1f; + d = (env->llreg & 0x20) != 0; + if (d) { + segv = get_user_s64(val, addr); + } else { + segv = get_user_s32(val, addr); + } + if (!segv) { + if (val != env->llval) { + env->active_tc.gpr[reg] = 0; + } else { + if (d) { + segv = put_user_u64(env->llnewval, addr); + } else { + segv = put_user_u32(env->llnewval, addr); + } + if (!segv) { + env->active_tc.gpr[reg] = 1; + } + } + } + } + env->lladdr = -1; + if (!segv) { + env->active_tc.PC += 4; + } + mmap_unlock(); + end_exclusive(); + return segv; +} + void cpu_loop(CPUMIPSState *env) { -#if 0 /* not yet */ + CPUState *cs = CPU(mips_env_get_cpu(env)); target_siginfo_t info; -#endif int trapnr; abi_long ret; unsigned int syscall_num; for (;;) { - cpu_exec_start(env); + cpu_exec_start(cs); trapnr = cpu_mips_exec(env); - cpu_exec_end(env); + cpu_exec_end(cs); switch (trapnr) { case EXCP_SYSCALL: /* syscall exception */ syscall_num = env->active_tc.gpr[2]; /* v0 */ @@ -521,7 +714,6 @@ void cpu_loop(CPUMIPSState *env) env->active_tc.gpr[2] = ret; /* v0 <- ret */ break; -#if 0 /* not yet */ case EXCP_TLBL: /* TLB miss on load */ case EXCP_TLBS: /* TLB miss on store */ case EXCP_AdEL: /* bad address on load */ @@ -541,13 +733,11 @@ void cpu_loop(CPUMIPSState *env) info.target_si_code = 0; queue_signal(env, info.target_si_signo, &info); break; -#endif /* not yet */ case EXCP_INTERRUPT: /* async interrupt */ /* just indicate that signals should be handled asap */ break; -#if 0 /* not yet */ case EXCP_DEBUG: /* cpu stopped after a breakpoint */ { int sig; @@ -571,7 +761,6 @@ void cpu_loop(CPUMIPSState *env) queue_signal(env, info.target_si_signo, &info); } break; -#endif /* not yet */ default: fprintf(stderr, "qemu: unhandled CPU exception " diff --git a/bsd-user/mips/target_arch_signal.h b/bsd-user/mips/target_arch_signal.h index f68034f..6be34c6 100644 --- a/bsd-user/mips/target_arch_signal.h +++ b/bsd-user/mips/target_arch_signal.h @@ -124,8 +124,8 @@ set_sigtramp_args(CPUMIPSState *regs, int sig, struct target_sigframe *frame, * Compare to mips/mips/pm_machdep.c get_mcontext() * Assumes that the memory is locked if mcp points to user memory. */ -static inline abi_long -get_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, int flags) +static inline abi_long get_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, + int flags) { int i, err = 0; @@ -180,8 +180,8 @@ get_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, int flags) } /* Compare to mips/mips/pm_machdep.c set_mcontext() */ -static inline abi_long set_mcontext(CPUMIPSState *regs, - target_mcontext_t *mcp, int srflag) +static inline abi_long set_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, + int srflag) { int i, err = 0; diff --git a/bsd-user/mips/target_arch_thread.h b/bsd-user/mips/target_arch_thread.h new file mode 100644 index 0000000..9384941 --- /dev/null +++ b/bsd-user/mips/target_arch_thread.h @@ -0,0 +1,45 @@ +/* + * mips thread support + * + * 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 _TARGET_ARCH_THREAD_H_ +#define _TARGET_ARCH_THREAD_H_ + +/* Compare to mips/mips/vm_machdep.c cpu_set_upcall_kse() */ +static inline void +thread_set_upcall(CPUMIPSState *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + abi_ulong sp; + + /* + * At the point where a function is called, sp must be 8 + * byte aligned[for compatibility with 64-bit CPUs] + * in ``See MIPS Run'' by D. Sweetman, p. 269 + * align stack + */ + sp = ((stack_base + stack_size) & ~0x7) - TARGET_CALLFRAME_SIZ; + + /* t9 = pc = start function entry */ + regs->active_tc.gpr[25] = regs->active_tc.PC = entry; + /* a0 = arg */ + regs->active_tc.gpr[4] = arg; + /* sp = top of the stack */ + regs->active_tc.gpr[29] = sp; +} + +#endif /* !_TARGET_ARCH_THREAD_H_ */ diff --git a/bsd-user/mips64/target_arch_signal.h b/bsd-user/mips64/target_arch_signal.h index 5edcc3a..ab84e0f 100644 --- a/bsd-user/mips64/target_arch_signal.h +++ b/bsd-user/mips64/target_arch_signal.h @@ -123,8 +123,8 @@ set_sigtramp_args(CPUMIPSState *regs, int sig, struct target_sigframe *frame, * Compare to mips/mips/pm_machdep.c get_mcontext() * Assumes that the memory is locked if mcp points to user memory. */ -static inline abi_long -get_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, int flags) +static inline abi_long get_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, + int flags) { int i, err = 0; @@ -179,8 +179,8 @@ get_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, int flags) } /* Compare to mips/mips/pm_machdep.c set_mcontext() */ -static inline abi_long set_mcontext(CPUMIPSState *regs, - target_mcontext_t *mcp, int srflag) +static inline abi_long set_mcontext(CPUMIPSState *regs, target_mcontext_t *mcp, + int srflag) { int i, err = 0; diff --git a/bsd-user/mips64/target_arch_thread.h b/bsd-user/mips64/target_arch_thread.h new file mode 100644 index 0000000..bf363af --- /dev/null +++ b/bsd-user/mips64/target_arch_thread.h @@ -0,0 +1,45 @@ +/* + * mips64 thread support + * + * 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 _MIPS64_ARCH_THREAD_H_ +#define _MIPS64_ARCH_THREAD_H_ + +/* Compare to mips/mips/vm_machdep.c cpu_set_upcall_kse() */ +static inline void +thread_set_upcall(CPUMIPSState *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + abi_ulong sp; + + /* + * At the point where a function is called, sp must be 8 + * byte aligned[for compatibility with 64-bit CPUs] + * in ``See MIPS Run'' by D. Sweetman, p. 269 + * align stack + */ + sp = ((stack_base + stack_size) & ~0x7) - TARGET_CALLFRAME_SIZ; + + /* t9 = pc = start function entry */ + regs->active_tc.gpr[25] = regs->active_tc.PC = entry; + /* a0 = arg */ + regs->active_tc.gpr[4] = arg; + /* sp = top of the stack */ + regs->active_tc.gpr[29] = sp; +} + +#endif /* !_MIPS64_ARCH_THREAD_H_ */ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 7ded869..f063974 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -243,6 +243,41 @@ int is_error(abi_long ret); abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, abi_ulong guest_envp, int do_fexec); +/* 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 diff --git a/bsd-user/sparc/target_arch_signal.h b/bsd-user/sparc/target_arch_signal.h index 275d1ef..70b89b9 100644 --- a/bsd-user/sparc/target_arch_signal.h +++ b/bsd-user/sparc/target_arch_signal.h @@ -10,6 +10,8 @@ #define TARGET_MINSIGSTKSZ (1024 * 4) /* min sig stack size */ #define TARGET_SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended size */ +#define TARGET_MC_GET_CLEAR_RET 0x0001 + struct target_sigcontext { /* to be added */ }; @@ -36,9 +38,19 @@ struct target_sigframe { uint32_t __spare__[2]; }; +/* Compare to sparc64/sparc64/machdep.c get_mcontext() */ +static inline abi_long get_mcontext(CPUSPARCState *regs, + target_mcontext_t *mcp, int flags) +{ + /* XXX */ + return -TARGET_EOPNOTSUPP; +} + +/* Compare to sparc64/space64/machdep.c set_mcontext() */ static inline abi_long set_mcontext(CPUSPARCState *regs, target_mcontext_t *mcp, int srflag) { + /* XXX */ return -TARGET_EOPNOTSUPP; } diff --git a/bsd-user/sparc/target_arch_thread.h b/bsd-user/sparc/target_arch_thread.h new file mode 100644 index 0000000..ddfbbe2 --- /dev/null +++ b/bsd-user/sparc/target_arch_thread.h @@ -0,0 +1,30 @@ +/* + * sparc thread support + * + * 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 _TARGET_ARCH_THREAD_H_ +#define _TARGET_ARCH_THREAD_H_ + +/* Compare to vm_machdep.c cpu_set_upcall_kse() */ +static inline void +thread_set_upcall(CPUSPARCState *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + /* XXX */ +} + +#endif /* !_TARGET_ARCH_THREAD_H_ */ diff --git a/bsd-user/sparc64/target_arch_signal.h b/bsd-user/sparc64/target_arch_signal.h index 8126383..cc075ed 100644 --- a/bsd-user/sparc64/target_arch_signal.h +++ b/bsd-user/sparc64/target_arch_signal.h @@ -27,6 +27,8 @@ #define TARGET_MINSIGSTKSZ (1024 * 4) /* min sig stack size */ #define TARGET_SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended size */ +#define TARGET_MC_GET_CLEAR_RET 0x0001 + struct target_sigcontext { /* to be added */ }; @@ -53,9 +55,19 @@ struct target_sigframe { uint32_t __spare__[2]; }; +/* Compare to sparc64/sparc64/machdep.c get_mcontext() */ +static inline abi_long get_mcontext(CPUSPARCState *regs, + target_mcontext_t *mcp, int flags) +{ + /* XXX */ + return -TARGET_EOPNOTSUPP; +} + +/* Compare to sparc64/space64/machdep.c set_mcontext() */ static inline abi_long set_mcontext(CPUSPARCState *regs, target_mcontext_t *mcp, int srflag) { + /* XXX */ return -TARGET_EOPNOTSUPP; } diff --git a/bsd-user/sparc64/target_arch_thread.h b/bsd-user/sparc64/target_arch_thread.h new file mode 100644 index 0000000..0f06d14 --- /dev/null +++ b/bsd-user/sparc64/target_arch_thread.h @@ -0,0 +1,30 @@ +/* + * sparc64 thread support + * + * 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 _TARGET_ARCH_THREAD_H_ +#define _TARGET_ARCH_THREAD_H_ + +/* Compare to vm_machdep.c cpu_set_upcall_kse() */ +static inline void +thread_set_upcall(CPUSPARCState *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + /* XXX */ +} + +#endif /* !_TARGET_ARCH_THREAD_H_ */ diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index fee602b..86aa471 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -34,17 +34,20 @@ #define target_to_host_bitmask(x, tbl) (x) static int host_to_target_errno(int err); +/* BSD independent syscall shims */ #include "bsd-file.h" #include "bsd-mem.h" #include "bsd-proc.h" #include "bsd-signal.h" #include "bsd-socket.h" +/* *BSD dependent syscall shims */ #include "os-time.h" #include "os-proc.h" #include "os-signal.h" #include "os-socket.h" #include "os-stat.h" +#include "os-thread.h" /* #define DEBUG */ @@ -1271,6 +1274,73 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, arg6, arg7, arg8); 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; + case TARGET_FREEBSD_NR_break: ret = do_obreak(arg1); diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index 0db7126..b4e0739 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -498,4 +498,95 @@ struct target_freebsd_flock { int32_t l_sysid; } QEMU_PACKED; +/* + * FreeBSD thread and user mutex support. + */ + +/* sys/thr.h */ +#define TARGET_THR_SUSPENDED 0x0001 +#define TARGET_THR_SYSTEM_SCOPE 0x0002 + +struct target_freebsd_thr_param { + abi_ulong start_func; /* thread entry function. */ + abi_ulong arg; /* argument for entry function. */ + abi_ulong stack_base; /* stack base address. */ + abi_ulong stack_size; /* stack size. */ + abi_ulong tls_base; /* tls base address. */ + abi_ulong tls_size; /* tls size. */ + abi_ulong child_tid; /* address to store new TID. */ + abi_ulong parent_tid; /* parent access the new TID here. */ + int32_t flags; /* thread flags. */ + abi_ulong rtp; /* Real-time scheduling priority. */ + abi_ulong spare[3]; /* spares. */ +}; + +/* sys/rtprio.h */ +struct target_freebsd_rtprio { + uint16_t type; + uint16_t prio; +}; + +typedef struct { + CPUArchState *env; + long parent_tid; + pthread_mutex_t mutex; + pthread_cond_t cond; + pthread_t thread; + sigset_t sigmask; + struct target_freebsd_thr_param param; +} new_freebsd_thread_info_t; + +/* sys/utmx.h */ +/* op code for _umtx_op */ +#define TARGET_UMTX_OP_LOCK 0 +#define TARGET_UMTX_OP_UNLOCK 1 +#define TARGET_UMTX_OP_WAIT 2 +#define TARGET_UMTX_OP_WAKE 3 +#define TARGET_UMTX_OP_MUTEX_TRYLOCK 4 +#define TARGET_UMTX_OP_MUTEX_LOCK 5 +#define TARGET_UMTX_OP_MUTEX_UNLOCK 6 +#define TARGET_UMTX_OP_SET_CEILING 7 +#define TARGET_UMTX_OP_CV_WAIT 8 +#define TARGET_UMTX_OP_CV_SIGNAL 9 +#define TARGET_UMTX_OP_CV_BROADCAST 10 +#define TARGET_UMTX_OP_WAIT_UINT 11 +#define TARGET_UMTX_OP_RW_RDLOCK 12 +#define TARGET_UMTX_OP_RW_WRLOCK 13 +#define TARGET_UMTX_OP_RW_UNLOCK 14 +#define TARGET_UMTX_OP_WAIT_UINT_PRIVATE 15 +#define TARGET_UMTX_OP_WAKE_PRIVATE 16 +#define TARGET_UMTX_OP_MUTEX_WAIT 17 +#define TARGET_UMTX_OP_MUTEX_WAKE 18 +#define TARGET_UMTX_OP_SEM_WAIT 19 +#define TARGET_UMTX_OP_SEM_WAKE 20 +#define TARGET_UMTX_OP_NWAKE_PRIVATE 21 +#define TARGET_UMTX_OP_MUTEX_WAKE2 22 +#define TARGET_UMTX_OP_MAX 23 + +/* flags for UMTX_OP_CV_WAIT */ +#define TARGET_CVWAIT_CHECK_UNPARKING 0x01 +#define TARGET_CVWAIT_ABSTIME 0x02 +#define TARGET_CVWAIT_CLOCKID 0x04 + +#define TARGET_UMTX_UNOWNED 0x0 +#define TARGET_UMUTEX_UNOWNED 0x0 +#define TARGET_UMTX_CONTESTED (abi_long)(0x8000000000000000) +#define TARGET_UMUTEX_CONTESTED 0x80000000U + +/* flags for umutex */ +#define TARGET_UMUTEX_ERROR_CHECK 0x0002 /* Error-checking mutex */ +#define TARGET_UMUTEX_PRIO_INHERIT 0x0004 /* Priority inherited mutex */ +#define TARGET_UMUTEX_PRIO_PROTECT 0x0008 /* Priority protect mutex */ + +#define TARGET_UMUTEX_TRY 1 +#define TARGET_UMUTEX_WAIT 2 + +/* urwlock flags */ +#define TARGET_URWLOCK_PREFER_READER 0x0002 +#define TARGET_URWLOCK_WRITE_OWNER 0x80000000U +#define TARGET_URWLOCK_WRITE_WAITERS 0x40000000U +#define TARGET_URWLOCK_READ_WAITERS 0x20000000U +#define TARGET_URWLOCK_MAX_READERS 0x1fffffffU +#define TARGET_URWLOCK_READER_COUNT(c) ((c) & TARGET_URWLOCK_MAX_READERS) + #endif /* ! _SYSCALL_DEFS_H_ */ diff --git a/bsd-user/x86_64/target_arch_signal.h b/bsd-user/x86_64/target_arch_signal.h index d43f5f0..7525a56 100644 --- a/bsd-user/x86_64/target_arch_signal.h +++ b/bsd-user/x86_64/target_arch_signal.h @@ -27,6 +27,8 @@ #define TARGET_MINSIGSTKSZ (512 * 4) /* min sig stack size */ #define TARGET_SIGSTKSZ (MINSIGSTKSZ + 32768) /* recommended size */ +#define TARGET_MC_GET_CLEAR_RET 0x0001 + struct target_sigcontext { /* to be added */ }; @@ -53,9 +55,19 @@ struct target_sigframe { uint32_t __spare__[2]; }; +/* Compare to amd64/amd64/machdep.c get_mcontext() */ +static inline abi_long get_mcontext(CPUX86State *regs, + target_mcontext_t *mcp, int flags) +{ + /* XXX */ + return -TARGET_EOPNOTSUPP; +} + +/* Compare to amd64/amd64/machdep.c set_mcontext() */ static inline abi_long set_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int srflag) { + /* XXX */ return -TARGET_EOPNOTSUPP; } diff --git a/bsd-user/x86_64/target_arch_thread.h b/bsd-user/x86_64/target_arch_thread.h new file mode 100644 index 0000000..32a833f --- /dev/null +++ b/bsd-user/x86_64/target_arch_thread.h @@ -0,0 +1,30 @@ +/* + * x86_64 thread support + * + * 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 _TARGET_ARCH_THREAD_H_ +#define _TARGET_ARCH_THREAD_H_ + +/* Compare to vm_machdep.c cpu_set_upcall_kse() */ +static inline void +thread_set_upcall(CPUX86State *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + /* XXX */ +} + +#endif /* !_TARGET_ARCH_THREAD_H_ */ diff --git a/configure b/configure index ba90975..812ea3f 100755 --- a/configure +++ b/configure @@ -1438,6 +1438,15 @@ fi if test "$nptl" != "no" ; then cat > $TMPC < +int main(void) { +#if !defined(THR_SUSPENDED) +#error bork +#endif + return 0; +} +#else #include #include int main(void) { @@ -1446,6 +1455,7 @@ int main(void) { #endif return 0; } +#endif EOF if compile_object ; then @@ -4202,6 +4212,7 @@ case "$target_name" in TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak + target_nptl="yes" ;; moxie) ;; 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