#include "hw/core/tcg-cpu-ops.h"
#include "host-signal.h"
-/*
- * Stubbed out routines until we merge signal support from bsd-user
- * fork.
- */
-
static struct target_sigaction sigact_table[TARGET_NSIG];
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc);
static void target_to_host_sigset_internal(sigset_t *d,
* We have to go based on the signal number now to figure out
* what's valid.
*/
+ si_type = QEMU_SI_NOINFO;
if (has_trapno(sig)) {
tinfo->_reason._fault._trapno = info->_reason._fault._trapno;
si_type = QEMU_SI_FAULT;
* capsicum is somewhere between weak and non-existant, but if we get
* one, then we know what to save.
*/
+#ifdef QEMU_SI_CAPSICUM
if (sig == TARGET_SIGTRAP) {
tinfo->_reason._capsicum._syscall =
info->_reason._capsicum._syscall;
si_type = QEMU_SI_CAPSICUM;
}
+#endif
break;
}
tinfo->si_code = deposit32(si_code, 24, 8, si_type);
}
+static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info)
+{
+ int si_type = extract32(info->si_code, 24, 8);
+ int si_code = sextract32(info->si_code, 0, 24);
+
+ __put_user(info->si_signo, &tinfo->si_signo);
+ __put_user(info->si_errno, &tinfo->si_errno);
+ __put_user(si_code, &tinfo->si_code); /* Zero out si_type, it's internal */
+ __put_user(info->si_pid, &tinfo->si_pid);
+ __put_user(info->si_uid, &tinfo->si_uid);
+ __put_user(info->si_status, &tinfo->si_status);
+ __put_user(info->si_addr, &tinfo->si_addr);
+ /*
+ * Unswapped, because we passed it through mostly untouched. si_value is
+ * opaque to the kernel, so we didn't bother with potentially wasting cycles
+ * to swap it into host byte order.
+ */
+ tinfo->si_value.sival_ptr = info->si_value.sival_ptr;
+
+ /*
+ * We can use our internal marker of which fields in the structure
+ * are valid, rather than duplicating the guesswork of
+ * host_to_target_siginfo_noswap() here.
+ */
+ switch (si_type) {
+ case QEMU_SI_NOINFO: /* No additional info */
+ break;
+ case QEMU_SI_FAULT:
+ __put_user(info->_reason._fault._trapno,
+ &tinfo->_reason._fault._trapno);
+ break;
+ case QEMU_SI_TIMER:
+ __put_user(info->_reason._timer._timerid,
+ &tinfo->_reason._timer._timerid);
+ __put_user(info->_reason._timer._overrun,
+ &tinfo->_reason._timer._overrun);
+ break;
+ case QEMU_SI_MESGQ:
+ __put_user(info->_reason._mesgq._mqd, &tinfo->_reason._mesgq._mqd);
+ break;
+ case QEMU_SI_POLL:
+ /* Note: Not generated on FreeBSD */
+ __put_user(info->_reason._poll._band, &tinfo->_reason._poll._band);
+ break;
+#ifdef QEMU_SI_CAPSICUM
+ case QEMU_SI_CAPSICUM:
+ __put_user(info->_reason._capsicum._syscall,
+ &tinfo->_reason._capsicum._syscall);
+ break;
+#endif
+ default:
+ g_assert_not_reached();
+ }
+}
+
+int block_signals(void)
+{
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+ sigset_t set;
+
+ /*
+ * It's OK to block everything including SIGSEGV, because we won't run any
+ * further guest code before unblocking signals in
+ * process_pending_signals(). We depend on the FreeBSD behaivor here where
+ * this will only affect this thread's signal mask. We don't use
+ * pthread_sigmask which might seem more correct because that routine also
+ * does odd things with SIGCANCEL to implement pthread_cancel().
+ */
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, 0);
+
+ return qatomic_xchg(&ts->signal_pending, 1);
+}
+
/* Returns 1 if given signal should dump core if not handled. */
static int core_dump_signal(int sig)
{
cpu_exit(thread_cpu);
}
+/* do_sigaltstack() returns target values and errnos. */
+/* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */
+abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
+{
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+ int ret;
+ target_stack_t oss;
+
+ if (uoss_addr) {
+ /* Save current signal stack params */
+ oss.ss_sp = tswapl(ts->sigaltstack_used.ss_sp);
+ oss.ss_size = tswapl(ts->sigaltstack_used.ss_size);
+ oss.ss_flags = tswapl(sas_ss_flags(ts, sp));
+ }
+
+ if (uss_addr) {
+ target_stack_t *uss;
+ target_stack_t ss;
+ size_t minstacksize = TARGET_MINSIGSTKSZ;
+
+ ret = -TARGET_EFAULT;
+ if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)) {
+ goto out;
+ }
+ __get_user(ss.ss_sp, &uss->ss_sp);
+ __get_user(ss.ss_size, &uss->ss_size);
+ __get_user(ss.ss_flags, &uss->ss_flags);
+ unlock_user_struct(uss, uss_addr, 0);
+
+ ret = -TARGET_EPERM;
+ if (on_sig_stack(ts, sp)) {
+ goto out;
+ }
+
+ ret = -TARGET_EINVAL;
+ if (ss.ss_flags != TARGET_SS_DISABLE
+ && ss.ss_flags != TARGET_SS_ONSTACK
+ && ss.ss_flags != 0) {
+ goto out;
+ }
+
+ if (ss.ss_flags == TARGET_SS_DISABLE) {
+ ss.ss_size = 0;
+ ss.ss_sp = 0;
+ } else {
+ ret = -TARGET_ENOMEM;
+ if (ss.ss_size < minstacksize) {
+ goto out;
+ }
+ }
+
+ ts->sigaltstack_used.ss_sp = ss.ss_sp;
+ ts->sigaltstack_used.ss_size = ss.ss_size;
+ }
+
+ if (uoss_addr) {
+ ret = -TARGET_EFAULT;
+ if (copy_to_user(uoss_addr, &oss, sizeof(oss))) {
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/* do_sigaction() return host values and errnos */
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact)
+{
+ struct target_sigaction *k;
+ struct sigaction act1;
+ int host_sig;
+ int ret = 0;
+
+ if (sig < 1 || sig > TARGET_NSIG) {
+ return -TARGET_EINVAL;
+ }
+
+ if ((sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) &&
+ act != NULL && act->_sa_handler != TARGET_SIG_DFL) {
+ return -TARGET_EINVAL;
+ }
+
+ if (block_signals()) {
+ return -TARGET_ERESTART;
+ }
+
+ k = &sigact_table[sig - 1];
+ if (oact) {
+ oact->_sa_handler = tswapal(k->_sa_handler);
+ oact->sa_flags = tswap32(k->sa_flags);
+ oact->sa_mask = k->sa_mask;
+ }
+ if (act) {
+ k->_sa_handler = tswapal(act->_sa_handler);
+ k->sa_flags = tswap32(act->sa_flags);
+ k->sa_mask = act->sa_mask;
+
+ /* Update the host signal state. */
+ host_sig = target_to_host_signal(sig);
+ if (host_sig != SIGSEGV && host_sig != SIGBUS) {
+ memset(&act1, 0, sizeof(struct sigaction));
+ sigfillset(&act1.sa_mask);
+ act1.sa_flags = SA_SIGINFO;
+ if (k->sa_flags & TARGET_SA_RESTART) {
+ act1.sa_flags |= SA_RESTART;
+ }
+ /*
+ * Note: It is important to update the host kernel signal mask to
+ * avoid getting unexpected interrupted system calls.
+ */
+ if (k->_sa_handler == TARGET_SIG_IGN) {
+ act1.sa_sigaction = (void *)SIG_IGN;
+ } else if (k->_sa_handler == TARGET_SIG_DFL) {
+ if (fatal_signal(sig)) {
+ act1.sa_sigaction = host_signal_handler;
+ } else {
+ act1.sa_sigaction = (void *)SIG_DFL;
+ }
+ } else {
+ act1.sa_sigaction = host_signal_handler;
+ }
+ ret = sigaction(host_sig, &act1, NULL);
+ }
+ }
+ return ret;
+}
+
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
CPUArchState *env, size_t frame_size)
{
unlock_user_struct(frame, frame_addr, 1);
}
+static int reset_signal_mask(target_ucontext_t *ucontext)
+{
+ int i;
+ sigset_t blocked;
+ target_sigset_t target_set;
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.__bits[i],
+ &ucontext->uc_sigmask.__bits[i])) {
+ return -TARGET_EFAULT;
+ }
+ }
+ target_to_host_sigset_internal(&blocked, &target_set);
+ ts->signal_mask = blocked;
+
+ return 0;
+}
+
+/* See sys/$M/$M/exec_machdep.c sigreturn() */
+long do_sigreturn(CPUArchState *env, abi_ulong addr)
+{
+ long ret;
+ abi_ulong target_ucontext;
+ target_ucontext_t *ucontext = NULL;
+
+ /* Get the target ucontext address from the stack frame */
+ ret = get_ucontext_sigreturn(env, addr, &target_ucontext);
+ if (is_error(ret)) {
+ return ret;
+ }
+ trace_user_do_sigreturn(env, addr);
+ if (!lock_user_struct(VERIFY_READ, ucontext, target_ucontext, 0)) {
+ goto badframe;
+ }
+
+ /* Set the register state back to before the signal. */
+ if (set_mcontext(env, &ucontext->uc_mcontext, 1)) {
+ goto badframe;
+ }
+
+ /* And reset the signal mask. */
+ if (reset_signal_mask(ucontext)) {
+ goto badframe;
+ }
+
+ unlock_user_struct(ucontext, target_ucontext, 0);
+ return -TARGET_EJUSTRETURN;
+
+badframe:
+ if (ucontext != NULL) {
+ unlock_user_struct(ucontext, target_ucontext, 0);
+ }
+ return -TARGET_EFAULT;
+}
+
void signal_init(void)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
}
}
-void process_pending_signals(CPUArchState *cpu_env)
+void process_pending_signals(CPUArchState *env)
{
+ CPUState *cpu = env_cpu(env);
+ int sig;
+ sigset_t *blocked_set, set;
+ struct emulated_sigtable *k;
+ TaskState *ts = cpu->opaque;
+
+ while (qatomic_read(&ts->signal_pending)) {
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, 0);
+
+ restart_scan:
+ sig = ts->sync_signal.pending;
+ if (sig) {
+ /*
+ * Synchronous signals are forced by the emulated CPU in some way.
+ * If they are set to ignore, restore the default handler (see
+ * sys/kern_sig.c trapsignal() and execsigs() for this behavior)
+ * though maybe this is done only when forcing exit for non SIGCHLD.
+ */
+ if (sigismember(&ts->signal_mask, target_to_host_signal(sig)) ||
+ sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) {
+ sigdelset(&ts->signal_mask, target_to_host_signal(sig));
+ sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL;
+ }
+ handle_pending_signal(env, sig, &ts->sync_signal);
+ }
+
+ k = ts->sigtab;
+ for (sig = 1; sig <= TARGET_NSIG; sig++, k++) {
+ blocked_set = ts->in_sigsuspend ?
+ &ts->sigsuspend_mask : &ts->signal_mask;
+ if (k->pending &&
+ !sigismember(blocked_set, target_to_host_signal(sig))) {
+ handle_pending_signal(env, sig, k);
+ /*
+ * Restart scan from the beginning, as handle_pending_signal
+ * might have resulted in a new synchronous signal (eg SIGSEGV).
+ */
+ goto restart_scan;
+ }
+ }
+
+ /*
+ * Unblock signals and check one more time. Unblocking signals may cause
+ * us to take another host signal, which will set signal_pending again.
+ */
+ qatomic_set(&ts->signal_pending, 0);
+ ts->in_sigsuspend = false;
+ set = ts->signal_mask;
+ sigdelset(&set, SIGSEGV);
+ sigdelset(&set, SIGBUS);
+ sigprocmask(SIG_SETMASK, &set, 0);
+ }
+ ts->in_sigsuspend = false;
}
void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,