1.0.3.16: experimental x86-64/darwin suport
[sbcl.git] / src / runtime / interrupt.c
index 6a52053..00c920e 100644 (file)
@@ -59,7 +59,6 @@
 #include "globals.h"
 #include "lispregs.h"
 #include "validate.h"
-#include "monitor.h"
 #include "gc.h"
 #include "alloc.h"
 #include "dynbind.h"
@@ -95,8 +94,12 @@ sigaddset_deferrable(sigset_t *s)
     sigaddset(s, SIGVTALRM);
     sigaddset(s, SIGPROF);
     sigaddset(s, SIGWINCH);
+
+#if !((defined(LISP_FEATURE_DARWIN) || defined(LISP_FEATURE_FREEBSD)) && defined(LISP_FEATURE_SB_THREAD))
     sigaddset(s, SIGUSR1);
     sigaddset(s, SIGUSR2);
+#endif
+
 #ifdef LISP_FEATURE_SB_THREAD
     sigaddset(s, SIG_INTERRUPT_THREAD);
 #endif
@@ -107,6 +110,9 @@ sigaddset_blockable(sigset_t *s)
 {
     sigaddset_deferrable(s);
 #ifdef LISP_FEATURE_SB_THREAD
+#ifdef SIG_RESUME_FROM_GC
+    sigaddset(s, SIG_RESUME_FROM_GC);
+#endif
     sigaddset(s, SIG_STOP_FOR_GC);
 #endif
 }
@@ -119,7 +125,7 @@ static sigset_t blockable_sigset;
 void
 check_blockables_blocked_or_lose()
 {
-#ifndef LISP_FEATURE_WIN32
+#if !defined(LISP_FEATURE_WIN32)
     /* Get the current sigmask, by blocking the empty set. */
     sigset_t empty,current;
     int i;
@@ -180,6 +186,14 @@ block_blockable_signals(void)
 #endif
 }
 
+void
+block_deferrable_signals(void)
+{
+#ifndef LISP_FEATURE_WIN32
+    thread_sigmask(SIG_BLOCK, &deferrable_sigset, 0);
+#endif
+}
+
 \f
 /*
  * utility routines used by various signal handlers
@@ -254,6 +268,7 @@ fake_foreign_function_call(os_context_t *context)
     dynamic_space_free_pointer =
         (lispobj *)(unsigned long)
             (*os_context_register_addr(context, reg_ALLOC));
+    /* fprintf(stderr,"dynamic_space_free_pointer: %p\n", dynamic_space_free_pointer); */
 #if defined(LISP_FEATURE_ALPHA)
     if ((long)dynamic_space_free_pointer & 1) {
         lose("dead in fake_foreign_function_call, context = %x\n", context);
@@ -305,7 +320,13 @@ undo_fake_foreign_function_call(os_context_t *context)
 #ifdef reg_ALLOC
     /* Put the dynamic space free pointer back into the context. */
     *os_context_register_addr(context, reg_ALLOC) =
-        (unsigned long) dynamic_space_free_pointer;
+        (unsigned long) dynamic_space_free_pointer
+        | (*os_context_register_addr(context, reg_ALLOC)
+           & LOWTAG_MASK);
+    /*
+      ((unsigned long)(*os_context_register_addr(context, reg_ALLOC)) & ~LOWTAG_MASK)
+      | ((unsigned long) dynamic_space_free_pointer & LOWTAG_MASK);
+    */
 #endif
 }
 
@@ -355,16 +376,15 @@ interrupt_handle_pending(os_context_t *context)
     struct thread *thread;
     struct interrupt_data *data;
 
-    check_blockables_blocked_or_lose();
+    FSHOW_SIGNAL((stderr, "/entering interrupt_handle_pending\n"));
 
+    check_blockables_blocked_or_lose();
     thread=arch_os_get_current_thread();
     data=thread->interrupt_data;
 
