aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc/kernel/signal32.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-12-26 12:30:13 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2012-01-06 14:14:05 -0800
commit2a89fc8b91abf1ba56daa23b05e6572b30331837 (patch)
tree39bb67a7a1075e7f8032b76c1295880a48382d06 /arch/sparc/kernel/signal32.c
parentd4afed4d20e91a12de7cd1c64bb3451ee2236d19 (diff)
sparc: Fix handling of orig_i0 wrt. debugging when restarting syscalls.
[ A combination of upstream commits 1d299bc7732c34d85bd43ac1a8745f5a2fed2078 and e88d2468718b0789b4c33da2f7e1cef2a1eee279 ] Although we provide a proper way for a debugger to control whether syscall restart occurs, we run into problems because orig_i0 is not saved and restored properly. Luckily we can solve this problem without having to make debuggers aware of the issue. Across system calls, several registers are considered volatile and can be safely clobbered. Therefore we use the pt_regs save area of one of those registers, %g6, as a place to save and restore orig_i0. Debuggers transparently will do the right thing because they save and restore this register already. Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'arch/sparc/kernel/signal32.c')
-rw-r--r--arch/sparc/kernel/signal32.c18
1 files changed, 10 insertions, 8 deletions
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index 5d92488fc16..2e58328c30e 100644
--- a/arch/sparc/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
@@ -829,21 +829,23 @@ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
-void do_signal32(sigset_t *oldset, struct pt_regs * regs,
- int restart_syscall, unsigned long orig_i0)
+void do_signal32(sigset_t *oldset, struct pt_regs * regs)
{
struct k_sigaction ka;
+ unsigned long orig_i0;
+ int restart_syscall;
siginfo_t info;
int signr;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- /* If the debugger messes with the program counter, it clears
- * the "in syscall" bit, directing us to not perform a syscall
- * restart.
- */
- if (restart_syscall && !pt_regs_is_syscall(regs))
- restart_syscall = 0;
+ restart_syscall = 0;
+ orig_i0 = 0;
+ if (pt_regs_is_syscall(regs) &&
+ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
+ restart_syscall = 1;
+ orig_i0 = regs->u_regs[UREG_G6];
+ }
if (signr > 0) {
if (restart_syscall)