diff options
Diffstat (limited to 'arch/um/kernel/tt')
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 00000000000..3d5177df350 --- /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 00000000000..065b504a653 --- /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 00000000000..a92c02ff2ce --- /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 00000000000..19a0ad7b35b --- /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 00000000000..93fb121f86a --- /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 00000000000..8eff674107c --- /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 00000000000..0440510ab3f --- /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 00000000000..efe46201906 --- /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 00000000000..28aaab3448f --- /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 00000000000..c667b67af40 --- /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 00000000000..f0bad010ceb --- /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 00000000000..92ec85d67c7 --- /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 00000000000..74346a04a2b --- /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 00000000000..3085267459b --- /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 00000000000..f19f7c18feb --- /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(¤t->thread.regs.regs) = (void *) (&sig + 1); + disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) | + (1 << (SIGIO - 1)) | (1 << (SIGPROF - 1)); + SC_SIGMASK(UPT_SC(¤t->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, ¤t->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(¤t->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(¤t->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 00000000000..3ad5b774de5 --- /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 00000000000..58800c50b10 --- /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 00000000000..5eb0285b196 --- /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 00000000000..528a5fc8d88 --- /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 00000000000..a5f0e01e214 --- /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 00000000000..735f488049a --- /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 00000000000..12f6319d8d7 --- /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 00000000000..542e73ee2ce --- /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 00000000000..2650a628719 --- /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(®s->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 00000000000..e4e7e9c2224 --- /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 00000000000..8565b71b07c --- /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 00000000000..203216ad86f --- /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 00000000000..7b5d937e595 --- /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 00000000000..92a3820ca54 --- /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 00000000000..a72aa632972 --- /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, ¤t->thread.fault_addr, + ¤t->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, ¤t->thread.fault_addr, + ¤t->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, + ¤t->thread.fault_addr, + ¤t->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, + ¤t->thread.fault_addr, + ¤t->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, ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)); +} + +int strnlen_user_tt(const void __user *str, int len) +{ + return(__do_strnlen_user(str, len, + ¤t->thread.fault_addr, + ¤t->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 00000000000..f01475512ec --- /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 00000000000..3f7aecdbe53 --- /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: + */ |