From 1d299bc7732c34d85bd43ac1a8745f5a2fed2078 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 14 Nov 2011 20:32:16 -0800 Subject: 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 --- arch/sparc/kernel/signal_32.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'arch/sparc/kernel/signal_32.c') diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c index 8ce247ac04cc..7dfaff64cd6b 100644 --- a/arch/sparc/kernel/signal_32.c +++ b/arch/sparc/kernel/signal_32.c @@ -519,10 +519,16 @@ 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->psr & PSR_C)) - restart_syscall = 1; - else - restart_syscall = 0; + regs->u_regs[UREG_G2] = orig_i0; if (test_thread_flag(TIF_RESTORE_SIGMASK)) oldset = ¤t->saved_sigmask; @@ -535,8 +541,12 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) * 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->psr & PSR_C)) { + restart_syscall = 1; + orig_i0 = regs->u_regs[UREG_G2]; + } + if (signr > 0) { if (restart_syscall) -- cgit v1.2.3 From e88d2468718b0789b4c33da2f7e1cef2a1eee279 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Nov 2011 12:57:00 -0800 Subject: sparc: Stash orig_i0 into %g6 instead of %g2 As per the comments added by this commit, %g2 turns out to not be a usable place to save away orig_i0 for syscall restart handling. In fact all of %g2, %g3, %g4, and %g5 are assumed to be saved across a system call by various bits of code in glibc. %g1 can't be used because that holds the syscall number, which would need to be saved and restored for syscall restart handling too, and that would only compound our problems :-) This leaves us with %g6 and %g7 which are for "system use". %g7 is used as the "thread register" by glibc, but %g6 is used as a compiler and assembler temporary scratch register. And in no instance is %g6 used to hold a value across a system call. Therefore %g6 is safe for storing away orig_i0, at least for now. Signed-off-by: David S. Miller --- arch/sparc/kernel/signal_32.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'arch/sparc/kernel/signal_32.c') diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c index 7dfaff64cd6b..d54c6e53aba0 100644 --- a/arch/sparc/kernel/signal_32.c +++ b/arch/sparc/kernel/signal_32.c @@ -523,12 +523,22 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) * 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. + * Although it should be the case that most of the global + * registers are volatile across a system call, glibc already + * depends upon that fact that we preserve them. So we can't + * just use any global register to save away the orig_i0 value. + * + * In particular %g2, %g3, %g4, and %g5 are all assumed to be + * preserved across a system call trap by various pieces of + * code in glibc. + * + * %g7 is used as the "thread register". %g6 is not used in + * any fixed manner. %g6 is used as a scratch register and + * a compiler temporary, but it's value is never used across + * a system call. Therefore %g6 is usable for orig_i0 storage. */ if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) - regs->u_regs[UREG_G2] = orig_i0; + regs->u_regs[UREG_G6] = orig_i0; if (test_thread_flag(TIF_RESTORE_SIGMASK)) oldset = ¤t->saved_sigmask; @@ -544,7 +554,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) restart_syscall = 0; if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) { restart_syscall = 1; - orig_i0 = regs->u_regs[UREG_G2]; + orig_i0 = regs->u_regs[UREG_G6]; } -- cgit v1.2.3