From: Jeff Dike <jdike@addtoit.com>

From: Bodo Stroesser <bstroesser@fujitsu-siemens.com>

This implements checking for the new ptrace option SYSEMU_SINGLESTEP
(advanced sysemu) and allows the values 0,1,2 for /proc/sysemu,
if advanced sysemu is available:
   0 = don't use sysemu
   1 = use sysemu, but don't use advanced sysemu
   2 = use sysemu and advanced sysemu

Signed-off-by: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/arch/um/include/ptrace_user.h        |    3 ++
 25-akpm/arch/um/include/sysdep-i386/ptrace.h |    3 ++
 25-akpm/arch/um/kernel/process.c             |   38 +++++++++++++++++++++++++--
 25-akpm/arch/um/kernel/process_kern.c        |    6 ++--
 4 files changed, 46 insertions(+), 4 deletions(-)

diff -puN arch/um/include/ptrace_user.h~uml-detect-sysemu_singlestep arch/um/include/ptrace_user.h
--- 25/arch/um/include/ptrace_user.h~uml-detect-sysemu_singlestep	Fri Dec  3 13:50:42 2004
+++ 25-akpm/arch/um/include/ptrace_user.h	Fri Dec  3 13:50:42 2004
@@ -21,6 +21,9 @@ extern void ptrace_pokeuser(unsigned lon
 #ifndef PTRACE_SYSEMU
 #define PTRACE_SYSEMU 31
 #endif
+#ifndef PTRACE_SYSEMU_SINGLESTEP
+#define PTRACE_SYSEMU_SINGLESTEP 32
+#endif
 
 void set_using_sysemu(int value);
 int get_using_sysemu(void);
diff -puN arch/um/include/sysdep-i386/ptrace.h~uml-detect-sysemu_singlestep arch/um/include/sysdep-i386/ptrace.h
--- 25/arch/um/include/sysdep-i386/ptrace.h~uml-detect-sysemu_singlestep	Fri Dec  3 13:50:42 2004
+++ 25-akpm/arch/um/include/sysdep-i386/ptrace.h	Fri Dec  3 13:50:42 2004
@@ -15,6 +15,9 @@
 #ifdef UML_CONFIG_MODE_SKAS
 #include "ptrace-skas.h"
 #endif
+#ifndef PTRACE_SYSEMU_SINGLESTEP
+#define PTRACE_SYSEMU_SINGLESTEP 32
+#endif
 
 #include "choose-mode.h"
 
diff -puN arch/um/kernel/process.c~uml-detect-sysemu_singlestep arch/um/kernel/process.c
--- 25/arch/um/kernel/process.c~uml-detect-sysemu_singlestep	Fri Dec  3 13:50:42 2004
+++ 25-akpm/arch/um/kernel/process.c	Fri Dec  3 13:50:42 2004
@@ -241,7 +241,7 @@ __uml_setup("nosysemu", nosysemu_cmd_par
 static void __init check_sysemu(void)
 {
 	void *stack;
-	int pid, n, status;
+	int pid, syscall, n, status, count=0;
 
 	printk("Checking syscall emulation patch for ptrace...");
 	sysemu_supported = 0;
@@ -269,12 +269,46 @@ static void __init check_sysemu(void)
 	sysemu_supported = 1;
 	printk("OK\n");
 	set_using_sysemu(!force_sysemu_disabled);
+
+	printk("Checking advanced syscall emulation patch for ptrace...");
+	pid = start_ptraced_child(&stack);
+	while(1){
+		count++;
+		if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
+			goto fail;
+		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+		if(n < 0)
+			panic("check_ptrace : wait failed, errno = %d", errno);
+		if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
+			panic("check_ptrace : expected (SIGTRAP|SYSCALL_TRAP), "
+			      "got status = %d", status);
+
+		syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
+				 0);
+		if(syscall == __NR_getpid){
+			if (!count)
+				panic("check_ptrace : SYSEMU_SINGLESTEP doesn't singlestep");
+			n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
+				   os_getpid());
+			if(n < 0)
+				panic("check_sysemu : failed to modify system "
+				      "call return, errno = %d", errno);
+			break;
+		}
+	}
+	if (stop_ptraced_child(pid, stack, 0, 0) < 0)
+		goto fail_stopped;
+
+	sysemu_supported = 2;
+	printk("OK\n");
+
+	if ( !force_sysemu_disabled )
+		set_using_sysemu(sysemu_supported);
 	return;
 
 fail:
 	stop_ptraced_child(pid, stack, 1, 0);
 fail_stopped:
-	sysemu_supported = 0;
 	printk("missing\n");
 }
 
diff -puN arch/um/kernel/process_kern.c~uml-detect-sysemu_singlestep arch/um/kernel/process_kern.c
--- 25/arch/um/kernel/process_kern.c~uml-detect-sysemu_singlestep	Fri Dec  3 13:50:42 2004
+++ 25-akpm/arch/um/kernel/process_kern.c	Fri Dec  3 13:50:42 2004
@@ -404,7 +404,9 @@ int sysemu_supported;
 
 void set_using_sysemu(int value)
 {
-	atomic_set(&using_sysemu, sysemu_supported && value);
+	if (value > sysemu_supported)
+		return;
+	atomic_set(&using_sysemu, value);
 }
 
 int get_using_sysemu(void)
@@ -427,7 +429,7 @@ static int proc_write_sysemu(struct file
 	if (copy_from_user(tmp, buf, 1))
 		return -EFAULT;
 
-	if (tmp[0] == '0' || tmp[0] == '1')
+	if (tmp[0] >= '0' && tmp[0] <= '2')
 		set_using_sysemu(tmp[0] - '0');
 	return count; /*We use the first char, but pretend to write everything*/
 }
_