-#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
     /* If pseudo_atomic_interrupted is set then the interrupt is going
      * to be handled now, ergo it's safe to clear it. */
     arch_clear_pseudo_atomic_interrupted(context);
-#endif
 
     if (SymbolValue(GC_INHIBIT,thread)==NIL) {
 #ifdef LISP_FEATURE_SB_THREAD
@@ -448,18 +468,23 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
     boolean were_in_lisp;
 #endif
     union interrupt_handler handler;
+
     check_blockables_blocked_or_lose();
+
+
 #ifndef LISP_FEATURE_WIN32
     if (sigismember(&deferrable_sigset,signal))
         check_interrupts_enabled_or_lose(context);
 #endif
 
-#ifdef LISP_FEATURE_LINUX
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     /* Under Linux on some architectures, we appear to have to restore
        the FPU control word from the context, as after the signal is
        delivered we appear to have a null FPU control word. */
     os_restore_fp_control(context);
 #endif
+
+
     handler = interrupt_handlers[signal];
 
     if (ARE_SAME_HANDLER(handler.c, SIG_IGN)) {
@@ -510,6 +535,9 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
             sigset_t unblock;
             sigemptyset(&unblock);
             sigaddset(&unblock, SIG_STOP_FOR_GC);
+#ifdef SIG_RESUME_FROM_GC
+            sigaddset(&unblock, SIG_RESUME_FROM_GC);
+#endif
             thread_sigmask(SIG_UNBLOCK, &unblock, 0);
         }
 #endif
@@ -556,6 +584,7 @@ run_deferred_handler(struct interrupt_data *data, void *v_context) {
      * pending handler before calling it. Trust the handler to finish
      * with the siginfo before enabling interrupts. */
     void (*pending_handler) (int, siginfo_t*, void*)=data->pending_handler;
+
     data->pending_handler=0;
     (*pending_handler)(data->pending_signal,&(data->pending_info), v_context);
 }
@@ -627,6 +656,9 @@ store_signal_data_for_later (struct interrupt_data *data, void *handler,
     data->pending_signal = signal;
     if(info)
         memcpy(&(data->pending_info), info, sizeof(siginfo_t));
+
+    FSHOW_SIGNAL((stderr, "/store_signal_data_for_later: signal: %d\n", signal));
+
     if(context) {
         /* the signal mask in the context (from before we were
          * interrupted) is copied to be restored when
@@ -642,11 +674,17 @@ static void
 maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = arch_os_get_context(&void_context);
-    struct thread *thread=arch_os_get_current_thread();
-    struct interrupt_data *data=thread->interrupt_data;
-#ifdef LISP_FEATURE_LINUX
+
+    struct thread *thread;
+    struct interrupt_data *data;
+
+    thread=arch_os_get_current_thread();
+    data=thread->interrupt_data;
+
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     os_restore_fp_control(context);
 #endif
+
     if(maybe_defer_handler(interrupt_handle_now,data,signal,info,context))
         return;
     interrupt_handle_now(signal, info, context);
@@ -661,9 +699,10 @@ low_level_interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = (os_context_t*)void_context;
 
-#ifdef LISP_FEATURE_LINUX
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     os_restore_fp_control(context);
 #endif
+
     check_blockables_blocked_or_lose();
     check_interrupts_enabled_or_lose(context);
     interrupt_low_level_handlers[signal](signal, info, void_context);
@@ -677,11 +716,16 @@ static void
 low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = arch_os_get_context(&void_context);
-    struct thread *thread=arch_os_get_current_thread();
-    struct interrupt_data *data=thread->interrupt_data;
-#ifdef LISP_FEATURE_LINUX
+    struct thread *thread;
+    struct interrupt_data *data;
+
+    thread=arch_os_get_current_thread();
+    data=thread->interrupt_data;
+
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     os_restore_fp_control(context);
 #endif
+
     if(maybe_defer_handler(low_level_interrupt_handle_now,data,
                            signal,info,context))
         return;
@@ -699,6 +743,7 @@ void
 sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = arch_os_get_context(&void_context);
+
     struct thread *thread=arch_os_get_current_thread();
     sigset_t ss;
 
@@ -723,10 +768,24 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
         thread->state=STATE_SUSPENDED;
         FSHOW_SIGNAL((stderr,"thread=%lu suspended\n",thread->os_thread));
 
+#if defined(SIG_RESUME_FROM_GC)
+        sigemptyset(&ss); sigaddset(&ss,SIG_RESUME_FROM_GC);
+#else
         sigemptyset(&ss); sigaddset(&ss,SIG_STOP_FOR_GC);
+#endif
+
         /* It is possible to get SIGCONT (and probably other
          * non-blockable signals) here. */
+#ifdef SIG_RESUME_FROM_GC
+        {
+            int sigret;
+            do { sigwait(&ss, &sigret); }
+            while (sigret != SIG_RESUME_FROM_GC);
+        }
+#else
         while (sigwaitinfo(&ss,0) != SIG_STOP_FOR_GC);
+#endif
+
         FSHOW_SIGNAL((stderr,"thread=%lu resumed\n",thread->os_thread));
         if(thread->state!=STATE_RUNNING) {
             lose("sig_stop_for_gc_handler: wrong thread state on wakeup: %ld\n",
@@ -762,7 +821,7 @@ gc_trigger_hit(int signal, siginfo_t *info, os_context_t *context)
     else{
         void *badaddr=arch_get_bad_addr(signal,info,context);
         return (badaddr >= (void *)current_auto_gc_trigger &&
-                badaddr <((void *)current_dynamic_space + DYNAMIC_SPACE_SIZE));
+                badaddr <((void *)current_dynamic_space + dynamic_space_size));
     }
 }
 #endif
@@ -778,6 +837,7 @@ extern int *context_eflags_addr(os_context_t *context);
 
 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
 extern void post_signal_tramp(void);
+extern void call_into_lisp_tramp(void);
 void
 arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 {
@@ -823,6 +883,35 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 
     u32 *sp=(u32 *)*os_context_register_addr(context,reg_ESP);
 
+#if defined(LISP_FEATURE_DARWIN)
+    u32 *register_save_area = (u32 *)os_validate(0, 0x40);
+
+    FSHOW_SIGNAL((stderr, "/arrange_return_to_lisp_function: preparing to go to function %x, sp: %x\n", function, sp));
+    FSHOW_SIGNAL((stderr, "/arrange_return_to_lisp_function: context: %x, &context %x\n", context, &context));
+
+    /* 1. os_validate (malloc/mmap) register_save_block
+     * 2. copy register state into register_save_block
+     * 3. put a pointer to register_save_block in a register in the context
+     * 4. set the context's EIP to point to a trampoline which:
+     *    a. builds the fake stack frame from the block
+     *    b. frees the block
+     *    c. calls the function
+     */
+
+    *register_save_area = *os_context_pc_addr(context);
+    *(register_save_area + 1) = function;
+    *(register_save_area + 2) = *os_context_register_addr(context,reg_EDI);
+    *(register_save_area + 3) = *os_context_register_addr(context,reg_ESI);
+    *(register_save_area + 4) = *os_context_register_addr(context,reg_EDX);
+    *(register_save_area + 5) = *os_context_register_addr(context,reg_ECX);
+    *(register_save_area + 6) = *os_context_register_addr(context,reg_EBX);
+    *(register_save_area + 7) = *os_context_register_addr(context,reg_EAX);
+    *(register_save_area + 8) = *context_eflags_addr(context);
+
+    *os_context_pc_addr(context) = call_into_lisp_tramp;
+    *os_context_register_addr(context,reg_ECX) = register_save_area;
+#else
+
     /* return address for call_into_lisp: */
     *(sp-15) = (u32)post_signal_tramp;
     *(sp-14) = function;        /* args for call_into_lisp : function*/
@@ -844,8 +933,11 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
     *(sp-2)=*os_context_register_addr(context,reg_EBP);
     *(sp-1)=*os_context_pc_addr(context);
 
+#endif
+
 #elif defined(LISP_FEATURE_X86_64)
     u64 *sp=(u64 *)*os_context_register_addr(context,reg_RSP);
+
     /* return address for call_into_lisp: */
     *(sp-18) = (u64)post_signal_tramp;
 
@@ -878,6 +970,8 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 #endif
 
 #ifdef LISP_FEATURE_X86
+
+#if !defined(LISP_FEATURE_DARWIN)
     *os_context_pc_addr(context) = (os_context_register_t)call_into_lisp;
     *os_context_register_addr(context,reg_ECX) = 0;
     *os_context_register_addr(context,reg_EBP) = (os_context_register_t)(sp-2);
@@ -886,7 +980,9 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
         (os_context_register_t)(sp-15);
 #else
     *os_context_register_addr(context,reg_ESP) = (os_context_register_t)(sp-15);
-#endif
+#endif /* __NETBSD__ */
+#endif /* LISP_FEATURE_DARWIN */
+
 #elif defined(LISP_FEATURE_X86_64)
     *os_context_pc_addr(context) = (os_context_register_t)call_into_lisp;
     *os_context_register_addr(context,reg_RCX) = 0;
@@ -920,6 +1016,7 @@ void
 interrupt_thread_handler(int num, siginfo_t *info, void *v_context)
 {
     os_context_t *context = (os_context_t*)arch_os_get_context(&v_context);
+
     /* let the handler enable interrupts again when it sees fit */
     sigaddset_deferrable(os_context_sigmask_addr(context));
     arrange_return_to_lisp_function(context, SymbolFunction(RUN_INTERRUPTION));
@@ -1056,6 +1153,9 @@ interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
     else {
         sigset_t new;
         sigemptyset(&new);
+#if defined(SIG_RESUME_FROM_GC)
+        sigaddset(&new,SIG_RESUME_FROM_GC);
+#endif
         sigaddset(&new,SIG_STOP_FOR_GC);
         thread_sigmask(SIG_UNBLOCK,&new,0);
     }
@@ -1080,18 +1180,32 @@ interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
  * the whole sa_mask is ignored and instead of not adding the signal
  * in question to the mask. That means if it's not blockable the
  * signal must be unblocked at the beginning of signal handlers.
+ *
+ * It turns out that NetBSD's SA_NODEFER doesn't DTRT in a different
+ * way: if SA_NODEFER is set and the signal is in sa_mask, the signal
+ * will be unblocked in the sigmask during the signal handler.  -- RMK
+ * X-mas day, 2005
  */
 static volatile int sigaction_nodefer_works = -1;
 
+#define SA_NODEFER_TEST_BLOCK_SIGNAL SIGABRT
+#define SA_NODEFER_TEST_KILL_SIGNAL SIGUSR1
+
 static void
 sigaction_nodefer_test_handler(int signal, siginfo_t *info, void *void_context)
 {
     sigset_t empty, current;
     int i;
     sigemptyset(&empty);
-    sigprocmask(SIG_BLOCK, &empty, &current);
+    thread_sigmask(SIG_BLOCK, &empty, &current);
+    /* There should be exactly two blocked signals: the two we added
+     * to sa_mask when setting up the handler.  NetBSD doesn't block
+     * the signal we're handling when SA_NODEFER is set; Linux before
+     * 2.6.13 or so also doesn't block the other signal when
+     * SA_NODEFER is set. */
     for(i = 1; i < NSIG; i++)
-        if (sigismember(&current, i) != ((i == SIGABRT) ? 1 : 0)) {
+        if (sigismember(&current, i) !=
+            (((i == SA_NODEFER_TEST_BLOCK_SIGNAL) || (i == signal)) ? 1 : 0)) {
             FSHOW_SIGNAL((stderr, "SA_NODEFER doesn't work, signal %d\n", i));
             sigaction_nodefer_works = 0;
         }
@@ -1107,23 +1221,28 @@ see_if_sigaction_nodefer_works()
     sa.sa_flags = SA_SIGINFO | SA_NODEFER;
     sa.sa_sigaction = sigaction_nodefer_test_handler;
     sigemptyset(&sa.sa_mask);
-    sigaddset(&sa.sa_mask, SIGABRT);
-    sigaction(SIGUSR1, &sa, &old_sa);
+    sigaddset(&sa.sa_mask, SA_NODEFER_TEST_BLOCK_SIGNAL);
+    sigaddset(&sa.sa_mask, SA_NODEFER_TEST_KILL_SIGNAL);
+    sigaction(SA_NODEFER_TEST_KILL_SIGNAL, &sa, &old_sa);
     /* Make sure no signals are blocked. */
     {
         sigset_t empty;
         sigemptyset(&empty);
-        sigprocmask(SIG_SETMASK, &empty, 0);
+        thread_sigmask(SIG_SETMASK, &empty, 0);
     }
-    kill(getpid(), SIGUSR1);
+    kill(getpid(), SA_NODEFER_TEST_KILL_SIGNAL);
     while (sigaction_nodefer_works == -1);
-    sigaction(SIGUSR1, &old_sa, NULL);
+    sigaction(SA_NODEFER_TEST_KILL_SIGNAL, &old_sa, NULL);
 }
 
+#undef SA_NODEFER_TEST_BLOCK_SIGNAL
+#undef SA_NODEFER_TEST_KILL_SIGNAL
+
 static void
 unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
     sigset_t unblock;
+
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
@@ -1134,6 +1253,7 @@ static void
 low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
     sigset_t unblock;
+
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
@@ -1156,15 +1276,21 @@ undoably_install_low_level_interrupt_handler (int signal,
         sa.sa_sigaction = handler;
     else if (sigismember(&deferrable_sigset,signal))
         sa.sa_sigaction = low_level_maybe_now_maybe_later;
+    /* The use of a trampoline appears to break the
+       arch_os_get_context() workaround for SPARC/Linux.  For now,
+       don't use the trampoline (and so be vulnerable to the problems
+       that SA_NODEFER is meant to solve. */
+#if !(defined(LISP_FEATURE_SPARC) && defined(LISP_FEATURE_LINUX))
     else if (!sigaction_nodefer_works &&
              !sigismember(&blockable_sigset, signal))
         sa.sa_sigaction = low_level_unblock_me_trampoline;
+#endif
     else
         sa.sa_sigaction = handler;
 
     sigcopyset(&sa.sa_mask, &blockable_sigset);
-    sa.sa_flags = SA_SIGINFO | SA_RESTART |
-        (sigaction_nodefer_works ? SA_NODEFER : 0);
+    sa.sa_flags = SA_SIGINFO | SA_RESTART
+        | (sigaction_nodefer_works ? SA_NODEFER : 0);
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
     if((signal==SIG_MEMORY_FAULT)
 #ifdef SIG_MEMORY_FAULT2
@@ -1258,3 +1384,24 @@ interrupt_init()
     SHOW("returning from interrupt_init()");
 #endif
 }
+
+#ifndef LISP_FEATURE_WIN32
+int
+siginfo_code(siginfo_t *info)
+{
+    return info->si_code;
+}
+os_vm_address_t current_memory_fault_address;
+
+void
+lisp_memory_fault_error(os_context_t *context, os_vm_address_t addr)
+{
+   /* FIXME: This is lossy: if we get another memory fault (eg. from
+    * another thread) before lisp has read this, we the information.
+    * However, since this is mostly informative, we'll live with that for
+    * now -- some address is better then no address in this case.
+    */
+    current_memory_fault_address = addr;
+    arrange_return_to_lisp_function(context, SymbolFunction(MEMORY_FAULT_ERROR));
+}
+#endif