diff options
Diffstat (limited to 'arch/um/os-Linux/signal.c')
| -rw-r--r-- | arch/um/os-Linux/signal.c | 171 |
1 files changed, 102 insertions, 69 deletions
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index e9800b0b568..7b605e4dfff 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -9,10 +9,42 @@ #include <errno.h> #include <signal.h> #include <strings.h> -#include "os.h" -#include "sysdep/barrier.h" -#include "sysdep/sigcontext.h" -#include "user.h" +#include <as-layout.h> +#include <kern_util.h> +#include <os.h> +#include <sysdep/mcontext.h> +#include "internal.h" + +void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = { + [SIGTRAP] = relay_signal, + [SIGFPE] = relay_signal, + [SIGILL] = relay_signal, + [SIGWINCH] = winch, + [SIGBUS] = bus_handler, + [SIGSEGV] = segv_handler, + [SIGIO] = sigio_handler, + [SIGVTALRM] = timer_handler }; + +static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc) +{ + struct uml_pt_regs r; + int save_errno = errno; + + r.is_user = 0; + if (sig == SIGSEGV) { + /* For segfaults, we want the data from the sigcontext. */ + get_regs_from_mc(&r, mc); + GET_FAULTINFO_FROM_MC(r.faultinfo, mc); + } + + /* enable signals if sig isn't IRQ signal */ + if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGVTALRM)) + unblock_signals(); + + (*sig_info[sig])(sig, si, &r); + + errno = save_errno; +} /* * These are the asynchronous signals. SIGPROF is excluded because we want to @@ -26,62 +58,56 @@ #define SIGVTALRM_BIT 1 #define SIGVTALRM_MASK (1 << SIGVTALRM_BIT) -/* - * These are used by both the signal handlers and - * block/unblock_signals. I don't want modifications cached in a - * register - they must go straight to memory. - */ -static volatile int signals_enabled = 1; -static volatile int pending = 0; +static int signals_enabled; +static unsigned int signals_pending; -void sig_handler(int sig, struct sigcontext *sc) +void sig_handler(int sig, struct siginfo *si, mcontext_t *mc) { int enabled; enabled = signals_enabled; if (!enabled && (sig == SIGIO)) { - pending |= SIGIO_MASK; + signals_pending |= SIGIO_MASK; return; } block_signals(); - sig_handler_common_skas(sig, sc); + sig_handler_common(sig, si, mc); set_signals(enabled); } -static void real_alarm_handler(struct sigcontext *sc) +static void real_alarm_handler(mcontext_t *mc) { struct uml_pt_regs regs; - if (sc != NULL) - copy_sc(®s, sc); + if (mc != NULL) + get_regs_from_mc(®s, mc); regs.is_user = 0; unblock_signals(); - timer_handler(SIGVTALRM, ®s); + timer_handler(SIGVTALRM, NULL, ®s); } -void alarm_handler(int sig, struct sigcontext *sc) +void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc) { int enabled; enabled = signals_enabled; if (!signals_enabled) { - pending |= SIGVTALRM_MASK; + signals_pending |= SIGVTALRM_MASK; return; } block_signals(); - real_alarm_handler(sc); + real_alarm_handler(mc); set_signals(enabled); } void timer_init(void) { - set_handler(SIGVTALRM, (__sighandler_t) alarm_handler, - SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGVTALRM); } void set_sigstack(void *sig_stack, int size) @@ -94,20 +120,23 @@ void set_sigstack(void *sig_stack, int size) panic("enabling signal stack failed, errno = %d\n", errno); } -void remove_sigstack(void) -{ - stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE, - .ss_sp = NULL, - .ss_size = 0 }); +static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = { + [SIGSEGV] = sig_handler, + [SIGBUS] = sig_handler, + [SIGILL] = sig_handler, + [SIGFPE] = sig_handler, + [SIGTRAP] = sig_handler, - if (sigaltstack(&stack, NULL) != 0) - panic("disabling signal stack failed, errno = %d\n", errno); -} + [SIGIO] = sig_handler, + [SIGWINCH] = sig_handler, + [SIGVTALRM] = alarm_handler +}; -void (*handlers[_NSIG])(int sig, struct sigcontext *sc); -void handle_signal(int sig, struct sigcontext *sc) +static void hard_handler(int sig, siginfo_t *si, void *p) { + struct ucontext *uc = p; + mcontext_t *mc = &uc->uc_mcontext; unsigned long pending = 1UL << sig; do { @@ -133,7 +162,7 @@ void handle_signal(int sig, struct sigcontext *sc) while ((sig = ffs(pending)) != 0){ sig--; pending &= ~(1 << sig); - (*handlers[sig])(sig, sc); + (*handlers[sig])(sig, (struct siginfo *)si, mc); } /* @@ -147,24 +176,25 @@ void handle_signal(int sig, struct sigcontext *sc) } while (pending); } -extern void hard_handler(int sig); - -void set_handler(int sig, void (*handler)(int), int flags, ...) +void set_handler(int sig) { struct sigaction action; - va_list ap; + int flags = SA_SIGINFO | SA_ONSTACK; sigset_t sig_mask; - int mask; - handlers[sig] = (void (*)(int, struct sigcontext *)) handler; - action.sa_handler = hard_handler; + action.sa_sigaction = hard_handler; + /* block irq ones */ sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGVTALRM); + sigaddset(&action.sa_mask, SIGIO); + sigaddset(&action.sa_mask, SIGWINCH); + + if (sig == SIGSEGV) + flags |= SA_NODEFER; - va_start(ap, flags); - while ((mask = va_arg(ap, int)) != -1) - sigaddset(&action.sa_mask, mask); - va_end(ap); + if (sigismember(&action.sa_mask, sig)) + flags |= SA_RESTART; /* if it's an irq signal */ action.sa_flags = flags; action.sa_restorer = NULL; @@ -179,12 +209,14 @@ void set_handler(int sig, void (*handler)(int), int flags, ...) int change_sig(int signal, int on) { - sigset_t sigset, old; + sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, signal); - sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old); - return !sigismember(&old, signal); + if (sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL) < 0) + return -errno; + + return 0; } void block_signals(void) @@ -196,7 +228,7 @@ void block_signals(void) * This might matter if gcc figures out how to inline this and * decides to shuffle this code into the caller. */ - mb(); + barrier(); } void unblock_signals(void) @@ -209,36 +241,26 @@ void unblock_signals(void) /* * We loop because the IRQ handler returns with interrupts off. So, * interrupts may have arrived and we need to re-enable them and - * recheck pending. + * recheck signals_pending. */ - while(1) { + while (1) { /* * Save and reset save_pending after enabling signals. This - * way, pending won't be changed while we're reading it. + * way, signals_pending won't be changed while we're reading it. */ signals_enabled = 1; /* - * Setting signals_enabled and reading pending must + * Setting signals_enabled and reading signals_pending must * happen in this order. */ - mb(); - - save_pending = pending; - if (save_pending == 0) { - /* - * This must return with signals enabled, so - * this barrier ensures that writes are - * flushed out before the return. This might - * matter if gcc figures out how to inline - * this (unlikely, given its size) and decides - * to shuffle this code into the caller. - */ - mb(); + barrier(); + + save_pending = signals_pending; + if (save_pending == 0) return; - } - pending = 0; + signals_pending = 0; /* * We have pending interrupts, so disable signals, as the @@ -252,9 +274,12 @@ void unblock_signals(void) * Deal with SIGIO first because the alarm handler might * schedule, leaving the pending SIGIO stranded until we come * back here. + * + * SIGIO's handler doesn't use siginfo or mcontext, + * so they can be NULL. */ if (save_pending & SIGIO_MASK) - sig_handler_common_skas(SIGIO, NULL); + sig_handler_common(SIGIO, NULL, NULL); if (save_pending & SIGVTALRM_MASK) real_alarm_handler(NULL); @@ -279,3 +304,11 @@ int set_signals(int enable) return ret; } + +int os_is_signal_stack(void) +{ + stack_t ss; + sigaltstack(NULL, &ss); + + return ss.ss_flags & SS_ONSTACK; +} |
