From 22e38f29328296d9d4cc33e46fd32a63e807abaf Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 4 Jun 2007 15:15:49 +1000 Subject: [POWERPC] Make syscall restart code more common This patch moves the code in signal_32.c and signal_64.c for handling syscall restart into a common signal.c file and converge around a single implementation that is based on the 32 bits one, using trap, ccr and r3 rather than the special "result" field for deciding what to do. The "result" field is now pretty much deprecated. We still set it for the sake of whatever might rely on it in userland but we no longer use it's content. This, along with a previous patch that enables ptracers to write to "trap" and "orig_r3" should allow gdb to properly handle syscall restarting. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/signal_64.c | 59 +++++------------------------------------ 1 file changed, 7 insertions(+), 52 deletions(-) (limited to 'arch/powerpc/kernel/signal_64.c') diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 1ce0ae3f6ffc..5d2faf0fbf05 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -34,6 +34,8 @@ #include #include +#include "signal.h" + #define DEBUG_SIG 0 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -463,41 +465,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, return ret; } -static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) -{ - switch ((int)regs->result) { - case -ERESTART_RESTARTBLOCK: - case -ERESTARTNOHAND: - /* ERESTARTNOHAND means that the syscall should only be - * restarted if there was no handler for the signal, and since - * we only get here if there is a handler, we dont restart. - */ - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - break; - case -ERESTARTSYS: - /* ERESTARTSYS means to restart the syscall if there is no - * handler or the handler was registered with SA_RESTART - */ - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - /* ERESTARTNOINTR means that the syscall should be - * called again after the signal handler returns. - */ - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; - regs->result = 0; - break; - } -} - /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by @@ -522,13 +489,13 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + /* Is there any syscall restart business here ? */ + check_syscall_restart(regs, &ka, signr > 0); + if (signr > 0) { int ret; - /* Whee! Actually deliver the signal. */ - if (TRAP(regs) == 0x0C00) - syscall_restart(regs, &ka); - /* * Reenable the DABR before delivering the signal to * user space. The DABR will have been cleared if it @@ -537,6 +504,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) if (current->thread.dabr) set_dabr(current->thread.dabr); + /* Whee! Actually deliver the signal. */ ret = handle_signal(signr, &ka, &info, oldset, regs); /* If a signal was successfully delivered, the saved sigmask is in @@ -547,19 +515,6 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) return ret; } - if (TRAP(regs) == 0x0C00) { /* System Call! */ - if ((int)regs->result == -ERESTARTNOHAND || - (int)regs->result == -ERESTARTSYS || - (int)regs->result == -ERESTARTNOINTR) { - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; /* Back up & retry system call */ - regs->result = 0; - } else if ((int)regs->result == -ERESTART_RESTARTBLOCK) { - regs->gpr[0] = __NR_restart_syscall; - regs->nip -= 4; - regs->result = 0; - } - } /* No signal to deliver -- put the saved sigmask back */ if (test_thread_flag(TIF_RESTORE_SIGMASK)) { clear_thread_flag(TIF_RESTORE_SIGMASK); -- cgit v1.2.3 From 69d15f6b352a681f1db9bc70219a3e8e9d503dbf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 4 Jun 2007 15:15:50 +1000 Subject: [POWERPC] Consolidate sys_sigaltstack sys_sigaltstack is the same on 32bit and 64 and we can consolidate it to signal.c. The only difference is that the 32bit code uses ints for the unused register paramaters and 64bit unsigned long. I've changed it to unsigned long because it's the same width on 32bit. (I also wonder who came up with this awkward calling convention.. :)) Signed-off-by: Christoph Hellwig Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/signal.c | 7 +++++++ arch/powerpc/kernel/signal_32.c | 8 -------- arch/powerpc/kernel/signal_64.c | 8 -------- include/asm-powerpc/syscalls.h | 7 ------- 4 files changed, 7 insertions(+), 23 deletions(-) (limited to 'arch/powerpc/kernel/signal_64.c') diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 88a12544e8b1..325d260abd4e 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -63,3 +63,10 @@ void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, regs->ccr |= 0x10000000; } } + +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) +{ + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index e5cc803476a1..f5713bfcc56e 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -253,14 +253,6 @@ long sys_sigsuspend(old_sigset_t mask) return -ERESTARTNOHAND; } -#ifdef CONFIG_PPC32 -long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, int r5, - int r6, int r7, int r8, struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->gpr[1]); -} -#endif - long sys_sigaction(int sig, struct old_sigaction __user *act, struct old_sigaction __user *oact) { diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 5d2faf0fbf05..817f1cf4a405 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -66,14 +66,6 @@ struct rt_sigframe { char abigap[288]; } __attribute__ ((aligned (16))); -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) -{ - return do_sigaltstack(uss, uoss, regs->gpr[1]); -} - - /* * Set up the sigcontext for the signal frame. */ diff --git a/include/asm-powerpc/syscalls.h b/include/asm-powerpc/syscalls.h index c2fe79d4f90f..b3ca41fc8bb1 100644 --- a/include/asm-powerpc/syscalls.h +++ b/include/asm-powerpc/syscalls.h @@ -43,16 +43,9 @@ asmlinkage long ppc_newuname(struct new_utsname __user * name); asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize); - -#ifndef __powerpc64__ -asmlinkage long sys_sigaltstack(const stack_t __user *uss, - stack_t __user *uoss, int r5, int r6, int r7, int r8, - struct pt_regs *regs); -#else /* __powerpc64__ */ asmlinkage 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); -#endif /* __powerpc64__ */ #endif /* __KERNEL__ */ #endif /* __ASM_POWERPC_SYSCALLS_H */ -- cgit v1.2.3 From db277e9a67b9d81b9d6cd74edf0c3e1a0ef2aa4b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 4 Jun 2007 15:15:51 +1000 Subject: [POWERPC] Consolidate restore_sigmask restore_sigmask is exactly the same on 32 and 64bit, so move it to common code. Also move _BLOCKABLE to signal.h to avoid defining it multiple times. Signed-off-by: Christoph Hellwig Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/signal.c | 15 +++++++++++++++ arch/powerpc/kernel/signal.h | 3 +++ arch/powerpc/kernel/signal_32.c | 19 ------------------- arch/powerpc/kernel/signal_64.c | 15 --------------- 4 files changed, 18 insertions(+), 34 deletions(-) (limited to 'arch/powerpc/kernel/signal_64.c') diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 325d260abd4e..f92856b98b70 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -13,6 +13,21 @@ #include #include +#include "signal.h" + + +/* + * Restore the user process's signal mask + */ +void restore_sigmask(sigset_t *set) +{ + sigdelsetmask(set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) { diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 1e8dfb8620ad..4b091d8b764c 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -10,6 +10,9 @@ #ifndef _POWERPC_ARCH_SIGNAL_H #define _POWERPC_ARCH_SIGNAL_H +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +extern void restore_sigmask(sigset_t *set); extern void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler); diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index f5713bfcc56e..1d899a56fa13 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -55,8 +55,6 @@ #undef DEBUG_SIG -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - #ifdef CONFIG_PPC64 #define do_signal do_signal32 #define sys_sigsuspend compat_sys_sigsuspend @@ -697,23 +695,6 @@ int compat_sys_sigaltstack(u32 __new, u32 __old, int r5, } #endif /* CONFIG_PPC64 */ - -/* - * Restore the user process's signal mask - */ -#ifdef CONFIG_PPC64 -extern void restore_sigmask(sigset_t *set); -#else /* CONFIG_PPC64 */ -static void restore_sigmask(sigset_t *set) -{ - sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} -#endif - /* * Set up a signal frame for a "real-time" signal handler * (one which gets siginfo). diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 817f1cf4a405..7e9c4b7e7e82 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -38,8 +38,6 @@ #define DEBUG_SIG 0 -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) #define FP_REGS_SIZE sizeof(elf_fpregset_t) @@ -242,19 +240,6 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) return err; } -/* - * Restore the user process's signal mask (also used by signal32.c) - */ -void restore_sigmask(sigset_t *set) -{ - sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - - /* * Handle {get,set,swap}_context operations */ -- cgit v1.2.3 From f478f5430c8a599f46c41e8172a507a5772a6b69 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 4 Jun 2007 15:15:52 +1000 Subject: [POWERPC] Consolidate do_signal do_signal has exactly the same behaviour on 32bit and 64bit and 32bit compat on 64bit for handling 32bit signals. Consolidate all these into one common function in signal.c. The only odd left over is the try_to_free in the 32bit version that no other architecture has in mainline (only in i386 for some odd SuSE release). We should probably get rid of it in a separate patch. Signed-off-by: Christoph Hellwig Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/signal.c | 107 +++++++++++++++++++++++++++++++++++++++- arch/powerpc/kernel/signal.h | 14 +++++- arch/powerpc/kernel/signal_32.c | 88 +-------------------------------- arch/powerpc/kernel/signal_64.c | 86 +------------------------------- 4 files changed, 120 insertions(+), 175 deletions(-) (limited to 'arch/powerpc/kernel/signal_64.c') diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index f92856b98b70..640b5f3611ee 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -9,6 +9,7 @@ * this archive for more details. */ +#include #include #include #include @@ -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) diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 4b091d8b764c..190d4325f974 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -13,7 +13,17 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) extern void restore_sigmask(sigset_t *set); -extern void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, - int has_handler); + +extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs, unsigned long newsp); + +extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs, unsigned long newsp); + +extern int handle_rt_signal64(int signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *set, + struct pt_regs *regs); #endif /* _POWERPC_ARCH_SIGNAL_H */ diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 1d899a56fa13..32481e71d71e 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -56,7 +56,6 @@ #undef DEBUG_SIG #ifdef CONFIG_PPC64 -#define do_signal do_signal32 #define sys_sigsuspend compat_sys_sigsuspend #define sys_rt_sigsuspend compat_sys_rt_sigsuspend #define sys_rt_sigreturn compat_sys_rt_sigreturn @@ -231,8 +230,6 @@ static inline int restore_general_regs(struct pt_regs *regs, #endif /* CONFIG_PPC64 */ -int do_signal(sigset_t *oldset, struct pt_regs *regs); - /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -699,7 +696,7 @@ int compat_sys_sigaltstack(u32 __new, u32 __old, int r5, * Set up a signal frame for a "real-time" signal handler * (one which gets siginfo). */ -static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka, +int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, unsigned long newsp) { @@ -990,7 +987,7 @@ int sys_debug_setcontext(struct ucontext __user *ctx, /* * OK, we're invoking a handler */ -static int handle_signal(unsigned long sig, struct k_sigaction *ka, +int handle_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, unsigned long newsp) { @@ -1101,84 +1098,3 @@ badframe: force_sig(SIGSEGV, current); return 0; } - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -int do_signal(sigset_t *oldset, struct pt_regs *regs) -{ - siginfo_t info; - struct k_sigaction ka; - unsigned int newsp; - int signr, ret; - -#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 */ - } - - 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]; - newsp &= ~0xfUL; - -#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 - - /* Whee! Actually deliver the signal. */ - if (ka.sa.sa_flags & SA_SIGINFO) - ret = handle_rt_signal(signr, &ka, &info, oldset, regs, newsp); - else - ret = handle_signal(signr, &ka, &info, oldset, regs, newsp); - - 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; -} diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 7e9c4b7e7e82..c17903cd384a 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -334,7 +334,7 @@ badframe: return 0; } -static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, +int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { /* Handler is *really* a pointer to the function descriptor for @@ -417,87 +417,3 @@ badframe: force_sigsegv(signr, current); return 0; } - - -/* - * OK, we're invoking a handler - */ -static int handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) -{ - int ret; - - /* Set up Signal Frame */ - ret = setup_rt_frame(sig, ka, info, oldset, regs); - - 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,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - - return ret; -} - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -int do_signal(sigset_t *oldset, struct pt_regs *regs) -{ - siginfo_t info; - int signr; - struct k_sigaction ka; - - /* - * If the current thread is 32 bit - invoke the - * 32 bit signal handling code - */ - if (test_thread_flag(TIF_32BIT)) - return do_signal32(oldset, regs); - - 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); - - /* Is there any syscall restart business here ? */ - check_syscall_restart(regs, &ka, signr > 0); - - if (signr > 0) { - int ret; - - /* - * 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); - - /* Whee! Actually deliver the signal. */ - ret = handle_signal(signr, &ka, &info, oldset, regs); - - /* If a signal was successfully delivered, the saved sigmask is in - its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ - if (ret && test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - - return ret; - } - - /* 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; -} -EXPORT_SYMBOL(do_signal); -- cgit v1.2.3 From a3f61dc0a5335334958ec3b97d0b1946b4ae5375 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 4 Jun 2007 17:22:48 +1000 Subject: [POWERPC] Merge creation of signal frame The code for creating signal frames was still duplicated and split in strange ways between 32 and 64 bits, including the SA_ONSTACK handling being in do_signal on 32 bits but inside handle_rt_signal on 64 bits etc... This moves the 64 bits get_sigframe() to the generic signal.c, cleans it a bit, moves the access_ok() call done by all callers to it as well, and adapts/cleanups the 3 different signal handling cases to use that common function. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/signal.c | 39 +++++++++++++++++++++++-------- arch/powerpc/kernel/signal.h | 6 +++-- arch/powerpc/kernel/signal_32.c | 52 +++++++++++++++++++---------------------- arch/powerpc/kernel/signal_64.c | 24 ++----------------- 4 files changed, 59 insertions(+), 62 deletions(-) (limited to 'arch/powerpc/kernel/signal_64.c') diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index a9c148a09361..dee275014e00 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -11,6 +11,7 @@ #include #include +#include #include #include "signal.h" @@ -28,6 +29,32 @@ static inline int is_32bit_task(void) } #endif +/* + * Allocate space for the signal frame + */ +void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) +{ + unsigned long oldsp, newsp; + + /* Default to using normal stack */ + oldsp = regs->gpr[1]; + + /* Check for alt stack */ + if ((ka->sa.sa_flags & SA_ONSTACK) && + current->sas_ss_size && !on_sig_stack(oldsp)) + oldsp = (current->sas_ss_sp + current->sas_ss_size); + + /* Get aligned frame */ + newsp = (oldsp - frame_size) & ~0xFUL; + + /* Check access */ + if (!access_ok(VERIFY_WRITE, (void __user *)newsp, oldsp - newsp)) + return NULL; + + return (void __user *)newsp; +} + /* * Restore the user process's signal mask @@ -130,20 +157,12 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) #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); + regs); else ret = handle_signal32(signr, &ka, &info, oldset, - regs, newsp); + regs); #ifdef CONFIG_PPC64 } else { ret = handle_rt_signal64(signr, &ka, &info, oldset, regs); diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 190d4325f974..c284f75afe77 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -12,15 +12,17 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size); extern void restore_sigmask(sigset_t *set); extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, - struct pt_regs *regs, unsigned long newsp); + struct pt_regs *regs); extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, - struct pt_regs *regs, unsigned long newsp); + struct pt_regs *regs); extern int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 32481e71d71e..590057e9e987 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -282,14 +282,17 @@ long sys_sigaction(int sig, struct old_sigaction __user *act, /* * When we have signals to deliver, we set up on the * user stack, going down from the original stack pointer: - * a sigregs struct + * an ABI gap of 56 words + * an mcontext struct * a sigcontext struct * a gap of __SIGNAL_FRAMESIZE bytes * - * Each of these things must be a multiple of 16 bytes in size. + * Each of these things must be a multiple of 16 bytes in size. The following + * structure represent all of this except the __SIGNAL_FRAMESIZE gap * */ -struct sigregs { +struct sigframe { + struct sigcontext sctx; /* the sigcontext */ struct mcontext mctx; /* all the register values */ /* * Programs using the rs6000/xcoff abi can save up to 19 gp @@ -698,21 +701,16 @@ int compat_sys_sigaltstack(u32 __new, u32 __old, int r5, */ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, - struct pt_regs *regs, unsigned long newsp) + struct pt_regs *regs) { struct rt_sigframe __user *rt_sf; struct mcontext __user *frame; - unsigned long origsp = newsp; + unsigned long newsp = 0; /* Set up Signal Frame */ /* Put a Real Time Context onto stack */ - newsp -= sizeof(*rt_sf); - rt_sf = (struct rt_sigframe __user *)newsp; - - /* create a stack frame for the caller of the handler */ - newsp -= __SIGNAL_FRAMESIZE + 16; - - if (!access_ok(VERIFY_WRITE, (void __user *)newsp, origsp - newsp)) + rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf)); + if (unlikely(rt_sf == NULL)) goto badframe; /* Put the siginfo & fill in most of the ucontext */ @@ -742,8 +740,12 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + /* create a stack frame for the caller of the handler */ + newsp = ((unsigned long)rt_sf) - (__SIGNAL_FRAMESIZE + 16); if (put_user(regs->gpr[1], (u32 __user *)newsp)) goto badframe; + + /* Fill registers for signal handler */ regs->gpr[1] = newsp; regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) &rt_sf->info; @@ -988,26 +990,17 @@ int sys_debug_setcontext(struct ucontext __user *ctx, * OK, we're invoking a handler */ int handle_signal32(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, - unsigned long newsp) + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { struct sigcontext __user *sc; - struct sigregs __user *frame; - unsigned long origsp = newsp; + struct sigframe __user *frame; + unsigned long newsp = 0; /* Set up Signal Frame */ - newsp -= sizeof(struct sigregs); - frame = (struct sigregs __user *) newsp; - - /* Put a sigcontext on the stack */ - newsp -= sizeof(*sc); - sc = (struct sigcontext __user *) newsp; - - /* create a stack frame for the caller of the handler */ - newsp -= __SIGNAL_FRAMESIZE; - - if (!access_ok(VERIFY_WRITE, (void __user *) newsp, origsp - newsp)) + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (unlikely(frame == NULL)) goto badframe; + sc = (struct sigcontext __user *) &frame->sctx; #if _NSIG != 64 #error "Please adjust handle_signal()" @@ -1019,7 +1012,7 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, #else || __put_user(oldset->sig[1], &sc->_unused[3]) #endif - || __put_user(to_user_ptr(frame), &sc->regs) + || __put_user(to_user_ptr(&frame->mctx), &sc->regs) || __put_user(sig, &sc->signal)) goto badframe; @@ -1035,8 +1028,11 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + /* create a stack frame for the caller of the handler */ + newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; if (put_user(regs->gpr[1], (u32 __user *)newsp)) goto badframe; + regs->gpr[1] = newsp; regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) sc; diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index c17903cd384a..5004a979ebc0 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -195,25 +195,6 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, return err; } -/* - * Allocate space for the signal frame - */ -static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - size_t frame_size) -{ - unsigned long newsp; - - /* Default to using normal stack */ - newsp = regs->gpr[1]; - - if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size) { - if (! on_sig_stack(regs->gpr[1])) - newsp = (current->sas_ss_sp + current->sas_ss_size); - } - - return (void __user *)((newsp - frame_size) & -16ul); -} - /* * Setup the trampoline code on the stack */ @@ -348,8 +329,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, long err = 0; frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + if (unlikely(frame == NULL)) goto badframe; err |= __put_user(&frame->info, &frame->pinfo); @@ -386,7 +366,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, funct_desc_ptr = (func_descr_t __user *) ka->sa.sa_handler; /* Allocate a dummy caller frame for the signal handler. */ - newsp = (unsigned long)frame - __SIGNAL_FRAMESIZE; + newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; err |= put_user(regs->gpr[1], (unsigned long __user *)newsp); /* Set up "regs" so we "return" to the signal handler. */ -- cgit v1.2.3