summaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/tt
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/kernel/tt')
-rw-r--r--arch/um/kernel/tt/Makefile28
-rw-r--r--arch/um/kernel/tt/exec_kern.c87
-rw-r--r--arch/um/kernel/tt/exec_user.c57
-rw-r--r--arch/um/kernel/tt/gdb.c278
-rw-r--r--arch/um/kernel/tt/gdb_kern.c40
-rw-r--r--arch/um/kernel/tt/include/debug.h29
-rw-r--r--arch/um/kernel/tt/include/mmu-tt.h23
-rw-r--r--arch/um/kernel/tt/include/mode-tt.h35
-rw-r--r--arch/um/kernel/tt/include/mode_kern-tt.h53
-rw-r--r--arch/um/kernel/tt/include/tt.h46
-rw-r--r--arch/um/kernel/tt/include/uaccess-tt.h71
-rw-r--r--arch/um/kernel/tt/ksyms.c28
-rw-r--r--arch/um/kernel/tt/mem.c51
-rw-r--r--arch/um/kernel/tt/mem_user.c49
-rw-r--r--arch/um/kernel/tt/process_kern.c476
-rw-r--r--arch/um/kernel/tt/ptproxy/Makefile10
-rw-r--r--arch/um/kernel/tt/ptproxy/proxy.c377
-rw-r--r--arch/um/kernel/tt/ptproxy/ptproxy.h61
-rw-r--r--arch/um/kernel/tt/ptproxy/ptrace.c237
-rw-r--r--arch/um/kernel/tt/ptproxy/sysdep.c70
-rw-r--r--arch/um/kernel/tt/ptproxy/sysdep.h25
-rw-r--r--arch/um/kernel/tt/ptproxy/wait.c86
-rw-r--r--arch/um/kernel/tt/ptproxy/wait.h15
-rw-r--r--arch/um/kernel/tt/syscall_kern.c47
-rw-r--r--arch/um/kernel/tt/syscall_user.c90
-rw-r--r--arch/um/kernel/tt/time.c28
-rw-r--r--arch/um/kernel/tt/tlb.c149
-rw-r--r--arch/um/kernel/tt/tracer.c480
-rw-r--r--arch/um/kernel/tt/trap_user.c60
-rw-r--r--arch/um/kernel/tt/uaccess.c73
-rw-r--r--arch/um/kernel/tt/uaccess_user.c98
-rw-r--r--arch/um/kernel/tt/unmap.c31
32 files changed, 3288 insertions, 0 deletions
diff --git a/arch/um/kernel/tt/Makefile b/arch/um/kernel/tt/Makefile
new file mode 100644
index 0000000..3d5177d
--- /dev/null
+++ b/arch/um/kernel/tt/Makefile
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
+# Licensed under the GPL
+#
+
+extra-y := unmap_fin.o
+clean-files := unmap_tmp.o
+
+obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \
+ syscall_kern.o syscall_user.o time.o tlb.o tracer.o trap_user.o \
+ uaccess.o uaccess_user.o
+
+obj-$(CONFIG_PT_PROXY) += gdb_kern.o ptproxy/
+
+USER_OBJS := gdb.o time.o tracer.o
+
+include arch/um/scripts/Makefile.rules
+
+UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS))
+UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS))
+
+#XXX: partially copied from arch/um/scripts/Makefile.rules
+$(obj)/unmap.o: c_flags = -Wp,-MD,$(depfile) $(UNMAP_CFLAGS)
+
+$(obj)/unmap_fin.o : $(obj)/unmap.o
+ $(LD) -r -o $(obj)/unmap_tmp.o $< $(shell $(CC) -print-file-name=libc.a)
+ $(OBJCOPY) $(obj)/unmap_tmp.o $@ -G switcheroo
+
diff --git a/arch/um/kernel/tt/exec_kern.c b/arch/um/kernel/tt/exec_kern.c
new file mode 100644
index 0000000..065b504
--- /dev/null
+++ b/arch/um/kernel/tt/exec_kern.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/kernel.h"
+#include "linux/mm.h"
+#include "asm/signal.h"
+#include "asm/ptrace.h"
+#include "asm/uaccess.h"
+#include "asm/pgalloc.h"
+#include "asm/tlbflush.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "irq_user.h"
+#include "time_user.h"
+#include "signal_user.h"
+#include "mem_user.h"
+#include "os.h"
+#include "tlb.h"
+#include "mode.h"
+
+static int exec_tramp(void *sig_stack)
+{
+ init_new_thread_stack(sig_stack, NULL);
+ init_new_thread_signals(1);
+ os_stop_process(os_getpid());
+ return(0);
+}
+
+void flush_thread_tt(void)
+{
+ unsigned long stack;
+ int new_pid;
+
+ stack = alloc_stack(0, 0);
+ if(stack == 0){
+ printk(KERN_ERR
+ "flush_thread : failed to allocate temporary stack\n");
+ do_exit(SIGKILL);
+ }
+
+ new_pid = start_fork_tramp(current->thread_info, stack, 0, exec_tramp);
+ if(new_pid < 0){
+ printk(KERN_ERR
+ "flush_thread : new thread failed, errno = %d\n",
+ -new_pid);
+ do_exit(SIGKILL);
+ }
+
+ if(current_thread->cpu == 0)
+ forward_interrupts(new_pid);
+ current->thread.request.op = OP_EXEC;
+ current->thread.request.u.exec.pid = new_pid;
+ unprotect_stack((unsigned long) current_thread);
+ os_usr1_process(os_getpid());
+ change_sig(SIGUSR1, 1);
+
+ change_sig(SIGUSR1, 0);
+ enable_timer();
+ free_page(stack);
+ protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1);
+ task_protections((unsigned long) current_thread);
+ force_flush_all();
+ unblock_signals();
+}
+
+void start_thread_tt(struct pt_regs *regs, unsigned long eip,
+ unsigned long esp)
+{
+ set_fs(USER_DS);
+ flush_tlb_mm(current->mm);
+ PT_REGS_IP(regs) = eip;
+ PT_REGS_SP(regs) = esp;
+ PT_FIX_EXEC_STACK(esp);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/exec_user.c b/arch/um/kernel/tt/exec_user.c
new file mode 100644
index 0000000..a92c02f
--- /dev/null
+++ b/arch/um/kernel/tt/exec_user.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "user_util.h"
+#include "kern_util.h"
+#include "user.h"
+#include "ptrace_user.h"
+#include "os.h"
+
+void do_exec(int old_pid, int new_pid)
+{
+ unsigned long regs[FRAME_SIZE];
+ int err;
+
+ if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) ||
+ (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0))
+ tracer_panic("do_exec failed to attach proc - errno = %d",
+ errno);
+
+ CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED));
+ if (err < 0)
+ tracer_panic("do_exec failed to attach proc in waitpid - errno = %d",
+ errno);
+
+ if(ptrace_getregs(old_pid, regs) < 0)
+ tracer_panic("do_exec failed to get registers - errno = %d",
+ errno);
+
+ os_kill_ptraced_process(old_pid, 0);
+
+ if (ptrace(PTRACE_OLDSETOPTIONS, new_pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
+ tracer_panic("do_exec: PTRACE_SETOPTIONS failed, errno = %d", errno);
+
+ if(ptrace_setregs(new_pid, regs) < 0)
+ tracer_panic("do_exec failed to start new proc - errno = %d",
+ errno);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/gdb.c b/arch/um/kernel/tt/gdb.c
new file mode 100644
index 0000000..19a0ad7
--- /dev/null
+++ b/arch/um/kernel/tt/gdb.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include "ptrace_user.h"
+#include "uml-config.h"
+#include "kern_constants.h"
+#include "chan_user.h"
+#include "init.h"
+#include "user.h"
+#include "debug.h"
+#include "kern_util.h"
+#include "user_util.h"
+#include "tt.h"
+#include "sysdep/thread.h"
+
+extern int debugger_pid;
+extern int debugger_fd;
+extern int debugger_parent;
+
+int detach(int pid, int sig)
+{
+ return(ptrace(PTRACE_DETACH, pid, 0, sig));
+}
+
+int attach(int pid)
+{
+ int err;
+
+ err = ptrace(PTRACE_ATTACH, pid, 0, 0);
+ if(err < 0) return(-errno);
+ else return(err);
+}
+
+int cont(int pid)
+{
+ return(ptrace(PTRACE_CONT, pid, 0, 0));
+}
+
+#ifdef UML_CONFIG_PT_PROXY
+
+int debugger_signal(int status, pid_t pid)
+{
+ return(debugger_proxy(status, pid));
+}
+
+void child_signal(pid_t pid, int status)
+{
+ child_proxy(pid, status);
+}
+
+static void gdb_announce(char *dev_name, int dev)
+{
+ printf("gdb assigned device '%s'\n", dev_name);
+}
+
+static struct chan_opts opts = {
+ .announce = gdb_announce,
+ .xterm_title = "UML kernel debugger",
+ .raw = 0,
+ .tramp_stack = 0,
+ .in_kernel = 0,
+};
+
+/* Accessed by the tracing thread, which automatically serializes access */
+static void *xterm_data;
+static int xterm_fd;
+
+extern void *xterm_init(char *, int, struct chan_opts *);
+extern int xterm_open(int, int, int, void *, char **);
+extern void xterm_close(int, void *);
+
+int open_gdb_chan(void)
+{
+ char stack[UM_KERN_PAGE_SIZE], *dummy;
+
+ opts.tramp_stack = (unsigned long) stack;
+ xterm_data = xterm_init("", 0, &opts);
+ xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy);
+ return(xterm_fd);
+}
+
+static void exit_debugger_cb(void *unused)
+{
+ if(debugger_pid != -1){
+ if(gdb_pid != -1){
+ fake_child_exit();
+ gdb_pid = -1;
+ }
+ else kill_child_dead(debugger_pid);
+ debugger_pid = -1;
+ if(debugger_parent != -1)
+ detach(debugger_parent, SIGINT);
+ }
+ if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data);
+}
+
+static void exit_debugger(void)
+{
+ initial_thread_cb(exit_debugger_cb, NULL);
+}
+
+__uml_exitcall(exit_debugger);
+
+struct gdb_data {
+ char *str;
+ int err;
+};
+
+static void config_gdb_cb(void *arg)
+{
+ struct gdb_data *data = arg;
+ void *task;
+ int pid;
+
+ data->err = -1;
+ if(debugger_pid != -1) exit_debugger_cb(NULL);
+ if(!strncmp(data->str, "pid,", strlen("pid,"))){
+ data->str += strlen("pid,");
+ pid = strtoul(data->str, NULL, 0);
+ task = cpu_tasks[0].task;
+ debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0);
+ if(debugger_pid != -1){
+ data->err = 0;
+ gdb_pid = pid;
+ }
+ return;
+ }
+ data->err = 0;
+ debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
+ init_proxy(debugger_pid, 0, 0);
+}
+
+int gdb_config(char *str)
+{
+ struct gdb_data data;
+
+ if(*str++ != '=') return(-1);
+ data.str = str;
+ initial_thread_cb(config_gdb_cb, &data);
+ return(data.err);
+}
+
+void remove_gdb_cb(void *unused)
+{
+ exit_debugger_cb(NULL);
+}
+
+int gdb_remove(char *unused)
+{
+ initial_thread_cb(remove_gdb_cb, NULL);
+ return(0);
+}
+
+void signal_usr1(int sig)
+{
+ if(debugger_pid != -1){
+ printf("The debugger is already running\n");
+ return;
+ }
+ debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
+ init_proxy(debugger_pid, 0, 0);
+}
+
+int init_ptrace_proxy(int idle_pid, int startup, int stop)
+{
+ int pid, status;
+
+ pid = start_debugger(linux_prog, startup, stop, &debugger_fd);
+ status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
+ if(pid < 0){
+ cont(idle_pid);
+ return(-1);
+ }
+ init_proxy(pid, 1, status);
+ return(pid);
+}
+
+int attach_debugger(int idle_pid, int pid, int stop)
+{
+ int status = 0, err;
+
+ err = attach(pid);
+ if(err < 0){
+ printf("Failed to attach pid %d, errno = %d\n", pid, -err);
+ return(-1);
+ }
+ if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
+ init_proxy(pid, 1, status);
+ return(pid);
+}
+
+#ifdef notdef /* Put this back in when it does something useful */
+static int __init uml_gdb_init_setup(char *line, int *add)
+{
+ gdb_init = uml_strdup(line);
+ return 0;
+}
+
+__uml_setup("gdb=", uml_gdb_init_setup,
+"gdb=<channel description>\n\n"
+);
+#endif
+
+static int __init uml_gdb_pid_setup(char *line, int *add)
+{
+ gdb_pid = strtoul(line, NULL, 0);
+ *add = 0;
+ return 0;
+}
+
+__uml_setup("gdb-pid=", uml_gdb_pid_setup,
+"gdb-pid=<pid>\n"
+" gdb-pid is used to attach an external debugger to UML. This may be\n"
+" an already-running gdb or a debugger-like process like strace.\n\n"
+);
+
+#else
+
+int debugger_signal(int status, pid_t pid){ return(0); }
+void child_signal(pid_t pid, int status){ }
+int init_ptrace_proxy(int idle_pid, int startup, int stop)
+{
+ printf("debug requested when CONFIG_PT_PROXY is off\n");
+ kill_child_dead(idle_pid);
+ exit(1);
+}
+
+void signal_usr1(int sig)
+{
+ printf("debug requested when CONFIG_PT_PROXY is off\n");
+}
+
+int attach_debugger(int idle_pid, int pid, int stop)
+{
+ printf("attach_debugger called when CONFIG_PT_PROXY "
+ "is off\n");
+ return(-1);
+}
+
+int config_gdb(char *str)
+{
+ return(-1);
+}
+
+int remove_gdb(void)
+{
+ return(-1);
+}
+
+int init_parent_proxy(int pid)
+{
+ return(-1);
+}
+
+void debugger_parent_signal(int status, int pid)
+{
+}
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/gdb_kern.c b/arch/um/kernel/tt/gdb_kern.c
new file mode 100644
index 0000000..93fb121
--- /dev/null
+++ b/arch/um/kernel/tt/gdb_kern.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/init.h"
+#include "linux/config.h"
+#include "mconsole_kern.h"
+
+#ifdef CONFIG_MCONSOLE
+
+extern int gdb_config(char *str);
+extern int gdb_remove(char *unused);
+
+static struct mc_device gdb_mc = {
+ .name = "gdb",
+ .config = gdb_config,
+ .remove = gdb_remove,
+};
+
+int gdb_mc_init(void)
+{
+ mconsole_register_dev(&gdb_mc);
+ return(0);
+}
+
+__initcall(gdb_mc_init);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/include/debug.h b/arch/um/kernel/tt/include/debug.h
new file mode 100644
index 0000000..8eff674
--- /dev/null
+++ b/arch/um/kernel/tt/include/debug.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) and
+ * Lars Brinkhoff.
+ * Licensed under the GPL
+ */
+
+#ifndef __DEBUG_H
+#define __DEBUG_H
+
+extern int debugger_proxy(int status, pid_t pid);
+extern void child_proxy(pid_t pid, int status);
+extern void init_proxy (pid_t pid, int waiting, int status);
+extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd);
+extern void fake_child_exit(void);
+extern int gdb_config(char *str);
+extern int gdb_remove(char *unused);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/include/mmu-tt.h b/arch/um/kernel/tt/include/mmu-tt.h
new file mode 100644
index 0000000..0440510
--- /dev/null
+++ b/arch/um/kernel/tt/include/mmu-tt.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_MMU_H
+#define __TT_MMU_H
+
+struct mmu_context_tt {
+};
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/include/mode-tt.h b/arch/um/kernel/tt/include/mode-tt.h
new file mode 100644
index 0000000..efe4620
--- /dev/null
+++ b/arch/um/kernel/tt/include/mode-tt.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __MODE_TT_H__
+#define __MODE_TT_H__
+
+#include "sysdep/ptrace.h"
+
+enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB };
+
+extern int tracing_pid;
+
+extern int tracer(int (*init_proc)(void *), void *sp);
+extern void user_time_init_tt(void);
+extern void sig_handler_common_tt(int sig, void *sc);
+extern void syscall_handler_tt(int sig, union uml_pt_regs *regs);
+extern void reboot_tt(void);
+extern void halt_tt(void);
+extern int is_tracer_winch(int pid, int fd, void *data);
+extern void kill_off_processes_tt(void);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/include/mode_kern-tt.h b/arch/um/kernel/tt/include/mode_kern-tt.h
new file mode 100644
index 0000000..28aaab3
--- /dev/null
+++ b/arch/um/kernel/tt/include/mode_kern-tt.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_MODE_KERN_H__
+#define __TT_MODE_KERN_H__
+
+#include "linux/sched.h"
+#include "asm/page.h"
+#include "asm/ptrace.h"
+#include "asm/uaccess.h"
+
+extern void *switch_to_tt(void *prev, void *next);
+extern void flush_thread_tt(void);
+extern void start_thread_tt(struct pt_regs *regs, unsigned long eip,
+ unsigned long esp);
+extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
+ unsigned long stack_top, struct task_struct *p,
+ struct pt_regs *regs);
+extern void release_thread_tt(struct task_struct *task);
+extern void exit_thread_tt(void);
+extern void initial_thread_cb_tt(void (*proc)(void *), void *arg);
+extern void init_idle_tt(void);
+extern void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end);
+extern void flush_tlb_kernel_vm_tt(void);
+extern void __flush_tlb_one_tt(unsigned long addr);
+extern void flush_tlb_range_tt(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
+extern void flush_tlb_mm_tt(struct mm_struct *mm);
+extern void force_flush_all_tt(void);
+extern long execute_syscall_tt(void *r);
+extern void before_mem_tt(unsigned long brk_start);
+extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,
+ unsigned long *task_size_out);
+extern int start_uml_tt(void);
+extern int external_pid_tt(struct task_struct *task);
+extern int thread_pid_tt(struct task_struct *task);
+
+#define kmem_end_tt (host_task_size - ABOVE_KMEM)
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/include/tt.h b/arch/um/kernel/tt/include/tt.h
new file mode 100644
index 0000000..c667b67
--- /dev/null
+++ b/arch/um/kernel/tt/include/tt.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_H__
+#define __TT_H__
+
+#include "sysdep/ptrace.h"
+
+extern int gdb_pid;
+extern int debug;
+extern int debug_stop;
+extern int debug_trace;
+
+extern int honeypot;
+
+extern int fork_tramp(void *sig_stack);
+extern int do_proc_op(void *t, int proc_id);
+extern int tracer(int (*init_proc)(void *), void *sp);
+extern void attach_process(int pid);
+extern void tracer_panic(char *format, ...);
+extern void set_init_pid(int pid);
+extern int set_user_mode(void *task);
+extern void set_tracing(void *t, int tracing);
+extern int is_tracing(void *task);
+extern void syscall_handler(int sig, union uml_pt_regs *regs);
+extern void exit_kernel(int pid, void *task);
+extern void do_syscall(void *task, int pid, int local_using_sysemu);
+extern void do_sigtrap(void *task);
+extern int is_valid_pid(int pid);
+extern void remap_data(void *segment_start, void *segment_end, int w);
+extern long execute_syscall_tt(void *r);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/include/uaccess-tt.h b/arch/um/kernel/tt/include/uaccess-tt.h
new file mode 100644
index 0000000..f0bad01
--- /dev/null
+++ b/arch/um/kernel/tt/include/uaccess-tt.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_UACCESS_H
+#define __TT_UACCESS_H
+
+#include "linux/string.h"
+#include "linux/sched.h"
+#include "asm/processor.h"
+#include "asm/errno.h"
+#include "asm/current.h"
+#include "asm/a.out.h"
+#include "uml_uaccess.h"
+
+#define ABOVE_KMEM (16 * 1024 * 1024)
+
+extern unsigned long end_vm;
+extern unsigned long uml_physmem;
+
+#define under_task_size(addr, size) \
+ (((unsigned long) (addr) < TASK_SIZE) && \
+ (((unsigned long) (addr) + (size)) < TASK_SIZE))
+
+#define is_stack(addr, size) \
+ (((unsigned long) (addr) < STACK_TOP) && \
+ ((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \
+ (((unsigned long) (addr) + (size)) <= STACK_TOP))
+
+#define access_ok_tt(type, addr, size) \
+ ((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \
+ (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \
+ (under_task_size(addr, size) || is_stack(addr, size))))
+
+static inline int verify_area_tt(int type, const void * addr,
+ unsigned long size)
+{
+ return(access_ok_tt(type, addr, size) ? 0 : -EFAULT);
+}
+
+extern unsigned long get_fault_addr(void);
+
+extern int __do_copy_from_user(void *to, const void *from, int n,
+ void **fault_addr, void **fault_catcher);
+extern int __do_strncpy_from_user(char *dst, const char *src, size_t n,
+ void **fault_addr, void **fault_catcher);
+extern int __do_clear_user(void *mem, size_t len, void **fault_addr,
+ void **fault_catcher);
+extern int __do_strnlen_user(const char *str, unsigned long n,
+ void **fault_addr, void **fault_catcher);
+
+extern int copy_from_user_tt(void *to, const void *from, int n);
+extern int copy_to_user_tt(void *to, const void *from, int n);
+extern int strncpy_from_user_tt(char *dst, const char *src, int count);
+extern int __clear_user_tt(void *mem, int len);
+extern int clear_user_tt(void *mem, int len);
+extern int strnlen_user_tt(const void *str, int len);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ksyms.c b/arch/um/kernel/tt/ksyms.c
new file mode 100644
index 0000000..92ec85d
--- /dev/null
+++ b/arch/um/kernel/tt/ksyms.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/module.h"
+#include "asm/uaccess.h"
+#include "mode.h"
+
+EXPORT_SYMBOL(__do_copy_from_user);
+EXPORT_SYMBOL(__do_copy_to_user);
+EXPORT_SYMBOL(__do_strncpy_from_user);
+EXPORT_SYMBOL(__do_strnlen_user);
+EXPORT_SYMBOL(__do_clear_user);
+
+EXPORT_SYMBOL(tracing_pid);
+EXPORT_SYMBOL(honeypot);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/mem.c b/arch/um/kernel/tt/mem.c
new file mode 100644
index 0000000..74346a0
--- /dev/null
+++ b/arch/um/kernel/tt/mem.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/config.h"
+#include "linux/mm.h"
+#include "asm/uaccess.h"
+#include "mem_user.h"
+#include "kern_util.h"
+#include "user_util.h"
+#include "kern.h"
+#include "tt.h"
+
+void before_mem_tt(unsigned long brk_start)
+{
+ if(debug)
+ remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1);
+ remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1);
+ remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1);
+}
+
+#ifdef CONFIG_HOST_2G_2G
+#define TOP 0x80000000
+#else
+#define TOP 0xc0000000
+#endif
+
+#define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000)
+#define START (TOP - SIZE)
+
+unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,
+ unsigned long *task_size_out)
+{
+ /* Round up to the nearest 4M */
+ *host_size_out = ROUND_4M((unsigned long) &arg);
+ *task_size_out = START;
+ return(START);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/mem_user.c b/arch/um/kernel/tt/mem_user.c
new file mode 100644
index 0000000..3085267
--- /dev/null
+++ b/arch/um/kernel/tt/mem_user.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include "tt.h"
+#include "mem_user.h"
+#include "user_util.h"
+
+void remap_data(void *segment_start, void *segment_end, int w)
+{
+ void *addr;
+ unsigned long size;
+ int data, prot;
+
+ if(w) prot = PROT_WRITE;
+ else prot = 0;
+ prot |= PROT_READ | PROT_EXEC;
+ size = (unsigned long) segment_end -
+ (unsigned long) segment_start;
+ data = create_mem_file(size);
+ addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0);
+ if(addr == MAP_FAILED){
+ perror("mapping new data segment");
+ exit(1);
+ }
+ memcpy(addr, segment_start, size);
+ if(switcheroo(data, prot, addr, segment_start, size) < 0){
+ printf("switcheroo failed\n");
+ exit(1);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c
new file mode 100644
index 0000000..f19f7c1
--- /dev/null
+++ b/arch/um/kernel/tt/process_kern.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/sched.h"
+#include "linux/signal.h"
+#include "linux/kernel.h"
+#include "linux/interrupt.h"
+#include "linux/ptrace.h"
+#include "asm/system.h"
+#include "asm/pgalloc.h"
+#include "asm/ptrace.h"
+#include "asm/tlbflush.h"
+#include "irq_user.h"
+#include "signal_user.h"
+#include "kern_util.h"
+#include "user_util.h"
+#include "os.h"
+#include "kern.h"
+#include "sigcontext.h"
+#include "time_user.h"
+#include "mem_user.h"
+#include "tlb.h"
+#include "mode.h"
+#include "init.h"
+#include "tt.h"
+
+void *switch_to_tt(void *prev, void *next, void *last)
+{
+ struct task_struct *from, *to, *prev_sched;
+ unsigned long flags;
+ int err, vtalrm, alrm, prof, cpu;
+ char c;
+ /* jailing and SMP are incompatible, so this doesn't need to be
+ * made per-cpu
+ */
+ static int reading;
+
+ from = prev;
+ to = next;
+
+ to->thread.prev_sched = from;
+
+ cpu = from->thread_info->cpu;
+ if(cpu == 0)
+ forward_interrupts(to->thread.mode.tt.extern_pid);
+#ifdef CONFIG_SMP
+ forward_ipi(cpu_data[cpu].ipi_pipe[0], to->thread.mode.tt.extern_pid);
+#endif
+ local_irq_save(flags);
+
+ vtalrm = change_sig(SIGVTALRM, 0);
+ alrm = change_sig(SIGALRM, 0);
+ prof = change_sig(SIGPROF, 0);
+
+ forward_pending_sigio(to->thread.mode.tt.extern_pid);
+
+ c = 0;
+ set_current(to);
+
+ reading = 0;
+ err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c));
+ if(err != sizeof(c))
+ panic("write of switch_pipe failed, err = %d", -err);
+
+ reading = 1;
+ if((from->exit_state == EXIT_ZOMBIE) ||
+ (from->exit_state == EXIT_DEAD))
+ os_kill_process(os_getpid(), 0);
+
+ err = os_read_file(from->thread.mode.tt.switch_pipe[0], &c, sizeof(c));
+ if(err != sizeof(c))
+ panic("read of switch_pipe failed, errno = %d", -err);
+
+ /* If the process that we have just scheduled away from has exited,
+ * then it needs to be killed here. The reason is that, even though
+ * it will kill itself when it next runs, that may be too late. Its
+ * stack will be freed, possibly before then, and if that happens,
+ * we have a use-after-free situation. So, it gets killed here
+ * in case it has not already killed itself.
+ */
+ prev_sched = current->thread.prev_sched;
+ if((prev_sched->exit_state == EXIT_ZOMBIE) ||
+ (prev_sched->exit_state == EXIT_DEAD))
+ os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1);
+
+ change_sig(SIGVTALRM, vtalrm);
+ change_sig(SIGALRM, alrm);
+ change_sig(SIGPROF, prof);
+
+ arch_switch();
+
+ flush_tlb_all();
+ local_irq_restore(flags);
+
+ return(current->thread.prev_sched);
+}
+
+void release_thread_tt(struct task_struct *task)
+{
+ int pid = task->thread.mode.tt.extern_pid;
+
+ if(os_getpid() != pid)
+ os_kill_process(pid, 0);
+}
+
+void exit_thread_tt(void)
+{
+ os_close_file(current->thread.mode.tt.switch_pipe[0]);
+ os_close_file(current->thread.mode.tt.switch_pipe[1]);
+}
+
+void suspend_new_thread(int fd)
+{
+ int err;
+ char c;
+
+ os_stop_process(os_getpid());
+ err = os_read_file(fd, &c, sizeof(c));
+ if(err != sizeof(c))
+ panic("read failed in suspend_new_thread, err = %d", -err);
+}
+
+void schedule_tail(task_t *prev);
+
+static void new_thread_handler(int sig)
+{
+ unsigned long disable;
+ int (*fn)(void *);
+ void *arg;
+
+ fn = current->thread.request.u.thread.proc;
+ arg = current->thread.request.u.thread.arg;
+
+ UPT_SC(&current->thread.regs.regs) = (void *) (&sig + 1);
+ disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) |
+ (1 << (SIGIO - 1)) | (1 << (SIGPROF - 1));
+ SC_SIGMASK(UPT_SC(&current->thread.regs.regs)) &= ~disable;
+
+ suspend_new_thread(current->thread.mode.tt.switch_pipe[0]);
+
+ force_flush_all();
+ if(current->thread.prev_sched != NULL)
+ schedule_tail(current->thread.prev_sched);
+ current->thread.prev_sched = NULL;
+
+ init_new_thread_signals(1);
+ enable_timer();
+ free_page(current->thread.temp_stack);
+ set_cmdline("(kernel thread)");
+
+ change_sig(SIGUSR1, 1);
+ change_sig(SIGVTALRM, 1);
+ change_sig(SIGPROF, 1);
+ local_irq_enable();
+ if(!run_kernel_thread(fn, arg, &current->thread.exec_buf))
+ do_exit(0);
+
+ /* XXX No set_user_mode here because a newly execed process will
+ * immediately segfault on its non-existent IP, coming straight back
+ * to the signal handler, which will call set_user_mode on its way
+ * out. This should probably change since it's confusing.
+ */
+}
+
+static int new_thread_proc(void *stack)
+{
+ /* local_irq_disable is needed to block out signals until this thread is
+ * properly scheduled. Otherwise, the tracing thread will get mighty
+ * upset about any signals that arrive before that.
+ * This has the complication that it sets the saved signal mask in
+ * the sigcontext to block signals. This gets restored when this
+ * thread (or a descendant, since they get a copy of this sigcontext)
+ * returns to userspace.
+ * So, this is compensated for elsewhere.
+ * XXX There is still a small window until local_irq_disable() actually
+ * finishes where signals are possible - shouldn't be a problem in
+ * practice since SIGIO hasn't been forwarded here yet, and the
+ * local_irq_disable should finish before a SIGVTALRM has time to be
+ * delivered.
+ */
+
+ local_irq_disable();
+ init_new_thread_stack(stack, new_thread_handler);
+ os_usr1_process(os_getpid());
+ change_sig(SIGUSR1, 1);
+ return(0);
+}
+
+/* Signal masking - signals are blocked at the start of fork_tramp. They
+ * are re-enabled when finish_fork_handler is entered by fork_tramp hitting
+ * itself with a SIGUSR1. set_user_mode has to be run with SIGUSR1 off,
+ * so it is blocked before it's called. They are re-enabled on sigreturn
+ * despite the fact that they were blocked when the SIGUSR1 was issued because
+ * copy_thread copies the parent's sigcontext, including the signal mask
+ * onto the signal frame.
+ */
+
+void finish_fork_handler(int sig)
+{
+ UPT_SC(&current->thread.regs.regs) = (void *) (&sig + 1);
+ suspend_new_thread(current->thread.mode.tt.switch_pipe[0]);
+
+ force_flush_all();
+ if(current->thread.prev_sched != NULL)
+ schedule_tail(current->thread.prev_sched);
+ current->thread.prev_sched = NULL;
+
+ enable_timer();
+ change_sig(SIGVTALRM, 1);
+ local_irq_enable();
+ if(current->mm != current->parent->mm)
+ protect_memory(uml_reserved, high_physmem - uml_reserved, 1,
+ 1, 0, 1);
+ task_protections((unsigned long) current_thread);
+
+ free_page(current->thread.temp_stack);
+ local_irq_disable();
+ change_sig(SIGUSR1, 0);
+ set_user_mode(current);
+}
+
+int fork_tramp(void *stack)
+{
+ local_irq_disable();
+ arch_init_thread();
+ init_new_thread_stack(stack, finish_fork_handler);
+
+ os_usr1_process(os_getpid());
+ change_sig(SIGUSR1, 1);
+ return(0);
+}
+
+int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
+ unsigned long stack_top, struct task_struct * p,
+ struct pt_regs *regs)
+{
+ int (*tramp)(void *);
+ int new_pid, err;
+ unsigned long stack;
+
+ if(current->thread.forking)
+ tramp = fork_tramp;
+ else {
+ tramp = new_thread_proc;
+ p->thread.request.u.thread = current->thread.request.u.thread;
+ }
+
+ err = os_pipe(p->thread.mode.tt.switch_pipe, 1, 1);
+ if(err < 0){
+ printk("copy_thread : pipe failed, err = %d\n", -err);
+ return(err);
+ }
+
+ stack = alloc_stack(0, 0);
+ if(stack == 0){
+ printk(KERN_ERR "copy_thread : failed to allocate "
+ "temporary stack\n");
+ return(-ENOMEM);
+ }
+
+ clone_flags &= CLONE_VM;
+ p->thread.temp_stack = stack;
+ new_pid = start_fork_tramp(p->thread_info, stack, clone_flags, tramp);
+ if(new_pid < 0){
+ printk(KERN_ERR "copy_thread : clone failed - errno = %d\n",
+ -new_pid);
+ return(new_pid);
+ }
+
+ if(current->thread.forking){
+ sc_to_sc(UPT_SC(&p->thread.regs.regs),
+ UPT_SC(&current->thread.regs.regs));
+ SC_SET_SYSCALL_RETURN(UPT_SC(&p->thread.regs.regs), 0);
+ if(sp != 0) SC_SP(UPT_SC(&p->thread.regs.regs)) = sp;
+ }
+ p->thread.mode.tt.extern_pid = new_pid;
+
+ current->thread.request.op = OP_FORK;
+ current->thread.request.u.fork.pid = new_pid;
+ os_usr1_process(os_getpid());
+
+ /* Enable the signal and then disable it to ensure that it is handled
+ * here, and nowhere else.
+ */
+ change_sig(SIGUSR1, 1);
+
+ change_sig(SIGUSR1, 0);
+ err = 0;
+ return(err);
+}
+
+void reboot_tt(void)
+{
+ current->thread.request.op = OP_REBOOT;
+ os_usr1_process(os_getpid());
+ change_sig(SIGUSR1, 1);
+}
+
+void halt_tt(void)
+{
+ current->thread.request.op = OP_HALT;
+ os_usr1_process(os_getpid());
+ change_sig(SIGUSR1, 1);
+}
+
+void kill_off_processes_tt(void)
+{
+ struct task_struct *p;
+ int me;
+
+ me = os_getpid();
+ for_each_process(p){
+ if(p->thread.mode.tt.extern_pid != me)
+ os_kill_process(p->thread.mode.tt.extern_pid, 0);
+ }
+ if(init_task.thread.mode.tt.extern_pid != me)
+ os_kill_process(init_task.thread.mode.tt.extern_pid, 0);
+}
+
+void initial_thread_cb_tt(void (*proc)(void *), void *arg)
+{
+ if(os_getpid() == tracing_pid){
+ (*proc)(arg);
+ }
+ else {
+ current->thread.request.op = OP_CB;
+ current->thread.request.u.cb.proc = proc;
+ current->thread.request.u.cb.arg = arg;
+ os_usr1_process(os_getpid());
+ change_sig(SIGUSR1, 1);
+
+ change_sig(SIGUSR1, 0);
+ }
+}
+
+int do_proc_op(void *t, int proc_id)
+{
+ struct task_struct *task;
+ struct thread_struct *thread;
+ int op, pid;
+
+ task = t;
+ thread = &task->thread;
+ op = thread->request.op;
+ switch(op){
+ case OP_NONE:
+ case OP_TRACE_ON:
+ break;
+ case OP_EXEC:
+ pid = thread->request.u.exec.pid;
+ do_exec(thread->mode.tt.extern_pid, pid);
+ thread->mode.tt.extern_pid = pid;
+ cpu_tasks[task->thread_info->cpu].pid = pid;
+ break;
+ case OP_FORK:
+ attach_process(thread->request.u.fork.pid);
+ break;
+ case OP_CB:
+ (*thread->request.u.cb.proc)(thread->request.u.cb.arg);
+ break;
+ case OP_REBOOT:
+ case OP_HALT:
+ break;
+ default:
+ tracer_panic("Bad op in do_proc_op");
+ break;
+ }
+ thread->request.op = OP_NONE;
+ return(op);
+}
+
+void init_idle_tt(void)
+{
+ default_idle();
+}
+
+extern void start_kernel(void);
+
+static int start_kernel_proc(void *unused)
+{
+ int pid;
+
+ block_signals();
+ pid = os_getpid();
+
+ cpu_tasks[0].pid = pid;
+ cpu_tasks[0].task = current;
+#ifdef CONFIG_SMP
+ cpu_online_map = cpumask_of_cpu(0);
+#endif
+ if(debug) os_stop_process(pid);
+ start_kernel();
+ return(0);
+}
+
+void set_tracing(void *task, int tracing)
+{
+ ((struct task_struct *) task)->thread.mode.tt.tracing = tracing;
+}
+
+int is_tracing(void *t)
+{
+ return (((struct task_struct *) t)->thread.mode.tt.tracing);
+}
+
+int set_user_mode(void *t)
+{
+ struct task_struct *task;
+
+ task = t ? t : current;
+ if(task->thread.mode.tt.tracing)
+ return(1);
+ task->thread.request.op = OP_TRACE_ON;
+ os_usr1_process(os_getpid());
+ return(0);
+}
+
+void set_init_pid(int pid)
+{
+ int err;
+
+ init_task.thread.mode.tt.extern_pid = pid;
+ err = os_pipe(init_task.thread.mode.tt.switch_pipe, 1, 1);
+ if(err)
+ panic("Can't create switch pipe for init_task, errno = %d",
+ -err);
+}
+
+int start_uml_tt(void)
+{
+ void *sp;
+ int pages;
+
+ pages = (1 << CONFIG_KERNEL_STACK_ORDER);
+ sp = (void *) ((unsigned long) init_task.thread_info) +
+ pages * PAGE_SIZE - sizeof(unsigned long);
+ return(tracer(start_kernel_proc, sp));
+}
+
+int external_pid_tt(struct task_struct *task)
+{
+ return(task->thread.mode.tt.extern_pid);
+}
+
+int thread_pid_tt(struct task_struct *task)
+{
+ return(task->thread.mode.tt.extern_pid);
+}
+
+int is_valid_pid(int pid)
+{
+ struct task_struct *task;
+
+ read_lock(&tasklist_lock);
+ for_each_process(task){
+ if(task->thread.mode.tt.extern_pid == pid){
+ read_unlock(&tasklist_lock);
+ return(1);
+ }
+ }
+ read_unlock(&tasklist_lock);
+ return(0);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/Makefile b/arch/um/kernel/tt/ptproxy/Makefile
new file mode 100644
index 0000000..3ad5b77
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+obj-y = proxy.o ptrace.o sysdep.o wait.o
+
+USER_OBJS := $(obj-y)
+
+include arch/um/scripts/Makefile.rules
diff --git a/arch/um/kernel/tt/ptproxy/proxy.c b/arch/um/kernel/tt/ptproxy/proxy.c
new file mode 100644
index 0000000..58800c5
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/proxy.c
@@ -0,0 +1,377 @@
+/**********************************************************************
+proxy.c
+
+Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
+terms and conditions.
+
+Jeff Dike (jdike@karaya.com) : Modified for integration into uml
+**********************************************************************/
+
+/* XXX This file shouldn't refer to CONFIG_* */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <asm/unistd.h>
+#include "ptrace_user.h"
+
+#include "ptproxy.h"
+#include "sysdep.h"
+#include "wait.h"
+
+#include "user_util.h"
+#include "user.h"
+#include "os.h"
+#include "tempfile.h"
+
+static int debugger_wait(debugger_state *debugger, int *status, int options,
+ int (*syscall)(debugger_state *debugger, pid_t child),
+ int (*normal_return)(debugger_state *debugger,
+ pid_t unused),
+ int (*wait_return)(debugger_state *debugger,
+ pid_t unused))
+{
+ if(debugger->real_wait){
+ debugger->handle_trace = normal_return;
+ syscall_continue(debugger->pid);
+ debugger->real_wait = 0;
+ return(1);
+ }
+ debugger->wait_status_ptr = status;
+ debugger->wait_options = options;
+ if((debugger->debugee != NULL) && debugger->debugee->event){
+ syscall_continue(debugger->pid);
+ wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL,
+ NULL);
+ (*wait_return)(debugger, -1);
+ return(0);
+ }
+ else if(debugger->wait_options & WNOHANG){
+ syscall_cancel(debugger->pid, 0);
+ debugger->handle_trace = syscall;
+ return(0);
+ }
+ else {
+ syscall_pause(debugger->pid);
+ debugger->handle_trace = wait_return;
+ debugger->waiting = 1;
+ }
+ return(1);
+}
+
+/*
+ * Handle debugger trap, i.e. syscall.
+ */
+
+int debugger_syscall(debugger_state *debugger, pid_t child)
+{
+ long arg1, arg2, arg3, arg4, arg5, result;
+ int syscall, ret = 0;
+
+ syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4,
+ &arg5);
+
+ switch(syscall){
+ case __NR_execve:
+ /* execve never returns */
+ debugger->handle_trace = debugger_syscall;
+ break;
+
+ case __NR_ptrace:
+ if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid;
+ if(!debugger->debugee->in_context)
+ child = debugger->debugee->pid;
+ result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child,
+ &ret);
+ syscall_cancel(debugger->pid, result);
+ debugger->handle_trace = debugger_syscall;
+ return(ret);
+
+#ifdef __NR_waitpid
+ case __NR_waitpid:
+#endif
+ case __NR_wait4:
+ if(!debugger_wait(debugger, (int *) arg2, arg3,
+ debugger_syscall, debugger_normal_return,
+ proxy_wait_return))
+ return(0);
+ break;
+
+ case __NR_kill:
+ if(!debugger->debugee->in_context)
+ child = debugger->debugee->pid;
+ if(arg1 == debugger->debugee->pid){
+ result = kill(child, arg2);
+ syscall_cancel(debugger->pid, result);
+ debugger->handle_trace = debugger_syscall;
+ return(0);
+ }
+ else debugger->handle_trace = debugger_normal_return;
+ break;
+
+ default:
+ debugger->handle_trace = debugger_normal_return;
+ }
+
+ syscall_continue(debugger->pid);
+ return(0);
+}
+
+/* Used by the tracing thread */
+static debugger_state parent;
+static int parent_syscall(debugger_state *debugger, int pid);
+
+int init_parent_proxy(int pid)
+{
+ parent = ((debugger_state) { .pid = pid,
+ .wait_options = 0,
+ .wait_status_ptr = NULL,
+ .waiting = 0,
+ .real_wait = 0,
+ .expecting_child = 0,
+ .handle_trace = parent_syscall,
+ .debugee = NULL } );
+ return(0);
+}
+
+int parent_normal_return(debugger_state *debugger, pid_t unused)
+{
+ debugger->handle_trace = parent_syscall;
+ syscall_continue(debugger->pid);
+ return(0);
+}
+
+static int parent_syscall(debugger_state *debugger, int pid)
+{
+ long arg1, arg2, arg3, arg4, arg5;
+ int syscall;
+
+ syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5);
+
+ if((syscall == __NR_wait4)
+#ifdef __NR_waitpid
+ || (syscall == __NR_waitpid)
+#endif
+ ){
+ debugger_wait(&parent, (int *) arg2, arg3, parent_syscall,
+ parent_normal_return, parent_wait_return);
+ }
+ else ptrace(PTRACE_SYSCALL, pid, 0, 0);
+ return(0);
+}
+
+int debugger_normal_return(debugger_state *debugger, pid_t unused)
+{
+ debugger->handle_trace = debugger_syscall;
+ syscall_continue(debugger->pid);
+ return(0);
+}
+
+void debugger_cancelled_return(debugger_state *debugger, int result)
+{
+ debugger->handle_trace = debugger_syscall;
+ syscall_set_result(debugger->pid, result);
+ syscall_continue(debugger->pid);
+}
+
+/* Used by the tracing thread */
+static debugger_state debugger;
+static debugee_state debugee;
+
+void init_proxy (pid_t debugger_pid, int stopped, int status)
+{
+ debugger.pid = debugger_pid;
+ debugger.handle_trace = debugger_syscall;
+ debugger.debugee = &debugee;
+ debugger.waiting = 0;
+ debugger.real_wait = 0;
+ debugger.expecting_child = 0;
+
+ debugee.pid = 0;
+ debugee.traced = 0;
+ debugee.stopped = stopped;
+ debugee.event = 0;
+ debugee.zombie = 0;
+ debugee.died = 0;
+ debugee.wait_status = status;
+ debugee.in_context = 1;
+}
+
+int debugger_proxy(int status, int pid)
+{
+ int ret = 0, sig;
+
+ if(WIFSTOPPED(status)){
+ sig = WSTOPSIG(status);
+ if (sig == SIGTRAP)
+ ret = (*debugger.handle_trace)(&debugger, pid);
+
+ else if(sig == SIGCHLD){
+ if(debugger.expecting_child){
+ ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
+ debugger.expecting_child = 0;
+ }
+ else if(debugger.waiting)
+ real_wait_return(&debugger);
+ else {
+ ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
+ debugger.real_wait = 1;
+ }
+ }
+ else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
+ }
+ else if(WIFEXITED(status)){
+ tracer_panic("debugger (pid %d) exited with status %d",
+ debugger.pid, WEXITSTATUS(status));
+ }
+ else if(WIFSIGNALED(status)){
+ tracer_panic("debugger (pid %d) exited with signal %d",
+ debugger.pid, WTERMSIG(status));
+ }
+ else {
+ tracer_panic("proxy got unknown status (0x%x) on debugger "
+ "(pid %d)", status, debugger.pid);
+ }
+ return(ret);
+}
+
+void child_proxy(pid_t pid, int status)
+{
+ debugee.event = 1;
+ debugee.wait_status = status;
+
+ if(WIFSTOPPED(status)){
+ debugee.stopped = 1;
+ debugger.expecting_child = 1;
+ kill(debugger.pid, SIGCHLD);
+ }
+ else if(WIFEXITED(status) || WIFSIGNALED(status)){
+ debugee.zombie = 1;
+ debugger.expecting_child = 1;
+ kill(debugger.pid, SIGCHLD);
+ }
+ else panic("proxy got unknown status (0x%x) on child (pid %d)",
+ status, pid);
+}
+
+void debugger_parent_signal(int status, int pid)
+{
+ int sig;
+
+ if(WIFSTOPPED(status)){
+ sig = WSTOPSIG(status);
+ if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid);
+ else ptrace(PTRACE_SYSCALL, pid, 0, sig);
+ }
+}
+
+void fake_child_exit(void)
+{
+ int status, pid;
+
+ child_proxy(1, W_EXITCODE(0, 0));
+ while(debugger.waiting == 1){
+ CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
+ if(pid != debugger.pid){
+ printk("fake_child_exit - waitpid failed, "
+ "errno = %d\n", errno);
+ return;
+ }
+ debugger_proxy(status, debugger.pid);
+ }
+ CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
+ if(pid != debugger.pid){
+ printk("fake_child_exit - waitpid failed, "
+ "errno = %d\n", errno);
+ return;
+ }
+ if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0)
+ printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n",
+ errno);
+}
+
+char gdb_init_string[] =
+"att 1 \n\
+b panic \n\
+b stop \n\
+handle SIGWINCH nostop noprint pass \n\
+";
+
+int start_debugger(char *prog, int startup, int stop, int *fd_out)
+{
+ int slave, child;
+
+ slave = open_gdb_chan();
+ child = fork();
+ if(child == 0){
+ char *tempname = NULL;
+ int fd;
+
+ if(setsid() < 0) perror("setsid");
+ if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) ||
+ (dup2(slave, 2) < 0)){
+ printk("start_debugger : dup2 failed, errno = %d\n",
+ errno);
+ exit(1);
+ }
+ if(ioctl(0, TIOCSCTTY, 0) < 0){
+ printk("start_debugger : TIOCSCTTY failed, "
+ "errno = %d\n", errno);
+ exit(1);
+ }
+ if(tcsetpgrp (1, os_getpid()) < 0){
+ printk("start_debugger : tcsetpgrp failed, "
+ "errno = %d\n", errno);
+#ifdef notdef
+ exit(1);
+#endif
+ }
+ fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0);
+ if(fd < 0){
+ printk("start_debugger : make_tempfile failed,"
+ "err = %d\n", -fd);
+ exit(1);
+ }
+ os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1);
+ if(startup){
+ if(stop){
+ os_write_file(fd, "b start_kernel\n",
+ strlen("b start_kernel\n"));
+ }
+ os_write_file(fd, "c\n", strlen("c\n"));
+ }
+ if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
+ printk("start_debugger : PTRACE_TRACEME failed, "
+ "errno = %d\n", errno);
+ exit(1);
+ }
+ execlp("gdb", "gdb", "--command", tempname, prog, NULL);
+ printk("start_debugger : exec of gdb failed, errno = %d\n",
+ errno);
+ }
+ if(child < 0){
+ printk("start_debugger : fork for gdb failed, errno = %d\n",
+ errno);
+ return(-1);
+ }
+ *fd_out = slave;
+ return(child);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/ptproxy.h b/arch/um/kernel/tt/ptproxy/ptproxy.h
new file mode 100644
index 0000000..5eb0285
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/ptproxy.h
@@ -0,0 +1,61 @@
+/**********************************************************************
+ptproxy.h
+
+Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
+terms and conditions.
+**********************************************************************/
+
+#ifndef __PTPROXY_H
+#define __PTPROXY_H
+
+#include <sys/types.h>
+
+typedef struct debugger debugger_state;
+typedef struct debugee debugee_state;
+
+struct debugger
+{
+ pid_t pid;
+ int wait_options;
+ int *wait_status_ptr;
+ unsigned int waiting : 1;
+ unsigned int real_wait : 1;
+ unsigned int expecting_child : 1;
+ int (*handle_trace) (debugger_state *, pid_t);
+
+ debugee_state *debugee;
+};
+
+struct debugee
+{
+ pid_t pid;
+ int wait_status;
+ unsigned int died : 1;
+ unsigned int event : 1;
+ unsigned int stopped : 1;
+ unsigned int trace_singlestep : 1;
+ unsigned int trace_syscall : 1;
+ unsigned int traced : 1;
+ unsigned int zombie : 1;
+ unsigned int in_context : 1;
+};
+
+extern int debugger_syscall(debugger_state *debugger, pid_t pid);
+extern int debugger_normal_return (debugger_state *debugger, pid_t unused);
+
+extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t,
+ int *strace_out);
+extern void debugger_cancelled_return(debugger_state *debugger, int result);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/ptrace.c b/arch/um/kernel/tt/ptproxy/ptrace.c
new file mode 100644
index 0000000..528a5fc
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/ptrace.c
@@ -0,0 +1,237 @@
+/**********************************************************************
+ptrace.c
+
+Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
+terms and conditions.
+
+Jeff Dike (jdike@karaya.com) : Modified for integration into uml
+**********************************************************************/
+
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include "ptproxy.h"
+#include "debug.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "ptrace_user.h"
+#include "tt.h"
+
+long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2,
+ long arg3, long arg4, pid_t child, int *ret)
+{
+ sigset_t relay;
+ long result;
+ int status;
+
+ *ret = 0;
+ if(debugger->debugee->died) return(-ESRCH);
+
+ switch(arg1){
+ case PTRACE_ATTACH:
+ if(debugger->debugee->traced) return(-EPERM);
+
+ debugger->debugee->pid = arg2;
+ debugger->debugee->traced = 1;
+
+ if(is_valid_pid(arg2) && (arg2 != child)){
+ debugger->debugee->in_context = 0;
+ kill(arg2, SIGSTOP);
+ debugger->debugee->event = 1;
+ debugger->debugee->wait_status = W_STOPCODE(SIGSTOP);
+ }
+ else {
+ debugger->debugee->in_context = 1;
+ if(debugger->debugee->stopped)
+ child_proxy(child, W_STOPCODE(SIGSTOP));
+ else kill(child, SIGSTOP);
+ }
+
+ return(0);
+
+ case PTRACE_DETACH:
+ if(!debugger->debugee->traced) return(-EPERM);
+
+ debugger->debugee->traced = 0;
+ debugger->debugee->pid = 0;
+ if(!debugger->debugee->in_context)
+ kill(child, SIGCONT);
+
+ return(0);
+
+ case PTRACE_CONT:
+ if(!debugger->debugee->in_context) return(-EPERM);
+ *ret = PTRACE_CONT;
+ return(ptrace(PTRACE_CONT, child, arg3, arg4));
+
+#ifdef UM_HAVE_GETFPREGS
+ case PTRACE_GETFPREGS:
+ {
+ long regs[FP_FRAME_SIZE];
+ int i, result;
+
+ result = ptrace(PTRACE_GETFPREGS, child, 0, regs);
+ if(result == -1) return(-errno);
+
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
+ ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
+ regs[i]);
+ return(result);
+ }
+#endif
+
+#ifdef UM_HAVE_GETFPXREGS
+ case PTRACE_GETFPXREGS:
+ {
+ long regs[FPX_FRAME_SIZE];
+ int i, result;
+
+ result = ptrace(PTRACE_GETFPXREGS, child, 0, regs);
+ if(result == -1) return(-errno);
+
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
+ ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
+ regs[i]);
+ return(result);
+ }
+#endif
+
+#ifdef UM_HAVE_GETREGS
+ case PTRACE_GETREGS:
+ {
+ long regs[FRAME_SIZE];
+ int i, result;
+
+ result = ptrace(PTRACE_GETREGS, child, 0, regs);
+ if(result == -1) return(-errno);
+
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
+ ptrace (PTRACE_POKEDATA, debugger->pid,
+ arg4 + 4 * i, regs[i]);
+ return(result);
+ }
+ break;
+#endif
+
+ case PTRACE_KILL:
+ result = ptrace(PTRACE_KILL, child, arg3, arg4);
+ if(result == -1) return(-errno);
+
+ return(result);
+
+ case PTRACE_PEEKDATA:
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKUSR:
+ /* The value being read out could be -1, so we have to
+ * check errno to see if there's an error, and zero it
+ * beforehand so we're not faked out by an old error
+ */
+
+ errno = 0;
+ result = ptrace(arg1, child, arg3, 0);
+ if((result == -1) && (errno != 0)) return(-errno);
+
+ result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result);
+ if(result == -1) return(-errno);
+
+ return(result);
+
+ case PTRACE_POKEDATA:
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEUSR:
+ result = ptrace(arg1, child, arg3, arg4);
+ if(result == -1) return(-errno);
+
+ if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4);
+ return(result);
+
+#ifdef UM_HAVE_SETFPREGS
+ case PTRACE_SETFPREGS:
+ {
+ long regs[FP_FRAME_SIZE];
+ int i;
+
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
+ regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
+ arg4 + 4 * i, 0);
+ result = ptrace(PTRACE_SETFPREGS, child, 0, regs);
+ if(result == -1) return(-errno);
+
+ return(result);
+ }
+#endif
+
+#ifdef UM_HAVE_SETFPXREGS
+ case PTRACE_SETFPXREGS:
+ {
+ long regs[FPX_FRAME_SIZE];
+ int i;
+
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
+ regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
+ arg4 + 4 * i, 0);
+ result = ptrace(PTRACE_SETFPXREGS, child, 0, regs);
+ if(result == -1) return(-errno);
+
+ return(result);
+ }
+#endif
+
+#ifdef UM_HAVE_SETREGS
+ case PTRACE_SETREGS:
+ {
+ long regs[FRAME_SIZE];
+ int i;
+
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
+ regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid,
+ arg4 + 4 * i, 0);
+ result = ptrace(PTRACE_SETREGS, child, 0, regs);
+ if(result == -1) return(-errno);
+
+ return(result);
+ }
+#endif
+
+ case PTRACE_SINGLESTEP:
+ if(!debugger->debugee->in_context) return(-EPERM);
+ sigemptyset(&relay);
+ sigaddset(&relay, SIGSEGV);
+ sigaddset(&relay, SIGILL);
+ sigaddset(&relay, SIGBUS);
+ result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4);
+ if(result == -1) return(-errno);
+
+ status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP,
+ &relay);
+ child_proxy(child, status);
+ return(result);
+
+ case PTRACE_SYSCALL:
+ if(!debugger->debugee->in_context) return(-EPERM);
+ result = ptrace(PTRACE_SYSCALL, child, arg3, arg4);
+ if(result == -1) return(-errno);
+
+ *ret = PTRACE_SYSCALL;
+ return(result);
+
+ case PTRACE_TRACEME:
+ default:
+ return(-EINVAL);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/sysdep.c b/arch/um/kernel/tt/ptproxy/sysdep.c
new file mode 100644
index 0000000..a5f0e01
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/sysdep.c
@@ -0,0 +1,70 @@
+/**********************************************************************
+sysdep.c
+
+Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
+terms and conditions.
+**********************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <linux/unistd.h>
+#include "ptrace_user.h"
+#include "user_util.h"
+#include "user.h"
+
+int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4,
+ long *arg5)
+{
+ *arg1 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG1_OFFSET, 0);
+ *arg2 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG2_OFFSET, 0);
+ *arg3 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG3_OFFSET, 0);
+ *arg4 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG4_OFFSET, 0);
+ *arg5 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG5_OFFSET, 0);
+ return(ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, 0));
+}
+
+void syscall_cancel(pid_t pid, int result)
+{
+ if((ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
+ __NR_getpid) < 0) ||
+ (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) ||
+ (wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL, NULL) < 0) ||
+ (ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result) < 0) ||
+ (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0))
+ printk("ptproxy: couldn't cancel syscall: errno = %d\n",
+ errno);
+}
+
+void syscall_set_result(pid_t pid, long result)
+{
+ ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result);
+}
+
+void syscall_continue(pid_t pid)
+{
+ ptrace(PTRACE_SYSCALL, pid, 0, 0);
+}
+
+int syscall_pause(pid_t pid)
+{
+ if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_pause) < 0){
+ printk("syscall_change - ptrace failed, errno = %d\n", errno);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/sysdep.h b/arch/um/kernel/tt/ptproxy/sysdep.h
new file mode 100644
index 0000000..735f488
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/sysdep.h
@@ -0,0 +1,25 @@
+/**********************************************************************
+sysdep.h
+
+Copyright (C) 1999 Lars Brinkhoff.
+Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+See the file COPYING for licensing terms and conditions.
+**********************************************************************/
+
+extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3,
+ long *arg4, long *arg5);
+extern void syscall_cancel (pid_t pid, long result);
+extern void syscall_set_result (pid_t pid, long result);
+extern void syscall_continue (pid_t pid);
+extern int syscall_pause(pid_t pid);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/wait.c b/arch/um/kernel/tt/ptproxy/wait.c
new file mode 100644
index 0000000..12f6319
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/wait.c
@@ -0,0 +1,86 @@
+/**********************************************************************
+wait.c
+
+Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
+terms and conditions.
+
+**********************************************************************/
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "ptproxy.h"
+#include "sysdep.h"
+#include "wait.h"
+#include "user_util.h"
+#include "ptrace_user.h"
+#include "sysdep/ptrace.h"
+#include "sysdep/sigcontext.h"
+
+int proxy_wait_return(struct debugger *debugger, pid_t unused)
+{
+ debugger->waiting = 0;
+
+ if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){
+ debugger_cancelled_return(debugger, -ECHILD);
+ return(0);
+ }
+
+ if(debugger->debugee->zombie && debugger->debugee->event)
+ debugger->debugee->died = 1;
+
+ if(debugger->debugee->event){
+ debugger->debugee->event = 0;
+ ptrace(PTRACE_POKEDATA, debugger->pid,
+ debugger->wait_status_ptr,
+ debugger->debugee->wait_status);
+ /* if (wait4)
+ ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */
+ debugger_cancelled_return(debugger, debugger->debugee->pid);
+ return(0);
+ }
+
+ /* pause will return -EINTR, which happens to be right for wait */
+ debugger_normal_return(debugger, -1);
+ return(0);
+}
+
+int parent_wait_return(struct debugger *debugger, pid_t unused)
+{
+ return(debugger_normal_return(debugger, -1));
+}
+
+int real_wait_return(struct debugger *debugger)
+{
+ unsigned long ip;
+ int pid;
+
+ pid = debugger->pid;
+
+ ip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0);
+ IP_RESTART_SYSCALL(ip);
+
+ if(ptrace(PTRACE_POKEUSR, pid, PT_IP_OFFSET, ip) < 0)
+ tracer_panic("real_wait_return : Failed to restart system "
+ "call, errno = %d\n", errno);
+
+ if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) ||
+ (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
+ (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
+ debugger_normal_return(debugger, -1))
+ tracer_panic("real_wait_return : gdb failed to wait, "
+ "errno = %d\n", errno);
+ return(0);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/ptproxy/wait.h b/arch/um/kernel/tt/ptproxy/wait.h
new file mode 100644
index 0000000..542e73e
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/wait.h
@@ -0,0 +1,15 @@
+/**********************************************************************
+wait.h
+
+Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
+terms and conditions.
+**********************************************************************/
+
+#ifndef __PTPROXY_WAIT_H
+#define __PTPROXY_WAIT_H
+
+extern int proxy_wait_return(struct debugger *debugger, pid_t unused);
+extern int real_wait_return(struct debugger *debugger);
+extern int parent_wait_return(struct debugger *debugger, pid_t unused);
+
+#endif
diff --git a/arch/um/kernel/tt/syscall_kern.c b/arch/um/kernel/tt/syscall_kern.c
new file mode 100644
index 0000000..2650a62
--- /dev/null
+++ b/arch/um/kernel/tt/syscall_kern.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/types.h"
+#include "linux/utime.h"
+#include "linux/sys.h"
+#include "linux/ptrace.h"
+#include "asm/unistd.h"
+#include "asm/ptrace.h"
+#include "asm/uaccess.h"
+#include "asm/stat.h"
+#include "sysdep/syscalls.h"
+#include "kern_util.h"
+
+extern syscall_handler_t *sys_call_table[];
+
+long execute_syscall_tt(void *r)
+{
+ struct pt_regs *regs = r;
+ long res;
+ int syscall;
+
+#ifdef CONFIG_SYSCALL_DEBUG
+ current->thread.nsyscalls++;
+ nsyscalls++;
+#endif
+ syscall = UPT_SYSCALL_NR(&regs->regs);
+
+ if((syscall >= NR_syscalls) || (syscall < 0))
+ res = -ENOSYS;
+ else res = EXECUTE_SYSCALL(syscall, regs);
+
+ return(res);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/syscall_user.c b/arch/um/kernel/tt/syscall_user.c
new file mode 100644
index 0000000..e4e7e9c
--- /dev/null
+++ b/arch/um/kernel/tt/syscall_user.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <asm/unistd.h>
+#include "sysdep/ptrace.h"
+#include "sigcontext.h"
+#include "ptrace_user.h"
+#include "task.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "syscall_user.h"
+#include "tt.h"
+
+
+void syscall_handler_tt(int sig, union uml_pt_regs *regs)
+{
+ void *sc;
+ long result;
+ int syscall;
+#ifdef UML_CONFIG_DEBUG_SYSCALL
+ int index;
+#endif
+
+ syscall = UPT_SYSCALL_NR(regs);
+ sc = UPT_SC(regs);
+ SC_START_SYSCALL(sc);
+
+#ifdef UML_CONFIG_DEBUG_SYSCALL
+ index = record_syscall_start(syscall);
+#endif
+ syscall_trace(regs, 0);
+ result = execute_syscall_tt(regs);
+
+ /* regs->sc may have changed while the system call ran (there may
+ * have been an interrupt or segfault), so it needs to be refreshed.
+ */
+ UPT_SC(regs) = sc;
+
+ SC_SET_SYSCALL_RETURN(sc, result);
+
+ syscall_trace(regs, 1);
+#ifdef UML_CONFIG_DEBUG_SYSCALL
+ record_syscall_end(index, result);
+#endif
+}
+
+void do_sigtrap(void *task)
+{
+ UPT_SYSCALL_NR(TASK_REGS(task)) = -1;
+}
+
+void do_syscall(void *task, int pid, int local_using_sysemu)
+{
+ unsigned long proc_regs[FRAME_SIZE];
+
+ if(ptrace_getregs(pid, proc_regs) < 0)
+ tracer_panic("Couldn't read registers");
+
+ UPT_SYSCALL_NR(TASK_REGS(task)) = PT_SYSCALL_NR(proc_regs);
+
+ if(((unsigned long *) PT_IP(proc_regs) >= &_stext) &&
+ ((unsigned long *) PT_IP(proc_regs) <= &_etext))
+ tracer_panic("I'm tracing myself and I can't get out");
+
+ /* advanced sysemu mode set syscall number to -1 automatically */
+ if (local_using_sysemu==2)
+ return;
+
+ /* syscall number -1 in sysemu skips syscall restarting in host */
+ if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
+ local_using_sysemu ? -1 : __NR_getpid) < 0)
+ tracer_panic("do_syscall : Nullifying syscall failed, "
+ "errno = %d", errno);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/time.c b/arch/um/kernel/tt/time.c
new file mode 100644
index 0000000..8565b71
--- /dev/null
+++ b/arch/um/kernel/tt/time.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <signal.h>
+#include <sys/time.h>
+#include <time_user.h>
+#include "process.h"
+#include "user.h"
+
+void user_time_init_tt(void)
+{
+ if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
+ panic("Couldn't set SIGVTALRM handler");
+ set_interval(ITIMER_VIRTUAL);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/tlb.c b/arch/um/kernel/tt/tlb.c
new file mode 100644
index 0000000..203216a
--- /dev/null
+++ b/arch/um/kernel/tt/tlb.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Copyright 2003 PathScale, Inc.
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/kernel.h"
+#include "linux/sched.h"
+#include "linux/mm.h"
+#include "asm/page.h"
+#include "asm/pgtable.h"
+#include "asm/uaccess.h"
+#include "asm/tlbflush.h"
+#include "user_util.h"
+#include "mem_user.h"
+#include "os.h"
+#include "tlb.h"
+
+static void do_ops(int unused, struct host_vm_op *ops, int last)
+{
+ struct host_vm_op *op;
+ int i;
+
+ for(i = 0; i <= last; i++){
+ op = &ops[i];
+ switch(op->type){
+ case MMAP:
+ os_map_memory((void *) op->u.mmap.addr, op->u.mmap.fd,
+ op->u.mmap.offset, op->u.mmap.len,
+ op->u.mmap.r, op->u.mmap.w,
+ op->u.mmap.x);
+ break;
+ case MUNMAP:
+ os_unmap_memory((void *) op->u.munmap.addr,
+ op->u.munmap.len);
+ break;
+ case MPROTECT:
+ protect_memory(op->u.mprotect.addr, op->u.munmap.len,
+ op->u.mprotect.r, op->u.mprotect.w,
+ op->u.mprotect.x, 1);
+ break;
+ default:
+ printk("Unknown op type %d in do_ops\n", op->type);
+ break;
+ }
+ }
+}
+
+static void fix_range(struct mm_struct *mm, unsigned long start_addr,
+ unsigned long end_addr, int force)
+{
+ if((current->thread.mode.tt.extern_pid != -1) &&
+ (current->thread.mode.tt.extern_pid != os_getpid()))
+ panic("fix_range fixing wrong address space, current = 0x%p",
+ current);
+
+ fix_range_common(mm, start_addr, end_addr, force, 0, do_ops);
+}
+
+atomic_t vmchange_seq = ATOMIC_INIT(1);
+
+void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end)
+{
+ if(flush_tlb_kernel_range_common(start, end))
+ atomic_inc(&vmchange_seq);
+}
+
+static void protect_vm_page(unsigned long addr, int w, int must_succeed)
+{
+ int err;
+
+ err = protect_memory(addr, PAGE_SIZE, 1, w, 1, must_succeed);
+ if(err == 0) return;
+ else if((err == -EFAULT) || (err == -ENOMEM)){
+ flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
+ protect_vm_page(addr, w, 1);
+ }
+ else panic("protect_vm_page : protect failed, errno = %d\n", err);
+}
+
+void mprotect_kernel_vm(int w)
+{
+ struct mm_struct *mm;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+ unsigned long addr;
+
+ mm = &init_mm;
+ for(addr = start_vm; addr < end_vm;){
+ pgd = pgd_offset(mm, addr);
+ pud = pud_offset(pgd, addr);
+ pmd = pmd_offset(pud, addr);
+ if(pmd_present(*pmd)){
+ pte = pte_offset_kernel(pmd, addr);
+ if(pte_present(*pte)) protect_vm_page(addr, w, 0);
+ addr += PAGE_SIZE;
+ }
+ else addr += PMD_SIZE;
+ }
+}
+
+void flush_tlb_kernel_vm_tt(void)
+{
+ flush_tlb_kernel_range(start_vm, end_vm);
+}
+
+void __flush_tlb_one_tt(unsigned long addr)
+{
+ flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
+}
+
+void flush_tlb_range_tt(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ if(vma->vm_mm != current->mm) return;
+
+ /* Assumes that the range start ... end is entirely within
+ * either process memory or kernel vm
+ */
+ if((start >= start_vm) && (start < end_vm)){
+ if(flush_tlb_kernel_range_common(start, end))
+ atomic_inc(&vmchange_seq);
+ }
+ else fix_range(vma->vm_mm, start, end, 0);
+}
+
+void flush_tlb_mm_tt(struct mm_struct *mm)
+{
+ unsigned long seq;
+
+ if(mm != current->mm) return;
+
+ fix_range(mm, 0, STACK_TOP, 0);
+
+ seq = atomic_read(&vmchange_seq);
+ if(current->thread.mode.tt.vm_seq == seq)
+ return;
+ current->thread.mode.tt.vm_seq = seq;
+ flush_tlb_kernel_range_common(start_vm, end_vm);
+}
+
+void force_flush_all_tt(void)
+{
+ fix_range(current->mm, 0, STACK_TOP, 1);
+ flush_tlb_kernel_range_common(start_vm, end_vm);
+}
diff --git a/arch/um/kernel/tt/tracer.c b/arch/um/kernel/tt/tracer.c
new file mode 100644
index 0000000..7b5d937
--- /dev/null
+++ b/arch/um/kernel/tt/tracer.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include "user.h"
+#include "sysdep/ptrace.h"
+#include "sigcontext.h"
+#include "sysdep/sigcontext.h"
+#include "os.h"
+#include "signal_user.h"
+#include "user_util.h"
+#include "mem_user.h"
+#include "process.h"
+#include "kern_util.h"
+#include "chan_user.h"
+#include "ptrace_user.h"
+#include "mode.h"
+#include "tt.h"
+
+static int tracer_winch[2];
+
+int is_tracer_winch(int pid, int fd, void *data)
+{
+ if(pid != tracing_pid)
+ return(0);
+
+ register_winch_irq(tracer_winch[0], fd, -1, data);
+ return(1);
+}
+
+static void tracer_winch_handler(int sig)
+{
+ int n;
+ char c = 1;
+
+ n = os_write_file(tracer_winch[1], &c, sizeof(c));
+ if(n != sizeof(c))
+ printk("tracer_winch_handler - write failed, err = %d\n", -n);
+}
+
+/* Called only by the tracing thread during initialization */
+
+static void setup_tracer_winch(void)
+{
+ int err;
+
+ err = os_pipe(tracer_winch, 1, 1);
+ if(err < 0){
+ printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err);
+ return;
+ }
+ signal(SIGWINCH, tracer_winch_handler);
+}
+
+void attach_process(int pid)
+{
+ if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) ||
+ (ptrace(PTRACE_CONT, pid, 0, 0) < 0))
+ tracer_panic("OP_FORK failed to attach pid");
+ wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL);
+ if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
+ tracer_panic("OP_FORK: PTRACE_SETOPTIONS failed, errno = %d", errno);
+ if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
+ tracer_panic("OP_FORK failed to continue process");
+}
+
+void tracer_panic(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ printf("\n");
+ while(1) pause();
+}
+
+static void tracer_segv(int sig, struct sigcontext sc)
+{
+ printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n",
+ SC_FAULT_ADDR(&sc), SC_IP(&sc));
+ while(1)
+ pause();
+}
+
+/* Changed early in boot, and then only read */
+int debug = 0;
+int debug_stop = 1;
+int debug_parent = 0;
+int honeypot = 0;
+
+static int signal_tramp(void *arg)
+{
+ int (*proc)(void *);
+
+ if(honeypot && munmap((void *) (host_task_size - 0x10000000),
+ 0x10000000))
+ panic("Unmapping stack failed");
+ if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
+ panic("ptrace PTRACE_TRACEME failed");
+ os_stop_process(os_getpid());
+ change_sig(SIGWINCH, 0);
+ signal(SIGUSR1, SIG_IGN);
+ change_sig(SIGCHLD, 0);
+ signal(SIGSEGV, (__sighandler_t) sig_handler);
+ set_cmdline("(idle thread)");
+ set_init_pid(os_getpid());
+ proc = arg;
+ return((*proc)(NULL));
+}
+
+static void sleeping_process_signal(int pid, int sig)
+{
+ switch(sig){
+ /* These two result from UML being ^Z-ed and bg-ed. PTRACE_CONT is
+ * right because the process must be in the kernel already.
+ */
+ case SIGCONT:
+ case SIGTSTP:
+ if(ptrace(PTRACE_CONT, pid, 0, sig) < 0)
+ tracer_panic("sleeping_process_signal : Failed to "
+ "continue pid %d, signal = %d, "
+ "errno = %d\n", pid, sig, errno);
+ break;
+
+ /* This happens when the debugger (e.g. strace) is doing system call
+ * tracing on the kernel. During a context switch, the current task
+ * will be set to the incoming process and the outgoing process will
+ * hop into write and then read. Since it's not the current process
+ * any more, the trace of those will land here. So, we need to just
+ * PTRACE_SYSCALL it.
+ */
+ case (SIGTRAP + 0x80):
+ if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
+ tracer_panic("sleeping_process_signal : Failed to "
+ "PTRACE_SYSCALL pid %d, errno = %d\n",
+ pid, errno);
+ break;
+ case SIGSTOP:
+ break;
+ default:
+ tracer_panic("sleeping process %d got unexpected "
+ "signal : %d\n", pid, sig);
+ break;
+ }
+}
+
+/* Accessed only by the tracing thread */
+int debugger_pid = -1;
+int debugger_parent = -1;
+int debugger_fd = -1;
+int gdb_pid = -1;
+
+struct {
+ int pid;
+ int signal;
+ unsigned long addr;
+ struct timeval time;
+} signal_record[1024][32];
+
+int signal_index[32];
+int nsignals = 0;
+int debug_trace = 0;
+extern int io_nsignals, io_count, intr_count;
+
+extern void signal_usr1(int sig);
+
+int tracing_pid = -1;
+
+int tracer(int (*init_proc)(void *), void *sp)
+{
+ void *task = NULL;
+ int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0;
+ int proc_id = 0, n, err, old_tracing = 0, strace = 0;
+ int local_using_sysemu = 0;
+#ifdef UML_CONFIG_SYSCALL_DEBUG
+ unsigned long eip = 0;
+ int last_index;
+#endif
+ signal(SIGPIPE, SIG_IGN);
+ setup_tracer_winch();
+ tracing_pid = os_getpid();
+ printf("tracing thread pid = %d\n", tracing_pid);
+
+ pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc);
+ CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+ if(n < 0){
+ printf("waitpid on idle thread failed, errno = %d\n", errno);
+ exit(1);
+ }
+ if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) {
+ printf("Failed to PTRACE_SETOPTIONS for idle thread, errno = %d\n", errno);
+ exit(1);
+ }
+ if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){
+ printf("Failed to continue idle thread, errno = %d\n", errno);
+ exit(1);
+ }
+
+ signal(SIGSEGV, (sighandler_t) tracer_segv);
+ signal(SIGUSR1, signal_usr1);
+ if(debug_trace){
+ printf("Tracing thread pausing to be attached\n");
+ stop();
+ }
+ if(debug){
+ if(gdb_pid != -1)
+ debugger_pid = attach_debugger(pid, gdb_pid, 1);
+ else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop);
+ if(debug_parent){
+ debugger_parent = os_process_parent(debugger_pid);
+ init_parent_proxy(debugger_parent);
+ err = attach(debugger_parent);
+ if(err){
+ printf("Failed to attach debugger parent %d, "
+ "errno = %d\n", debugger_parent, -err);
+ debugger_parent = -1;
+ }
+ else {
+ if(ptrace(PTRACE_SYSCALL, debugger_parent,
+ 0, 0) < 0){
+ printf("Failed to continue debugger "
+ "parent, errno = %d\n", errno);
+ debugger_parent = -1;
+ }
+ }
+ }
+ }
+ set_cmdline("(tracing thread)");
+ while(1){
+ CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED));
+ if(pid <= 0){
+ if(errno != ECHILD){
+ printf("wait failed - errno = %d\n", errno);
+ }
+ continue;
+ }
+ if(pid == debugger_pid){
+ int cont = 0;
+
+ if(WIFEXITED(status) || WIFSIGNALED(status))
+ debugger_pid = -1;
+ /* XXX Figure out how to deal with gdb and SMP */
+ else cont = debugger_signal(status, cpu_tasks[0].pid);
+ if(cont == PTRACE_SYSCALL) strace = 1;
+ continue;
+ }
+ else if(pid == debugger_parent){
+ debugger_parent_signal(status, pid);
+ continue;
+ }
+ nsignals++;
+ if(WIFEXITED(status)) ;
+#ifdef notdef
+ {
+ printf("Child %d exited with status %d\n", pid,
+ WEXITSTATUS(status));
+ }
+#endif
+ else if(WIFSIGNALED(status)){
+ sig = WTERMSIG(status);
+ if(sig != 9){
+ printf("Child %d exited with signal %d\n", pid,
+ sig);
+ }
+ }
+ else if(WIFSTOPPED(status)){
+ proc_id = pid_to_processor_id(pid);
+ sig = WSTOPSIG(status);
+#ifdef UML_CONFIG_SYSCALL_DEBUG
+ if(signal_index[proc_id] == 1024){
+ signal_index[proc_id] = 0;
+ last_index = 1023;
+ }
+ else last_index = signal_index[proc_id] - 1;
+ if(((sig == SIGPROF) || (sig == SIGVTALRM) ||
+ (sig == SIGALRM)) &&
+ (signal_record[proc_id][last_index].signal == sig)&&
+ (signal_record[proc_id][last_index].pid == pid))
+ signal_index[proc_id] = last_index;
+ signal_record[proc_id][signal_index[proc_id]].pid = pid;
+ gettimeofday(&signal_record[proc_id][signal_index[proc_id]].time, NULL);
+ eip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0);
+ signal_record[proc_id][signal_index[proc_id]].addr = eip;
+ signal_record[proc_id][signal_index[proc_id]++].signal = sig;
+#endif
+ if(proc_id == -1){
+ sleeping_process_signal(pid, sig);
+ continue;
+ }
+
+ task = cpu_tasks[proc_id].task;
+ tracing = is_tracing(task);
+ old_tracing = tracing;
+
+ /* Assume: no syscall, when coming from user */
+ if ( tracing )
+ do_sigtrap(task);
+
+ switch(sig){
+ case SIGUSR1:
+ sig = 0;
+ op = do_proc_op(task, proc_id);
+ switch(op){
+ /*
+ * This is called when entering user mode; after
+ * this, we start intercepting syscalls.
+ *
+ * In fact, a process is started in kernel mode,
+ * so with is_tracing() == 0 (and that is reset
+ * when executing syscalls, since UML kernel has
+ * the right to do syscalls);
+ */
+ case OP_TRACE_ON:
+ arch_leave_kernel(task, pid);
+ tracing = 1;
+ break;
+ case OP_REBOOT:
+ case OP_HALT:
+ unmap_physmem();
+ kmalloc_ok = 0;
+ os_kill_ptraced_process(pid, 0);
+ /* Now let's reap remaining zombies */
+ errno = 0;
+ do {
+ waitpid(-1, &status,
+ WUNTRACED);
+ } while (errno != ECHILD);
+ return(op == OP_REBOOT);
+ case OP_NONE:
+ printf("Detaching pid %d\n", pid);
+ detach(pid, SIGSTOP);
+ continue;
+ default:
+ break;
+ }
+ /* OP_EXEC switches host processes on us,
+ * we want to continue the new one.
+ */
+ pid = cpu_tasks[proc_id].pid;
+ break;
+ case (SIGTRAP + 0x80):
+ if(!tracing && (debugger_pid != -1)){
+ child_signal(pid, status & 0x7fff);
+ continue;
+ }
+ tracing = 0;
+ /* local_using_sysemu has been already set
+ * below, since if we are here, is_tracing() on
+ * the traced task was 1, i.e. the process had
+ * already run through one iteration of the
+ * loop which executed a OP_TRACE_ON request.*/
+ do_syscall(task, pid, local_using_sysemu);
+ sig = SIGUSR2;
+ break;
+ case SIGTRAP:
+ if(!tracing && (debugger_pid != -1)){
+ child_signal(pid, status);
+ continue;
+ }
+ tracing = 0;
+ break;
+ case SIGPROF:
+ if(tracing) sig = 0;
+ break;
+ case SIGCHLD:
+ case SIGHUP:
+ sig = 0;
+ break;
+ case SIGSEGV:
+ case SIGIO:
+ case SIGALRM:
+ case SIGVTALRM:
+ case SIGFPE:
+ case SIGBUS:
+ case SIGILL:
+ case SIGWINCH:
+
+ default:
+ tracing = 0;
+ break;
+ }
+ set_tracing(task, tracing);
+
+ if(!tracing && old_tracing)
+ arch_enter_kernel(task, pid);
+
+ if(!tracing && (debugger_pid != -1) && (sig != 0) &&
+ (sig != SIGALRM) && (sig != SIGVTALRM) &&
+ (sig != SIGSEGV) && (sig != SIGTRAP) &&
+ (sig != SIGUSR2) && (sig != SIGIO) &&
+ (sig != SIGFPE)){
+ child_signal(pid, status);
+ continue;
+ }
+
+ local_using_sysemu = get_using_sysemu();
+
+ if(tracing)
+ cont_type = SELECT_PTRACE_OPERATION(local_using_sysemu,
+ singlestepping(task));
+ else if((debugger_pid != -1) && strace)
+ cont_type = PTRACE_SYSCALL;
+ else
+ cont_type = PTRACE_CONT;
+
+ if(ptrace(cont_type, pid, 0, sig) != 0){
+ tracer_panic("ptrace failed to continue "
+ "process - errno = %d\n",
+ errno);
+ }
+ }
+ }
+ return(0);
+}
+
+static int __init uml_debug_setup(char *line, int *add)
+{
+ char *next;
+
+ debug = 1;
+ *add = 0;
+ if(*line != '=') return(0);
+ line++;
+
+ while(line != NULL){
+ next = strchr(line, ',');
+ if(next) *next++ = '\0';
+
+ if(!strcmp(line, "go")) debug_stop = 0;
+ else if(!strcmp(line, "parent")) debug_parent = 1;
+ else printf("Unknown debug option : '%s'\n", line);
+
+ line = next;
+ }
+ return(0);
+}
+
+__uml_setup("debug", uml_debug_setup,
+"debug\n"
+" Starts up the kernel under the control of gdb. See the \n"
+" kernel debugging tutorial and the debugging session pages\n"
+" at http://user-mode-linux.sourceforge.net/ for more information.\n\n"
+);
+
+static int __init uml_debugtrace_setup(char *line, int *add)
+{
+ debug_trace = 1;
+ return 0;
+}
+__uml_setup("debugtrace", uml_debugtrace_setup,
+"debugtrace\n"
+" Causes the tracing thread to pause until it is attached by a\n"
+" debugger and continued. This is mostly for debugging crashes\n"
+" early during boot, and should be pretty much obsoleted by\n"
+" the debug switch.\n\n"
+);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/trap_user.c b/arch/um/kernel/tt/trap_user.c
new file mode 100644
index 0000000..92a3820
--- /dev/null
+++ b/arch/um/kernel/tt/trap_user.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include "sysdep/ptrace.h"
+#include "signal_user.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "task.h"
+#include "tt.h"
+
+void sig_handler_common_tt(int sig, void *sc_ptr)
+{
+ struct sigcontext *sc = sc_ptr;
+ struct tt_regs save_regs, *r;
+ struct signal_info *info;
+ int save_errno = errno, is_user;
+
+ /* This is done because to allow SIGSEGV to be delivered inside a SEGV
+ * handler. This can happen in copy_user, and if SEGV is disabled,
+ * the process will die.
+ */
+ if(sig == SIGSEGV)
+ change_sig(SIGSEGV, 1);
+
+ r = &TASK_REGS(get_current())->tt;
+ save_regs = *r;
+ is_user = user_context(SC_SP(sc));
+ r->sc = sc;
+ if(sig != SIGUSR2)
+ r->syscall = -1;
+
+ info = &sig_info[sig];
+ if(!info->is_irq) unblock_signals();
+
+ (*info->handler)(sig, (union uml_pt_regs *) r);
+
+ if(is_user){
+ interrupt_end();
+ block_signals();
+ set_user_mode(NULL);
+ }
+ *r = save_regs;
+ errno = save_errno;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/uaccess.c b/arch/um/kernel/tt/uaccess.c
new file mode 100644
index 0000000..a72aa63
--- /dev/null
+++ b/arch/um/kernel/tt/uaccess.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/sched.h"
+#include "asm/uaccess.h"
+
+int copy_from_user_tt(void *to, const void __user *from, int n)
+{
+ if(!access_ok_tt(VERIFY_READ, from, n))
+ return(n);
+
+ return(__do_copy_from_user(to, from, n, &current->thread.fault_addr,
+ &current->thread.fault_catcher));
+}
+
+int copy_to_user_tt(void __user *to, const void *from, int n)
+{
+ if(!access_ok_tt(VERIFY_WRITE, to, n))
+ return(n);
+
+ return(__do_copy_to_user(to, from, n, &current->thread.fault_addr,
+ &current->thread.fault_catcher));
+}
+
+int strncpy_from_user_tt(char *dst, const char __user *src, int count)
+{
+ int n;
+
+ if(!access_ok_tt(VERIFY_READ, src, 1))
+ return(-EFAULT);
+
+ n = __do_strncpy_from_user(dst, src, count,
+ &current->thread.fault_addr,
+ &current->thread.fault_catcher);
+ if(n < 0) return(-EFAULT);
+ return(n);
+}
+
+int __clear_user_tt(void __user *mem, int len)
+{
+ return(__do_clear_user(mem, len,
+ &current->thread.fault_addr,
+ &current->thread.fault_catcher));
+}
+
+int clear_user_tt(void __user *mem, int len)
+{
+ if(!access_ok_tt(VERIFY_WRITE, mem, len))
+ return(len);
+
+ return(__do_clear_user(mem, len, &current->thread.fault_addr,
+ &current->thread.fault_catcher));
+}
+
+int strnlen_user_tt(const void __user *str, int len)
+{
+ return(__do_strnlen_user(str, len,
+ &current->thread.fault_addr,
+ &current->thread.fault_catcher));
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/uaccess_user.c b/arch/um/kernel/tt/uaccess_user.c
new file mode 100644
index 0000000..f014755
--- /dev/null
+++ b/arch/um/kernel/tt/uaccess_user.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <setjmp.h>
+#include <string.h>
+#include "user_util.h"
+#include "uml_uaccess.h"
+#include "task.h"
+#include "kern_util.h"
+
+int __do_copy_from_user(void *to, const void *from, int n,
+ void **fault_addr, void **fault_catcher)
+{
+ struct tt_regs save = TASK_REGS(get_current())->tt;
+ unsigned long fault;
+ int faulted;
+
+ fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
+ __do_copy, &faulted);
+ TASK_REGS(get_current())->tt = save;
+
+ if(!faulted) return(0);
+ else return(n - (fault - (unsigned long) from));
+}
+
+static void __do_strncpy(void *dst, const void *src, int count)
+{
+ strncpy(dst, src, count);
+}
+
+int __do_strncpy_from_user(char *dst, const char *src, unsigned long count,
+ void **fault_addr, void **fault_catcher)
+{
+ struct tt_regs save = TASK_REGS(get_current())->tt;
+ unsigned long fault;
+ int faulted;
+
+ fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher,
+ __do_strncpy, &faulted);
+ TASK_REGS(get_current())->tt = save;
+
+ if(!faulted) return(strlen(dst));
+ else return(-1);
+}
+
+static void __do_clear(void *to, const void *from, int n)
+{
+ memset(to, 0, n);
+}
+
+int __do_clear_user(void *mem, unsigned long len,
+ void **fault_addr, void **fault_catcher)
+{
+ struct tt_regs save = TASK_REGS(get_current())->tt;
+ unsigned long fault;
+ int faulted;
+
+ fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher,
+ __do_clear, &faulted);
+ TASK_REGS(get_current())->tt = save;
+
+ if(!faulted) return(0);
+ else return(len - (fault - (unsigned long) mem));
+}
+
+int __do_strnlen_user(const char *str, unsigned long n,
+ void **fault_addr, void **fault_catcher)
+{
+ struct tt_regs save = TASK_REGS(get_current())->tt;
+ int ret;
+ unsigned long *faddrp = (unsigned long *)fault_addr;
+ sigjmp_buf jbuf;
+
+ *fault_catcher = &jbuf;
+ if(sigsetjmp(jbuf, 1) == 0)
+ ret = strlen(str) + 1;
+ else ret = *faddrp - (unsigned long) str;
+
+ *fault_addr = NULL;
+ *fault_catcher = NULL;
+
+ TASK_REGS(get_current())->tt = save;
+ return ret;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/kernel/tt/unmap.c b/arch/um/kernel/tt/unmap.c
new file mode 100644
index 0000000..3f7aecd
--- /dev/null
+++ b/arch/um/kernel/tt/unmap.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <sys/mman.h>
+
+int switcheroo(int fd, int prot, void *from, void *to, int size)
+{
+ if(munmap(to, size) < 0){
+ return(-1);
+ }
+ if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){
+ return(-1);
+ }
+ if(munmap(from, size) < 0){
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
OpenPOWER on IntegriCloud