$NetBSD$

--- src/arch/netbsd/async/signal.c.orig	2017-11-02 06:34:33.815443408 +0000
+++ src/arch/netbsd/async/signal.c
@@ -0,0 +1,1210 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <linux/version.h>
+
+#include "emu.h"
+#ifdef __linux__
+#include "sys_vm86.h"
+#endif
+#include "bios.h"
+#include "mouse.h"
+#include "video.h"
+#include "vgaemu.h"
+#include "vgatext.h"
+#include "render.h"
+#include "timers.h"
+#include "int.h"
+#include "lowmem.h"
+#include "coopth.h"
+#include "dpmi.h"
+#include "pic.h"
+#include "ipx.h"
+#include "pktdrvr.h"
+#include "iodev.h"
+#include "serial.h"
+#include "debug.h"
+#include "mhpdbg.h"
+#include "utilities.h"
+#include "userhook.h"
+#include "ringbuf.h"
+#include "dosemu_config.h"
+#include "sound.h"
+#include "cpu-emu.h"
+#include "sig.h"
+
+#define SIGALTSTACK_WA_DEFAULT 1
+#if SIGALTSTACK_WA_DEFAULT
+  #ifdef DISABLE_SYSTEM_WA
+    #ifdef SS_AUTODISARM
+      #define SIGALTSTACK_WA 0
+    #else
+      #ifdef WARN_UNDISABLED_WA
+        #warning Not disabling SIGALTSTACK_WA, update your kernel
+      #endif
+      #define SIGALTSTACK_WA 1
+    #endif
+  #else
+    /* work-around sigaltstack badness - disable when kernel is fixed */
+    #define SIGALTSTACK_WA 1
+  #endif
+  #if defined(WARN_OUTDATED_WA) && defined(SS_AUTODISARM)
+    #warning SIGALTSTACK_WA is outdated
+  #endif
+#else
+  #define SIGALTSTACK_WA 0
+#endif
+#if SIGALTSTACK_WA
+#include "mcontext.h"
+#include "mapping.h"
+#endif
+/* SS_AUTODISARM is a dosemu-specific sigaltstack extension supported
+ * by some kernels */
+#ifndef SS_AUTODISARM
+#define SS_AUTODISARM  (1U << 31)    /* disable sas during sighandling */
+#endif
+
+#ifdef __x86_64__
+  #define SIGRETURN_WA_DEFAULT 1
+#else
+  #define SIGRETURN_WA_DEFAULT 0
+#endif
+#if SIGRETURN_WA_DEFAULT
+  #ifdef DISABLE_SYSTEM_WA
+    #ifdef UC_SIGCONTEXT_SS
+      #define SIGRETURN_WA 0
+    #else
+      #ifdef WARN_UNDISABLED_WA
+        #warning Not disabling SIGRETURN_WA, update your kernel
+      #endif
+      #define SIGRETURN_WA 1
+    #endif
+  #else
+    /* work-around sigreturn badness - disable when kernel is fixed */
+    #define SIGRETURN_WA 1
+  #endif
+  #if defined(WARN_OUTDATED_WA) && defined(UC_SIGCONTEXT_SS)
+    #warning SIGRETURN_WA is outdated
+  #endif
+#else
+  #define SIGRETURN_WA 0
+#endif
+
+/* Variables for keeping track of signals */
+#define MAX_SIG_QUEUE_SIZE 50
+#define MAX_SIG_DATA_SIZE 128
+static u_short SIGNAL_head=0; u_short SIGNAL_tail=0;
+struct  SIGNAL_queue {
+  void (*signal_handler)(void *);
+  char arg[MAX_SIG_DATA_SIZE];
+  size_t arg_size;
+  const char *name;
+};
+static struct SIGNAL_queue signal_queue[MAX_SIG_QUEUE_SIZE];
+
+#define MAX_SIGCHLD_HANDLERS 10
+struct sigchld_hndl {
+  pid_t pid;
+  void (*handler)(void);
+  int enabled;
+};
+static struct sigchld_hndl chld_hndl[MAX_SIGCHLD_HANDLERS];
+static int chd_hndl_num;
+
+#define MAX_SIGALRM_HANDLERS 50
+struct sigalrm_hndl {
+  void (*handler)(void);
+};
+static struct sigalrm_hndl alrm_hndl[MAX_SIGALRM_HANDLERS];
+static int alrm_hndl_num;
+
+static sigset_t q_mask;
+static sigset_t nonfatal_q_mask;
+static sigset_t fatal_q_mask;
+static void *cstack;
+#if SIGALTSTACK_WA
+static void *backup_stack;
+static int need_sas_wa;
+#endif
+#if SIGRETURN_WA
+static int need_sr_wa;
+#endif
+static int block_all_sigs;
+
+static int sh_tid;
+static int in_handle_signals;
+static void handle_signals_force_enter(int tid, int sl_state);
+static void handle_signals_force_leave(int tid);
+static void async_awake(void *arg);
+static int event_fd;
+static struct rng_s cbks;
+#define MAX_CBKS 1000
+static pthread_mutex_t cbk_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+struct eflags_fs_gs eflags_fs_gs;
+
+static void (*sighandlers[NSIG])(struct sigcontext *, siginfo_t *);
+static void (*qsighandlers[NSIG])(int sig, siginfo_t *si, void *uc);
+
+static void sigalrm(struct sigcontext *, siginfo_t *);
+static void sigio(struct sigcontext *, siginfo_t *);
+static void sigasync(int sig, siginfo_t *si, void *uc);
+static void leavedos_sig(int sig);
+
+static void _newsetqsig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc))
+{
+	if (qsighandlers[sig])
+		return;
+	/* collect this mask so that all async (fatal and non-fatal)
+	 * signals can be blocked by threads */
+	sigaddset(&q_mask, sig);
+	qsighandlers[sig] = fun;
+}
+
+static void newsetqsig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc))
+{
+#if SIGRETURN_WA
+	sigaddset(&fatal_q_mask, sig);
+#endif
+	_newsetqsig(sig, fun);
+}
+
+static void qsig_init(void)
+{
+	struct sigaction sa;
+	int i;
+
+	sa.sa_flags = SA_RESTART | SA_ONSTACK | SA_SIGINFO;
+	if (block_all_sigs)
+	{
+		/* initially block all async signals. */
+		sa.sa_mask = q_mask;
+	}
+	else
+	{
+		/* block all non-fatal async signals */
+		sa.sa_mask = nonfatal_q_mask;
+	}
+	for (i = 0; i < NSIG; i++) {
+		if (qsighandlers[i]) {
+			sa.sa_sigaction = qsighandlers[i];
+			sigaction(i, &sa, NULL);
+		}
+	}
+}
+
+/* registers non-emergency async signals */
+void registersig(int sig, void (*fun)(struct sigcontext *, siginfo_t *))
+{
+	/* first need to collect the mask, then register all handlers
+	 * because the same mask of non-emergency async signals
+	 * is used for every handler */
+	sigaddset(&nonfatal_q_mask, sig);
+	_newsetqsig(sig, sigasync);
+	sighandlers[sig] = fun;
+}
+
+static void newsetsig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc))
+{
+	struct sigaction sa;
+
+	sa.sa_flags = SA_RESTART | SA_ONSTACK | SA_SIGINFO;
+	if (kernel_version_code >= KERNEL_VERSION(2, 6, 14))
+		sa.sa_flags |= SA_NODEFER;
+	if (block_all_sigs)
+	{
+		/* initially block all async signals. */
+		sa.sa_mask = q_mask;
+	}
+	else
+	{
+		/* block all non-fatal async signals */
+		sa.sa_mask = nonfatal_q_mask;
+	}
+	sa.sa_sigaction = fun;
+	sigaction(sig, &sa, NULL);
+}
+
+/* init_handler puts the handler in a sane state that glibc
+   expects. That means restoring fs and gs for vm86 (necessary for
+   2.4 kernels) and fs, gs and eflags for DPMI. */
+SIG_PROTO_PFX
+static void __init_handler(struct sigcontext *scp, int async)
+{
+#ifdef __x86_64__
+  unsigned short __ss;
+#endif
+  /*
+   * FIRST thing to do in signal handlers - to avoid being trapped into int0x11
+   * forever, we must restore the eflags.
+   */
+  loadflags(eflags_fs_gs.eflags);
+
+#ifdef __x86_64__
+  /* ds,es, and ss are ignored in 64-bit mode and not present or
+     saved in the sigcontext, so we need to do it ourselves
+     (using the 3 high words of the trapno field).
+     fs and gs are set to 0 in the sigcontext, so we also need
+     to save those ourselves */
+  _ds = getsegment(ds);
+  _es = getsegment(es);
+  /* some kernels save and switch ss, some do not... The simplest
+   * thing is to assume that if the ss is from GDT, then it is already
+   * saved. */
+  __ss = getsegment(ss);
+  if (DPMIValidSelector(__ss))
+    _ss = __ss;
+  _fs = getsegment(fs);
+  _gs = getsegment(gs);
+  if (_cs == 0) {
+      if (config.dpmi && config.cpuemu < 4) {
+	fprintf(stderr, "Cannot run DPMI code natively ");
+	if (kernel_version_code < KERNEL_VERSION(2, 6, 15))
+	  fprintf(stderr, "because your Linux kernel is older than version 2.6.15.\n");
+	else
+	  fprintf(stderr, "for unknown reasons.\nPlease contact linux-msdos@vger.kernel.org.\n");
+	fprintf(stderr, "Set $_cpu_emu=\"full\" or \"fullsim\" to avoid this message.\n");
+      }
+      config.cpu_vm = CPUVM_EMU;
+      config.cpuemu = 4;
+      _cs = getsegment(cs);
+  }
+#endif
+
+  if (in_vm86) {
+#ifdef __i386__
+#ifdef X86_EMULATOR
+    if (config.cpu_vm != CPUVM_EMU)
+#endif
+      {
+	if (getsegment(fs) != eflags_fs_gs.fs)
+	  loadregister(fs, eflags_fs_gs.fs);
+	if (getsegment(gs) != eflags_fs_gs.gs)
+	  loadregister(gs, eflags_fs_gs.gs);
+      }
+#endif
+    return;
+  }
+
+#if SIGRETURN_WA
+  if (need_sr_wa && !DPMIValidSelector(_cs))
+    dpmi_iret_unwind(scp);
+#endif
+
+#if 0
+  /* for async signals need to restore fs/gs even if dosemu code
+   * was interrupted, because it can be interrupted in a switching
+   * routine when fs or gs are already switched but cs is not */
+  if (!DPMIValidSelector(_cs) && !async)
+    return;
+#else
+  /* as DIRECT_DPMI_SWITCH support is now removed, the above comment
+   * applies only to DPMI_iret, which is now unwound.
+   * We don't need to restore segregs for async signals any more. */
+  if (!DPMIValidSelector(_cs))
+    return;
+#endif
+
+  /* restore %fs and %gs for compatibility with NPTL. */
+  if (getsegment(fs) != eflags_fs_gs.fs)
+    loadregister(fs, eflags_fs_gs.fs);
+  if (getsegment(gs) != eflags_fs_gs.gs)
+    loadregister(gs, eflags_fs_gs.gs);
+#ifdef __x86_64__
+  loadregister(ds, eflags_fs_gs.ds);
+  loadregister(es, eflags_fs_gs.es);
+  /* kernel has the following rule: non-zero selector means 32bit base
+   * in GDT. Zero selector means 64bit base, set via msr.
+   * So if we set selector to 0, need to use also prctl(ARCH_SET_xS).
+   * Also, if the bases are not used they are 0 so no need to restore,
+   * which saves a syscall */
+  if (!eflags_fs_gs.fs && eflags_fs_gs.fsbase)
+    dosemu_arch_prctl(ARCH_SET_FS, eflags_fs_gs.fsbase);
+  if (!eflags_fs_gs.gs && eflags_fs_gs.gsbase)
+    dosemu_arch_prctl(ARCH_SET_GS, eflags_fs_gs.gsbase);
+#endif
+}
+
+SIG_PROTO_PFX
+void init_handler(struct sigcontext *scp, int async)
+{
+  /* Async signals are initially blocked.
+   * If we don't block them, nested sighandler will clobber SS
+   * before we manage to save it.
+   * Even if the nested sighandler tries hard, it can't properly
+   * restore SS, at least until the proper sigreturn() support is in.
+   * For kernels that have the proper SS support, only nonfatal
+   * async signals are initially blocked. They need to be blocked
+   * because of sas wa and because they should not interrupt
+   * deinit_handler() after it changed %fs. In this case, however,
+   * we can block them later at the right places, but this will
+   * cost a syscall per every signal.
+   * Note: in 64bit mode some segment registers are neither saved nor
+   * restored by the signal dispatching code in kernel, so we have
+   * to restore them by hands.
+   * Note: most async signals are left blocked, we unblock only few.
+   * Sync signals like SIGSEGV are never blocked.
+   */
+  __init_handler(scp, async);
+  if (!block_all_sigs)
+    return;
+#if SIGALTSTACK_WA
+  /* for SAS WA we unblock the fatal signals even later if we came
+   * from DPMI, as then we'll be switching stacks which is racy when
+   * async signals enabled. */
+  if (need_sas_wa && DPMIValidSelector(_cs))
+    return;
+#endif
+  /* either came from dosemu/vm86 or having SS_AUTODISARM -
+   * then we can unblock any signals we want. For now leave nonfatal
+   * signals blocked as they are rarely needed inside sighandlers
+   * (needed only for instremu, see
+   * https://github.com/stsp/dosemu2/issues/477
+   * ) */
+  sigprocmask(SIG_UNBLOCK, &fatal_q_mask, NULL);
+}
+
+SIG_PROTO_PFX
+void deinit_handler(struct sigcontext *scp, unsigned long *uc_flags)
+{
+  /* in fullsim mode nothing to do */
+  if (CONFIG_CPUSIM && config.cpuemu >= 4)
+    return;
+
+  if (!DPMIValidSelector(_cs))
+    return;
+
+#ifdef __x86_64__
+#ifndef UC_SIGCONTEXT_SS
+/*
+ * UC_SIGCONTEXT_SS will be set when delivering 64-bit or x32 signals on
+ * kernels that save SS in the sigcontext.  Kernels that set UC_SIGCONTEXT_SS
+ * allow signal handlers to set UC_RESTORE_SS; if UC_RESTORE_SS is set,
+ * then sigreturn will restore SS.
+ *
+ * For compatibility with old programs, the kernel will *not* set
+ * UC_RESTORE_SS when delivering signals.
+ */
+#define UC_SIGCONTEXT_SS       0x2
+#define UC_STRICT_RESTORE_SS   0x4
+#endif
+
+  if (*uc_flags & UC_SIGCONTEXT_SS) {
+    /*
+     * On Linux 4.4 (possibly) and up, the kernel can fully restore
+     * SS and ESP, so we don't need any special tricks.  To avoid confusion,
+     * force strict restore.  (Some 4.1 versions support this as well but
+     * without the uc_flags bits.  It's not trying to detect those kernels.)
+     */
+    *uc_flags |= UC_STRICT_RESTORE_SS;
+  } else {
+#if SIGRETURN_WA
+    if (!need_sr_wa) {
+      need_sr_wa = 1;
+      warn("Enabling sigreturn() work-around\n");
+    }
+    dpmi_iret_setup(scp);
+#else
+    error("Your kernel does not support UC_STRICT_RESTORE_SS and the "
+	  "work-around in dosemu is not enabled.\n");
+    leavedos_sig(11);
+#endif
+  }
+
+  if (_fs != getsegment(fs))
+    loadregister(fs, _fs);
+  if (_gs != getsegment(gs))
+    loadregister(gs, _gs);
+
+  loadregister(ds, _ds);
+  loadregister(es, _es);
+#endif
+}
+
+static int ld_sig;
+static void leavedos_call(void *arg)
+{
+  int *sig = arg;
+  leavedos(*sig);
+}
+
+int sigchld_register_handler(pid_t pid, void (*handler)(void))
+{
+  assert(chd_hndl_num < MAX_SIGCHLD_HANDLERS);
+  chld_hndl[chd_hndl_num].handler = handler;
+  chld_hndl[chd_hndl_num].pid = pid;
+  chld_hndl[chd_hndl_num].enabled = 1;
+  chd_hndl_num++;
+  return 0;
+}
+
+int sigchld_enable_handler(pid_t pid, int on)
+{
+  int i;
+  for (i = 0; i < chd_hndl_num; i++) {
+    if (chld_hndl[i].pid == pid)
+      break;
+  }
+  if (i >= chd_hndl_num)
+    return -1;
+  chld_hndl[i].enabled = on;
+  return 0;
+}
+
+static void cleanup_child(void *arg)
+{
+  int i, status;
+  pid_t pid2, pid = *(pid_t *)arg;
+
+  for (i = 0; i < chd_hndl_num; i++) {
+    if (chld_hndl[i].pid == pid)
+      break;
+  }
+  if (i >= chd_hndl_num)
+    return;
+  if (!chld_hndl[i].enabled)
+    return;
+  pid2 = waitpid(pid, &status, WNOHANG);
+  if (pid2 != pid)
+    return;
+  if (chld_hndl[i].handler)
+    chld_hndl[i].handler();
+}
+
+/* this cleaning up is necessary to avoid the port server becoming
+   a zombie process */
+static void sig_child(struct sigcontext *scp, siginfo_t *si)
+{
+  SIGNAL_save(cleanup_child, &si->si_pid, sizeof(si->si_pid), __func__);
+}
+
+int sigalrm_register_handler(void (*handler)(void))
+{
+  assert(alrm_hndl_num < MAX_SIGALRM_HANDLERS);
+  alrm_hndl[alrm_hndl_num].handler = handler;
+  alrm_hndl_num++;
+  return 0;
+}
+
+void leavedos_from_sig(int sig)
+{
+  /* anything more sophisticated? */
+  leavedos_main(sig);
+}
+
+static void leavedos_sig(int sig)
+{
+  dbug_printf("Terminating on signal %i\n", sig);
+  SIGNAL_save(leavedos_call, &sig, sizeof(sig), __func__);
+  /* abort current sighandlers */
+  if (in_handle_signals) {
+    g_printf("Interrupting active signal handlers\n");
+    in_handle_signals = 0;
+  }
+}
+
+__attribute__((noinline))
+static void _leavedos_signal(int sig, struct sigcontext *scp)
+{
+  if (ld_sig) {
+    /* don't print anything - may lock up */
+#if 0
+    error("gracefull exit failed, aborting (sig=%i)\n", sig);
+#endif
+    _exit(sig);
+  }
+  ld_sig = sig;
+  leavedos_sig(sig);
+  if (!in_vm86)
+    dpmi_sigio(scp);
+}
+
+SIG_PROTO_PFX
+static void leavedos_signal(int sig, siginfo_t *si, void *uc)
+{
+  ucontext_t *uct = uc;
+  struct sigcontext *scp = (struct sigcontext *)&uct->uc_mcontext;
+  init_handler(scp, 1);
+  _leavedos_signal(sig, scp);
+  deinit_handler(scp, &uct->uc_flags);
+}
+
+SIG_PROTO_PFX
+static void abort_signal(int sig, siginfo_t *si, void *uc)
+{
+  struct sigcontext *scp =
+	(struct sigcontext *)&((ucontext_t *)uc)->uc_mcontext;
+  init_handler(scp, 0);
+  gdb_debug();
+  _exit(sig);
+}
+
+/* Silly Interrupt Generator Initialization/Closedown */
+
+#ifdef SIG
+SillyG_t       *SillyG = 0;
+static SillyG_t SillyG_[16 + 1];
+#endif
+
+/*
+ * DANG_BEGIN_FUNCTION SIG_init
+ *
+ * description: Allow DOSEMU to be made aware when a hard interrupt occurs
+ * The IRQ numbers to monitor are taken from config.sillyint, each bit
+ * corresponding to one IRQ. The higher 16 bit are defining the use of
+ * SIGIO
+ *
+ * DANG_END_FUNCTION
+ */
+void SIG_init(void)
+{
+#if defined(SIG)
+    PRIV_SAVE_AREA
+    /* Get in touch with Silly Interrupt Handling */
+    if (config.sillyint) {
+	char            prio_table[] =
+	{8, 9, 10, 11, 12, 14, 15, 3, 4, 5, 6, 7};
+	int             i,
+	                irq;
+	SillyG_t       *sg = SillyG_;
+	for (i = 0; i < sizeof(prio_table); i++) {
+	    irq = prio_table[i];
+	    if (config.sillyint & (1 << irq)) {
+		int ret;
+		enter_priv_on();
+		ret = vm86_plus(VM86_REQUEST_IRQ, (SIGIO << 8) | irq);
+		leave_priv_setting();
+		if ( ret > 0) {
+		    g_printf("Gonna monitor the IRQ %d you requested\n", irq);
+		    sg->fd = -1;
+		    sg->irq = irq;
+		    g_printf("SIG: IRQ%d, enabling PIC-level %ld\n", irq, pic_irq_list[irq]);
+		    sg++;
+		}
+	    }
+	}
+	sg->fd = 0;
+	if (sg != SillyG_)
+	    SillyG = SillyG_;
+    }
+#endif
+}
+
+void SIG_close(void)
+{
+#if defined(SIG)
+    if (SillyG) {
+	SillyG_t       *sg = SillyG;
+	while (sg->fd) {
+	    vm86_plus(VM86_FREE_IRQ, sg->irq);
+	    sg++;
+	}
+	g_printf("Closing all IRQ you opened!\n");
+    }
+#endif
+}
+
+void sig_ctx_prepare(int tid)
+{
+  rm_stack_enter();
+  clear_IF();
+}
+
+void sig_ctx_restore(int tid)
+{
+  rm_stack_leave();
+}
+
+static void signal_thr_post(int tid)
+{
+  in_handle_signals--;
+}
+
+static void signal_thr(void *arg)
+{
+  struct SIGNAL_queue *sig = &signal_queue[SIGNAL_head];
+  struct SIGNAL_queue sig_c;	// local copy for signal-safety
+  sig_c.signal_handler = signal_queue[SIGNAL_head].signal_handler;
+  sig_c.arg_size = sig->arg_size;
+  if (sig->arg_size)
+    memcpy(sig_c.arg, sig->arg, sig->arg_size);
+  sig_c.name = sig->name;
+  SIGNAL_head = (SIGNAL_head + 1) % MAX_SIG_QUEUE_SIZE;
+  if (debug_level('g') > 5)
+    g_printf("Processing signal %s\n", sig_c.name);
+  sig_c.signal_handler(sig_c.arg);
+}
+
+static void sigstack_init(void)
+{
+#ifndef MAP_STACK
+#define MAP_STACK 0
+#endif
+
+  /* sigaltstack_wa is optional. See if we need it. */
+  stack_t dummy = { .ss_flags = SS_DISABLE | SS_AUTODISARM };
+  int err = sigaltstack(&dummy, NULL);
+#if SIGALTSTACK_WA
+  if ((err && errno == EINVAL)
+#ifdef __i386__
+      /* kernels before 4.11 had the needed functionality only for 64bits */
+      || kernel_version_code < KERNEL_VERSION(4, 11, 0)
+#endif
+     )
+  {
+    need_sas_wa = 1;
+    warn("Enabling sigaltstack() work-around\n");
+    /* for SAS WA block all signals. If we dont, there is a
+     * race that the signal can come after we switched to backup stack
+     * but before we disabled sigaltstack. We unblock the fatal signals
+     * later, only right before switching back to dosemu. */
+    block_all_sigs = 1;
+  }
+
+  if (need_sas_wa) {
+    cstack = alloc_mapping(MAPPING_SHARED, SIGSTACK_SIZE);
+    if (cstack == MAP_FAILED) {
+      error("Unable to allocate stack\n");
+      config.exitearly = 1;
+    }
+    backup_stack = alias_mapping_high(MAPPING_OTHER, SIGSTACK_SIZE,
+	PROT_READ | PROT_WRITE, cstack);
+    if (backup_stack == MAP_FAILED) {
+      error("Unable to allocate stack\n");
+      config.exitearly = 1;
+    }
+  } else {
+    cstack = mmap(NULL, SIGSTACK_SIZE, PROT_READ | PROT_WRITE,
+	MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+    if (cstack == MAP_FAILED) {
+      error("Unable to allocate stack\n");
+      config.exitearly = 1;
+    }
+  }
+#else
+  if ((err && errno == EINVAL)
+#ifdef __i386__
+      || kernel_version_code < KERNEL_VERSION(4, 11, 0)
+#endif
+     )
+   {
+    error("Your kernel does not support SS_AUTODISARM and the "
+	  "work-around in dosemu is not enabled.\n");
+    config.exitearly = 1;
+  }
+  cstack = mmap(NULL, SIGSTACK_SIZE, PROT_READ | PROT_WRITE,
+	MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+  if (cstack == MAP_FAILED) {
+    error("Unable to allocate stack\n");
+    config.exitearly = 1;
+  }
+#endif
+}
+
+/* DANG_BEGIN_FUNCTION signal_pre_init
+ *
+ * description:
+ *  Initialize the signals to have NONE being blocked.
+ * Currently this is NOT of much use to DOSEMU.
+ *
+ * DANG_END_FUNCTION
+ *
+ */
+void
+signal_pre_init(void)
+{
+  /* initialize user data & code selector values (used by DPMI code) */
+  /* And save %fs, %gs for NPTL */
+  eflags_fs_gs.fs = getsegment(fs);
+  eflags_fs_gs.gs = getsegment(gs);
+  eflags_fs_gs.eflags = getflags();
+  dbug_printf("initial register values: fs: 0x%04x  gs: 0x%04x eflags: 0x%04lx\n",
+    eflags_fs_gs.fs, eflags_fs_gs.gs, eflags_fs_gs.eflags);
+#ifdef __x86_64__
+  eflags_fs_gs.ds = getsegment(ds);
+  eflags_fs_gs.es = getsegment(es);
+  eflags_fs_gs.ss = getsegment(ss);
+  /* get long fs and gs bases. If they are in the first 32 bits
+     normal 386-style fs/gs switching can happen so we can ignore
+     fsbase/gsbase */
+  dosemu_arch_prctl(ARCH_GET_FS, &eflags_fs_gs.fsbase);
+  if ((unsigned long)eflags_fs_gs.fsbase <= 0xffffffff)
+    eflags_fs_gs.fsbase = 0;
+  dosemu_arch_prctl(ARCH_GET_GS, &eflags_fs_gs.gsbase);
+  if ((unsigned long)eflags_fs_gs.gsbase <= 0xffffffff)
+    eflags_fs_gs.gsbase = 0;
+  dbug_printf("initial segment bases: fs: %p  gs: %p\n",
+    eflags_fs_gs.fsbase, eflags_fs_gs.gsbase);
+#endif
+
+  /* first set up the blocking mask: registersig() and newsetqsig()
+   * adds to it */
+  sigemptyset(&q_mask);
+  sigemptyset(&nonfatal_q_mask);
+  registersig(SIGALRM, sigalrm);
+  registersig(SIGIO, sigio);
+  registersig(SIGCHLD, sig_child);
+  newsetqsig(SIGQUIT, leavedos_signal);
+  newsetqsig(SIGINT, leavedos_signal);   /* for "graceful" shutdown for ^C too*/
+  newsetqsig(SIGHUP, leavedos_signal);	/* for "graceful" shutdown */
+  newsetqsig(SIGTERM, leavedos_signal);
+  /* below ones are initialized by other subsystems */
+  registersig(SIGPROF, NULL);
+  registersig(SIG_ACQUIRE, NULL);
+  registersig(SIG_RELEASE, NULL);
+  /* mask is set up, now start using it */
+  qsig_init();
+  newsetsig(SIGILL, dosemu_fault);
+  newsetsig(SIGFPE, dosemu_fault);
+  newsetsig(SIGTRAP, dosemu_fault);
+  newsetsig(SIGBUS, dosemu_fault);
+  newsetsig(SIGABRT, abort_signal);
+  newsetsig(SIGSEGV, dosemu_fault);
+
+  /* block async signals so that threads inherit the blockage */
+  sigprocmask(SIG_BLOCK, &q_mask, NULL);
+
+  signal(SIGPIPE, SIG_IGN);
+  dosemu_pthread_self = pthread_self();
+}
+
+void
+signal_init(void)
+{
+  sigstack_init();
+#if SIGRETURN_WA
+  /* 4.6+ are able to correctly restore SS */
+  if (kernel_version_code < KERNEL_VERSION(4, 6, 0)) {
+    need_sr_wa = 1;
+    warn("Enabling sigreturn() work-around for old kernel\n");
+    /* block all sigs for SR WA. If we dont, the signal can come before
+     * SS is saved, but we can't restore SS on signal exit. */
+    block_all_sigs = 1;
+  }
+#endif
+
+  sh_tid = coopth_create("signal handling");
+  /* normally we don't need ctx handlers because the thread is detached.
+   * But some crazy code (vbe.c) can call coopth_attach() on it, so we
+   * set up the handlers just in case. */
+  coopth_set_ctx_handlers(sh_tid, sig_ctx_prepare, sig_ctx_restore);
+  coopth_set_sleep_handlers(sh_tid, handle_signals_force_enter,
+	handle_signals_force_leave);
+  coopth_set_permanent_post_handler(sh_tid, signal_thr_post);
+  coopth_set_detached(sh_tid);
+
+  event_fd = eventfd(0, EFD_CLOEXEC);
+  add_to_io_select(event_fd, async_awake, NULL);
+  rng_init(&cbks, MAX_CBKS, sizeof(struct callback_s));
+
+  /* unblock async signals in main thread */
+  pthread_sigmask(SIG_UNBLOCK, &q_mask, NULL);
+}
+
+void signal_done(void)
+{
+    struct itimerval itv;
+
+    itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0;
+    itv.it_value = itv.it_interval;
+    if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
+	g_printf("can't turn off timer at shutdown: %s\n", strerror(errno));
+    registersig(SIGALRM, NULL);
+    registersig(SIGIO, NULL);
+    registersig(SIGCHLD, NULL);
+    signal(SIGCHLD, SIG_DFL);
+    SIGNAL_head = SIGNAL_tail;
+}
+
+static void handle_signals_force_enter(int tid, int sl_state)
+{
+  if (!in_handle_signals) {
+    dosemu_error("in_handle_signals=0\n");
+    return;
+  }
+  in_handle_signals--;
+}
+
+static void handle_signals_force_leave(int tid)
+{
+  in_handle_signals++;
+}
+
+int signal_pending(void)
+{
+  return (SIGNAL_head != SIGNAL_tail);
+}
+
+/*
+ * DANG_BEGIN_FUNCTION handle_signals
+ *
+ * description:
+ *  Due to signals happening at any time, the actual work to be done
+ * because a signal occurs is done here in a serial fashion.
+ *
+ * The concept, should this eventualy work, is that a signal should only
+ * flag that it has occurred and let DOSEMU deal with it in an orderly
+ * fashion as it executes the rest of it's code.
+ *
+ * DANG_END_FUNCTION
+ *
+ */
+void handle_signals(void)
+{
+  while (signal_pending() && !in_handle_signals) {
+    in_handle_signals++;
+    coopth_start(sh_tid, signal_thr, NULL);
+    coopth_run_tid(sh_tid);
+  }
+}
+
+/* ==============================================================
+ *
+ * This is called by default at around 100Hz.
+ * (see timer_interrupt_init() in init.c)
+ *
+ * The actual formulas, starting with the configurable parameter
+ * config.freq, are:
+ *	config.freq				default=18
+ *	config.update = 1E6/config.freq		default=54945
+ *	timer tick(us) = config.update/6	default=9157.5us
+ *		       = 166667/config.freq
+ *	timer tick(Hz) = 6*config.freq		default=100Hz
+ *
+ * 6 is the magical TIMER_DIVISOR macro used to get 100Hz
+ *
+ * This call should NOT be used if you need timing accuracy - many
+ * signals can get lost e.g. when kernel accesses disk, and the whole
+ * idea of timing-by-counting is plain wrong. We'll need the Pentium
+ * counter here.
+ * ============================================================== */
+
+static void SIGALRM_call(void *arg)
+{
+  static int first = 0;
+  static hitimer_t cnt200 = 0;
+  static hitimer_t cnt1000 = 0;
+  int i;
+
+  if (first==0) {
+    cnt200 =
+    cnt1000 =
+    pic_sys_time;	/* initialize */
+    first = 1;
+  }
+
+  if (video_initialized && !config.vga)
+    update_screen();
+
+  for (i = 0; i < alrm_hndl_num; i++)
+    alrm_hndl[i].handler();
+
+  if (config.rdtsc)
+    update_cputime_TSCBase();
+  timer_tick();
+
+#if 0
+/*
+ * DANG_BEGIN_REMARK
+ *  Check for keyboard coming from client
+ *  For now, first byte is interrupt requests from Client
+ * DANG_END_REMARK
+ */
+ if (*(u_char *)(shared_qf_memory + CLIENT_REQUEST_FLAG_AREA) & 0x40) {
+   k_printf("KBD: Client sent key\n");
+   pic_request (PIC_IRQ1);
+   *(u_char *)(shared_qf_memory + CLIENT_REQUEST_FLAG_AREA) &=  ~0x40;
+ }
+#endif
+
+  io_select();	/* we need this in order to catch lost SIGIOs */
+  /* catch user hooks here */
+  if (uhook_fdin != -1) uhook_poll();
+
+  alarm_idle();
+
+  /* Here we 'type in' prestrokes from commandline, as long as there are any
+   * Were won't overkill dosemu, hence we type at a speed of 14cps
+   */
+  if (config.pre_stroke) {
+    static int count=-1;
+    if (--count < 0) {
+      count = type_in_pre_strokes();
+      if (count <0) count =7; /* with HZ=100 we have a stroke rate of 14cps */
+    }
+  }
+
+  /* this should be for per-second activities, it is actually at
+   * 200ms more or less (PARTIALS=5) */
+  if ((pic_sys_time-cnt200) >= (PIT_TICK_RATE/PARTIALS)) {
+    cnt200 = pic_sys_time;
+/*    g_printf("**** ALRM: %dms\n",(1000/PARTIALS)); */
+
+    printer_tick(0);
+    floppy_tick();
+  }
+
+/* We update the RTC from here if it has not been defined as a thread */
+
+  /* this is for EXACT per-second activities (can produce bursts) */
+  if ((pic_sys_time-cnt1000) >= PIT_TICK_RATE) {
+    cnt1000 += PIT_TICK_RATE;
+/*    g_printf("**** ALRM: 1sec\n"); */
+    rtc_update();
+  }
+}
+
+/* DANG_BEGIN_FUNCTION SIGNAL_save
+ *
+ * arguments:
+ * context     - signal context to save.
+ * signal_call - signal handling routine to be called.
+ *
+ * description:
+ *  Save into an array structure queue the signal context of the current
+ * signal as well as the function to call for dealing with this signal.
+ * This is a queue because any signal may occur multiple times before
+ * DOSEMU deals with it down the road.
+ *
+ * DANG_END_FUNCTION
+ *
+ */
+void SIGNAL_save(void (*signal_call)(void *), void *arg, size_t len,
+	const char *name)
+{
+  signal_queue[SIGNAL_tail].signal_handler = signal_call;
+  signal_queue[SIGNAL_tail].arg_size = len;
+  assert(len <= MAX_SIG_DATA_SIZE);
+  if (len)
+    memcpy(signal_queue[SIGNAL_tail].arg, arg, len);
+  signal_queue[SIGNAL_tail].name = name;
+  SIGNAL_tail = (SIGNAL_tail + 1) % MAX_SIG_QUEUE_SIZE;
+  if (in_dpmi_pm())
+    dpmi_return_request();
+}
+
+
+/*
+ * DANG_BEGIN_FUNCTION SIGIO_call
+ *
+ * description:
+ *  Whenever I/O occurs on devices allowing SIGIO to occur, DOSEMU
+ * will be flagged to run this call which inturn checks which
+ * fd(s) was set and execute the proper routine to get the I/O
+ * from that device.
+ *
+ * DANG_END_FUNCTION
+ *
+ */
+static void SIGIO_call(void *arg){
+  /* Call select to see if any I/O is ready on devices */
+  io_select();
+}
+
+#ifdef __linux__
+static void sigio(struct sigcontext *scp, siginfo_t *si)
+{
+  /* prints non reentrant! dont do! */
+#if 0
+  g_printf("got SIGIO\n");
+#endif
+  e_gen_sigalrm(scp);
+  SIGNAL_save(SIGIO_call, NULL, 0, __func__);
+  if (!in_vm86)
+    dpmi_sigio(scp);
+}
+
+static void sigalrm(struct sigcontext *scp, siginfo_t *si)
+{
+  if(e_gen_sigalrm(scp)) {
+    SIGNAL_save(SIGALRM_call, NULL, 0, __func__);
+    if (!in_vm86)
+      dpmi_sigio(scp);
+  }
+}
+
+__attribute__((noinline))
+static void sigasync0(int sig, struct sigcontext *scp, siginfo_t *si)
+{
+  pthread_t tid = pthread_self();
+  if (!pthread_equal(tid, dosemu_pthread_self)) {
+    char name[128];
+    pthread_getname_np(tid, name, sizeof(name));
+    dosemu_error("Async signal %i from thread %s\n", sig, name);
+  }
+  if (sighandlers[sig])
+	  sighandlers[sig](scp, si);
+}
+
+SIG_PROTO_PFX
+static void sigasync(int sig, siginfo_t *si, void *uc)
+{
+  ucontext_t *uct = uc;
+  struct sigcontext *scp = (struct sigcontext *)&uct->uc_mcontext;
+  init_handler(scp, 1);
+  sigasync0(sig, scp, si);
+  deinit_handler(scp, &uct->uc_flags);
+}
+#endif
+
+
+void do_periodic_stuff(void)
+{
+    check_leavedos();
+    handle_signals();
+#ifdef USE_MHPDBG
+    /* mhp_debug() must be called exactly after handle_signals()
+     * and before coopth_run(). handle_signals() feeds input to
+     * debugger, and coopth_run() runs DPMI (oops!). We need to
+     * run debugger before DPMI to not lose single-steps. */
+    if (mhpdbg.active)
+	mhp_debug(DBG_POLL, 0, 0);
+#endif
+    coopth_run();
+
+    if (video_initialized && Video && Video->change_config)
+	update_xtitle();
+}
+
+void add_thread_callback(void (*cb)(void *), void *arg, const char *name)
+{
+  if (cb) {
+    struct callback_s cbk;
+    int i;
+    cbk.func = cb;
+    cbk.arg = arg;
+    cbk.name = name;
+    pthread_mutex_lock(&cbk_mtx);
+    i = rng_put(&cbks, &cbk);
+    g_printf("callback %s added, %i queued\n", name, rng_count(&cbks));
+    pthread_mutex_unlock(&cbk_mtx);
+    if (!i)
+      error("callback queue overflow, %s\n", name);
+  }
+  eventfd_write(event_fd, 1);
+  /* unfortunately eventfd does not support SIGIO :( So we kill ourself. */
+  pthread_kill(dosemu_pthread_self, SIGIO);
+}
+
+static void async_awake(void *arg)
+{
+  struct callback_s cbk;
+  int i;
+  eventfd_t val;
+  eventfd_read(event_fd, &val);
+  g_printf("processing %"PRId64" callbacks\n", val);
+  do {
+    pthread_mutex_lock(&cbk_mtx);
+    i = rng_get(&cbks, &cbk);
+    pthread_mutex_unlock(&cbk_mtx);
+    if (i)
+      cbk.func(cbk.arg);
+  } while (i);
+}
+
+static int saved_fc;
+
+void signal_switch_to_dosemu(void)
+{
+  saved_fc = fault_cnt;
+  fault_cnt = 0;
+}
+
+void signal_switch_to_dpmi(void)
+{
+  fault_cnt = saved_fc;
+}
+
+#if SIGALTSTACK_WA
+static void signal_sas_wa(void)
+{
+  int err;
+  stack_t ss = {};
+  m_ucontext_t hack;
+  unsigned char *sp;
+  unsigned char *top = cstack + SIGSTACK_SIZE;
+  unsigned char *btop = backup_stack + SIGSTACK_SIZE;
+  ptrdiff_t delta;
+
+  if (getmcontext(&hack) == 0) {
+    sp = alloca(sizeof(void *));
+    delta = top - sp;
+    asm volatile(
+#ifdef __x86_64__
+    "mov %0, %%rsp\n"
+#else
+    "mov %0, %%esp\n"
+#endif
+     :: "r"(btop - delta) : "sp");
+  } else {
+    sigprocmask(SIG_UNBLOCK, &fatal_q_mask, NULL);
+    return;
+  }
+
+  ss.ss_flags = SS_DISABLE;
+  /* sas will re-enable itself when returning from sighandler */
+  err = sigaltstack(&ss, NULL);
+  if (err)
+    perror("sigaltstack");
+
+  setmcontext(&hack);
+}
+#endif
+
+void signal_return_to_dosemu(void)
+{
+#if SIGALTSTACK_WA
+  if (need_sas_wa)
+    signal_sas_wa();
+#endif
+}
+
+void signal_return_to_dpmi(void)
+{
+}
+
+void signal_set_altstack(int on)
+{
+  stack_t stk;
+
+  if (!on) {
+    stk.ss_sp = NULL;
+    stk.ss_size = 0;
+    stk.ss_flags = SS_DISABLE;
+  } else {
+    stk.ss_sp = cstack;
+    stk.ss_size = SIGSTACK_SIZE;
+#if SIGALTSTACK_WA
+    stk.ss_flags = SS_ONSTACK | (need_sas_wa ? 0 : SS_AUTODISARM);
+#else
+    stk.ss_flags = SS_ONSTACK | SS_AUTODISARM;
+#endif
+  }
+  sigaltstack(&stk, NULL);
+}
+
+void signal_unblock_async_sigs(void)
+{
+  /* unblock only nonfatal, fatals should already be unblocked */
+  sigprocmask(SIG_UNBLOCK, &nonfatal_q_mask, NULL);
+}
+
+void signal_restore_async_sigs(void)
+{
+  /* block sigs even for !sas_wa because if deinit_handler is
+   * interrupted after changing %fs, we are in troubles */
+  sigprocmask(SIG_BLOCK, &nonfatal_q_mask, NULL);
+}
