[ARM] 3262/4: allow ptraced syscalls to be overriden

Patch from Nicolas Pitre

This is needed by strace to properly handle the tracing of some system
calls. It could be useful for other applications as well.

Based on an earlier patch from Daniel Jacobowitz.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Daniel Jacobowitz <dan@debian.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 8826d98..2b92ce8 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -214,11 +214,13 @@
 	 * context switches, and waiting for our parent to respond.
 	 */
 __sys_trace:
+	mov	r2, scno
 	add	r1, sp, #S_OFF
 	mov	r0, #0				@ trace entry [IP = 0]
 	bl	syscall_trace
 
 	adr	lr, __sys_trace_return		@ return address
+	mov	scno, r0			@ syscall number (possibly new)
 	add	r1, sp, #S_R0 + S_OFF		@ pointer to regs
 	cmp	scno, #NR_syscalls		@ check upper syscall limit
 	ldmccia	r1, {r0 - r3}			@ have to reload r0 - r3
@@ -227,6 +229,7 @@
 
 __sys_trace_return:
 	str	r0, [sp, #S_R0 + S_OFF]!	@ save returned r0
+	mov	r2, scno
 	mov	r1, sp
 	mov	r0, #1				@ trace exit [IP = 1]
 	bl	syscall_trace
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index e591f72b..7b6256b 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -766,6 +766,11 @@
 				       (unsigned long __user *) data);
 			break;
 
+		case PTRACE_SET_SYSCALL:
+			ret = 0;
+			child->ptrace_message = data;
+			break;
+
 		default:
 			ret = ptrace_request(child, request, addr, data);
 			break;
@@ -774,14 +779,14 @@
 	return ret;
 }
 
-asmlinkage void syscall_trace(int why, struct pt_regs *regs)
+asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
 {
 	unsigned long ip;
 
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
-		return;
+		return scno;
 	if (!(current->ptrace & PT_PTRACED))
-		return;
+		return scno;
 
 	/*
 	 * Save IP.  IP is used to denote syscall entry/exit:
@@ -790,6 +795,8 @@
 	ip = regs->ARM_ip;
 	regs->ARM_ip = why;
 
+	current->ptrace_message = scno;
+
 	/* the 0x80 provides a way for the tracing parent to distinguish
 	   between a syscall stop and SIGTRAP delivery */
 	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
@@ -804,4 +811,6 @@
 		current->exit_code = 0;
 	}
 	regs->ARM_ip = ip;
+
+	return current->ptrace_message;
 }