summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/signal_64.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-11-14 20:32:16 -0800
committerDavid S. Miller <davem@davemloft.net>2011-11-14 20:43:07 -0800
commit1d299bc7732c34d85bd43ac1a8745f5a2fed2078 (patch)
tree6a39fe975dd6c18ab0e5efda38ac0a5c04c62863 /arch/sparc/kernel/signal_64.c
parent9ff03b392fa34f6d549fbb56bf05d8a0483aa818 (diff)
downloadlinux-stable-1d299bc7732c34d85bd43ac1a8745f5a2fed2078.tar.gz
linux-stable-1d299bc7732c34d85bd43ac1a8745f5a2fed2078.tar.bz2
linux-stable-1d299bc7732c34d85bd43ac1a8745f5a2fed2078.zip
sparc: Fix handling of orig_i0 wrt. debugging when restarting syscalls.
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, %g2, 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>
Diffstat (limited to 'arch/sparc/kernel/signal_64.c')
-rw-r--r--arch/sparc/kernel/signal_64.c32
1 files changed, 18 insertions, 14 deletions
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index a2b81598d905..1ddf0dedb929 100644
--- a/arch/sparc/kernel/signal_64.c
+++ b/arch/sparc/kernel/signal_64.c
@@ -529,11 +529,17 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
siginfo_t info;
int signr;
+ /* It's a lot of work and synchronization to add a new ptrace
+ * register for GDB to save and restore in order to get
+ * orig_i0 correct for syscall restarts when debugging.
+ *
+ * However, we luckily can use the fact that several registers
+ * are volatile across system calls. One such register is
+ * %g2, so use that as a place to save away orig_i0.
+ */
if (pt_regs_is_syscall(regs) &&
- (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
- restart_syscall = 1;
- } else
- restart_syscall = 0;
+ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
+ regs->u_regs[UREG_G2] = orig_i0;
if (current_thread_info()->status & TS_RESTORE_SIGMASK)
oldset = &current->saved_sigmask;
@@ -542,22 +548,20 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT)) {
- extern void do_signal32(sigset_t *, struct pt_regs *,
- int restart_syscall,
- unsigned long orig_i0);
- do_signal32(oldset, regs, restart_syscall, orig_i0);
+ extern void do_signal32(sigset_t *, struct pt_regs *);
+ do_signal32(oldset, regs);
return;
}
#endif
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- /* If the debugger messes with the program counter, it clears
- * the software "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;
+ if (pt_regs_is_syscall(regs) &&
+ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
+ restart_syscall = 1;
+ orig_i0 = regs->u_regs[UREG_G2];
+ }
if (signr > 0) {
if (restart_syscall)