diff options
Diffstat (limited to 'arch/powerpc/kernel/signal.c')
-rw-r--r-- | arch/powerpc/kernel/signal.c | 107 |
1 files changed, 105 insertions, 2 deletions
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index f92856b98b7..640b5f3611e 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -9,6 +9,7 @@ * this archive for more details. */ +#include <linux/freezer.h> #include <linux/ptrace.h> #include <linux/signal.h> #include <asm/unistd.h> @@ -16,6 +17,19 @@ #include "signal.h" +#ifdef CONFIG_PPC64 +static inline int is_32bit_task(void) +{ + return test_thread_flag(TIF_32BIT); +} +#else +static inline int is_32bit_task(void) +{ + return 1; +} +#endif + + /* * Restore the user process's signal mask */ @@ -28,8 +42,8 @@ void restore_sigmask(sigset_t *set) spin_unlock_irq(¤t->sighand->siglock); } -void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, - int has_handler) +static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, + int has_handler) { unsigned long ret = regs->gpr[3]; int restart = 1; @@ -79,6 +93,95 @@ void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, } } +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + int signr; + struct k_sigaction ka; + int ret; + int is32 = is_32bit_task(); + +#ifdef CONFIG_PPC32 + if (try_to_freeze()) { + signr = 0; + if (!signal_pending(current)) + goto no_signal; + } +#endif + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + +#ifdef CONFIG_PPC32 +no_signal: +#endif + /* Is there any syscall restart business here ? */ + check_syscall_restart(regs, &ka, signr > 0); + + if (signr <= 0) { + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } + return 0; /* no signals delivered */ + } + +#ifdef CONFIG_PPC64 + /* + * Reenable the DABR before delivering the signal to + * user space. The DABR will have been cleared if it + * triggered inside the kernel. + */ + if (current->thread.dabr) + set_dabr(current->thread.dabr); +#endif + + if (is32) { + unsigned int newsp; + + if ((ka.sa.sa_flags & SA_ONSTACK) && + current->sas_ss_size && !on_sig_stack(regs->gpr[1])) + newsp = current->sas_ss_sp + current->sas_ss_size; + else + newsp = regs->gpr[1]; + + if (ka.sa.sa_flags & SA_SIGINFO) + ret = handle_rt_signal32(signr, &ka, &info, oldset, + regs, newsp); + else + ret = handle_signal32(signr, &ka, &info, oldset, + regs, newsp); +#ifdef CONFIG_PPC64 + } else { + ret = handle_rt_signal64(signr, &ka, &info, oldset, regs); +#endif + } + + if (ret) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka.sa.sa_mask); + if (!(ka.sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, signr); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + /* + * A signal was successfully delivered; the saved sigmask is in + * its frame, and we can clear the TIF_RESTORE_SIGMASK flag. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return ret; +} + long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5, unsigned long r6, unsigned long r7, unsigned long r8, struct pt_regs *regs) |