X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fum%2Fkernel%2Fskas%2Fprocess.c;h=ae4fa71d3b8b6540d4071712a1f5404ea5436eb9;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=cb748f85daceb9338ee367bcbc6363d34676c25a;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index cb748f85d..ae4fa71d3 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -1,427 +1,217 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "user.h" -#include "ptrace_user.h" -#include "time_user.h" -#include "sysdep/ptrace.h" -#include "user_util.h" +#include "linux/sched.h" +#include "linux/slab.h" +#include "linux/ptrace.h" +#include "linux/proc_fs.h" +#include "linux/file.h" +#include "linux/errno.h" +#include "linux/init.h" +#include "asm/uaccess.h" +#include "asm/atomic.h" #include "kern_util.h" #include "skas.h" -#include "sysdep/sigcontext.h" #include "os.h" -#include "proc_mm.h" -#include "skas_ptrace.h" -#include "chan_user.h" -#include "signal_user.h" +#include "user_util.h" +#include "tlb.h" +#include "kern.h" +#include "mode.h" +#include "registers.h" -int is_skas_winch(int pid, int fd, void *data) +void switch_to_skas(void *prev, void *next) { - if(pid != getpid()) - return(0); + struct task_struct *from, *to; - register_winch_irq(-1, fd, -1, data); - return(1); -} + from = prev; + to = next; -/* These are set once at boot time and not changed thereafter */ + /* XXX need to check runqueues[cpu].idle */ + if(current->pid == 0) + switch_timers(0); -unsigned long exec_regs[FRAME_SIZE]; -unsigned long exec_fp_regs[HOST_FP_SIZE]; -unsigned long exec_fpx_regs[HOST_XFP_SIZE]; -int have_fpx_regs = 1; + switch_threads(&from->thread.mode.skas.switch_buf, + &to->thread.mode.skas.switch_buf); -static void handle_segv(int pid) -{ - struct ptrace_faultinfo fault; - int err; - - err = ptrace(PTRACE_FAULTINFO, pid, 0, &fault); - if(err) - panic("handle_segv - PTRACE_FAULTINFO failed, errno = %d\n", - errno); + arch_switch_to_skas(current->thread.prev_sched, current); - segv(fault.addr, 0, FAULT_WRITE(fault.is_write), 1, NULL); + if(current->pid == 0) + switch_timers(1); } -/*To use the same value of using_sysemu as the caller, ask it that value (in local_using_sysemu)*/ -static void handle_trap(int pid, union uml_pt_regs *regs, int local_using_sysemu) -{ - int err, syscall_nr, status; +extern void schedule_tail(struct task_struct *prev); - syscall_nr = PT_SYSCALL_NR(regs->skas.regs); - UPT_SYSCALL_NR(regs) = syscall_nr; - if(syscall_nr < 1){ - relay_signal(SIGTRAP, regs); - return; - } +/* This is called magically, by its address being stuffed in a jmp_buf + * and being longjmp-d to. + */ +void new_thread_handler(void) +{ + int (*fn)(void *), n; + void *arg; - if (!local_using_sysemu) - { - err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, __NR_getpid); - if(err < 0) - panic("handle_trap - nullifying syscall failed errno = %d\n", - errno); - - err = ptrace(PTRACE_SYSCALL, pid, 0, 0); - if(err < 0) - panic("handle_trap - continuing to end of syscall failed, " - "errno = %d\n", errno); - - CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); - if((err < 0) || !WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) - panic("handle_trap - failed to wait at end of syscall, " - "errno = %d, status = %d\n", errno, status); - } + if(current->thread.prev_sched != NULL) + schedule_tail(current->thread.prev_sched); + current->thread.prev_sched = NULL; - handle_syscall(regs); -} + fn = current->thread.request.u.thread.proc; + arg = current->thread.request.u.thread.arg; -static int userspace_tramp(void *arg) -{ - init_new_thread_signals(0); - enable_timer(); - ptrace(PTRACE_TRACEME, 0, 0, 0); - os_stop_process(os_getpid()); - return(0); + /* The return value is 1 if the kernel thread execs a process, + * 0 if it just exits + */ + n = run_kernel_thread(fn, arg, ¤t->thread.exec_buf); + if(n == 1){ + /* Handle any immediate reschedules or signals */ + interrupt_end(); + userspace(¤t->thread.regs.regs); + } + else do_exit(0); } -/* Each element set once, and only accessed by a single processor anyway */ -#define NR_CPUS 1 -int userspace_pid[NR_CPUS]; - -void start_userspace(int cpu) +void release_thread_skas(struct task_struct *task) { - void *stack; - unsigned long sp; - int pid, status, n; - - stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if(stack == MAP_FAILED) - panic("start_userspace : mmap failed, errno = %d", errno); - sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *); - - pid = clone(userspace_tramp, (void *) sp, - CLONE_FILES | CLONE_VM | SIGCHLD, NULL); - if(pid < 0) - panic("start_userspace : clone failed, errno = %d", errno); - - do { - CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); - if(n < 0) - panic("start_userspace : wait failed, errno = %d", - errno); - } while(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGVTALRM)); - - if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) - panic("start_userspace : expected SIGSTOP, got status = %d", - status); - - if(munmap(stack, PAGE_SIZE) < 0) - panic("start_userspace : munmap failed, errno = %d\n", errno); - - userspace_pid[cpu] = pid; } -void userspace(union uml_pt_regs *regs) +/* Called magically, see new_thread_handler above */ +void fork_handler(void) { - int err, status, op, pid = userspace_pid[0]; - int local_using_sysemu; /*To prevent races if using_sysemu changes under us.*/ - - restore_registers(regs); - - local_using_sysemu = get_using_sysemu(); - - if (local_using_sysemu) - err = ptrace(PTRACE_SYSEMU, pid, 0, 0); - else - err = ptrace(PTRACE_SYSCALL, pid, 0, 0); - if(err) - panic("userspace - PTRACE_%s failed, errno = %d\n", - local_using_sysemu ? "SYSEMU" : "SYSCALL", errno); - while(1){ - CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); - if(err < 0) - panic("userspace - waitpid failed, errno = %d\n", - errno); - - regs->skas.is_user = 1; - save_registers(regs); - - if(WIFSTOPPED(status)){ - switch(WSTOPSIG(status)){ - case SIGSEGV: - handle_segv(pid); - break; - case SIGTRAP: - handle_trap(pid, regs, local_using_sysemu); - break; - case SIGIO: - case SIGVTALRM: - case SIGILL: - case SIGBUS: - case SIGFPE: - case SIGWINCH: - user_signal(WSTOPSIG(status), regs); - break; - default: - printk("userspace - child stopped with signal " - "%d\n", WSTOPSIG(status)); - } - interrupt_end(); - } + force_flush_all(); + if(current->thread.prev_sched == NULL) + panic("blech"); - restore_registers(regs); + schedule_tail(current->thread.prev_sched); - /*Now we ended the syscall, so re-read local_using_sysemu.*/ - local_using_sysemu = get_using_sysemu(); + /* XXX: if interrupt_end() calls schedule, this call to + * arch_switch_to_skas isn't needed. We could want to apply this to + * improve performance. -bb */ + arch_switch_to_skas(current->thread.prev_sched, current); - if (local_using_sysemu) - op = singlestepping_skas() ? PTRACE_SINGLESTEP : - PTRACE_SYSEMU; - else - op = singlestepping_skas() ? PTRACE_SINGLESTEP : - PTRACE_SYSCALL; + current->thread.prev_sched = NULL; - err = ptrace(op, pid, 0, 0); - if(err) - panic("userspace - PTRACE_%s failed, " - "errno = %d\n", - local_using_sysemu ? "SYSEMU" : "SYSCALL", errno); - } -} +/* Handle any immediate reschedules or signals */ + interrupt_end(); -void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, - void (*handler)(int)) -{ - unsigned long flags; - sigjmp_buf switch_buf, fork_buf; - - *switch_buf_ptr = &switch_buf; - *fork_buf_ptr = &fork_buf; - - /* Somewhat subtle - siglongjmp restores the signal mask before doing - * the longjmp. This means that when jumping from one stack to another - * when the target stack has interrupts enabled, an interrupt may occur - * on the source stack. This is bad when starting up a process because - * it's not supposed to get timer ticks until it has been scheduled. - * So, we disable interrupts around the sigsetjmp to ensure that - * they can't happen until we get back here where they are safe. - */ - flags = get_signals(); - block_signals(); - if(sigsetjmp(fork_buf, 1) == 0) - new_thread_proc(stack, handler); - set_signals(flags); - - remove_sigstack(); + userspace(¤t->thread.regs.regs); } -void thread_wait(void *sw, void *fb) +int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp, + unsigned long stack_top, struct task_struct * p, + struct pt_regs *regs) { - sigjmp_buf buf, **switch_buf = sw, *fork_buf; + void (*handler)(void); - *switch_buf = &buf; - fork_buf = fb; - if(sigsetjmp(buf, 1) == 0) - siglongjmp(*fork_buf, 1); -} + if(current->thread.forking){ + memcpy(&p->thread.regs.regs.skas, ®s->regs.skas, + sizeof(p->thread.regs.regs.skas)); + REGS_SET_SYSCALL_RETURN(p->thread.regs.regs.skas.regs, 0); + if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp; -static int move_registers(int pid, int int_op, int fp_op, - union uml_pt_regs *regs, unsigned long *fp_regs) -{ - if(ptrace(int_op, pid, 0, regs->skas.regs) < 0) - return(-errno); - if(ptrace(fp_op, pid, 0, fp_regs) < 0) - return(-errno); - return(0); -} - -void save_registers(union uml_pt_regs *regs) -{ - unsigned long *fp_regs; - int err, fp_op; + handler = fork_handler; - if(have_fpx_regs){ - fp_op = PTRACE_GETFPXREGS; - fp_regs = regs->skas.xfp; + arch_copy_thread(¤t->thread.arch, &p->thread.arch); } else { - fp_op = PTRACE_GETFPREGS; - fp_regs = regs->skas.fp; + init_thread_registers(&p->thread.regs.regs); + p->thread.request.u.thread = current->thread.request.u.thread; + handler = new_thread_handler; } - err = move_registers(userspace_pid[0], PTRACE_GETREGS, fp_op, regs, - fp_regs); - if(err) - panic("save_registers - saving registers failed, errno = %d\n", - -err); + new_thread(task_stack_page(p), &p->thread.mode.skas.switch_buf, + handler); + return(0); } -void restore_registers(union uml_pt_regs *regs) +int new_mm(unsigned long stack) { - unsigned long *fp_regs; - int err, fp_op; + int fd; - if(have_fpx_regs){ - fp_op = PTRACE_SETFPXREGS; - fp_regs = regs->skas.xfp; - } - else { - fp_op = PTRACE_SETFPREGS; - fp_regs = regs->skas.fp; - } + fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0); + if(fd < 0) + return(fd); - err = move_registers(userspace_pid[0], PTRACE_SETREGS, fp_op, regs, - fp_regs); - if(err) - panic("restore_registers - saving registers failed, " - "errno = %d\n", -err); -} + if(skas_needs_stub) + map_stub_pages(fd, CONFIG_STUB_CODE, CONFIG_STUB_DATA, stack); -void switch_threads(void *me, void *next) -{ - sigjmp_buf my_buf, **me_ptr = me, *next_buf = next; - - *me_ptr = &my_buf; - if(sigsetjmp(my_buf, 1) == 0) - siglongjmp(*next_buf, 1); + return(fd); } -static sigjmp_buf initial_jmpbuf; - -/* XXX Make these percpu */ -static void (*cb_proc)(void *arg); -static void *cb_arg; -static sigjmp_buf *cb_back; - -int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) +void init_idle_skas(void) { - sigjmp_buf **switch_buf = switch_buf_ptr; - int n; - - *fork_buf_ptr = &initial_jmpbuf; - n = sigsetjmp(initial_jmpbuf, 1); - if(n == 0) - new_thread_proc((void *) stack, new_thread_handler); - else if(n == 1) - remove_sigstack(); - else if(n == 2){ - (*cb_proc)(cb_arg); - siglongjmp(*cb_back, 1); - } - else if(n == 3){ - kmalloc_ok = 0; - return(0); - } - else if(n == 4){ - kmalloc_ok = 0; - return(1); - } - siglongjmp(**switch_buf, 1); + cpu_tasks[current_thread->cpu].pid = os_getpid(); + default_idle(); } -void remove_sigstack(void) -{ - stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE, - .ss_sp = NULL, - .ss_size = 0 }); - - if(sigaltstack(&stack, NULL) != 0) - panic("disabling signal stack failed, errno = %d\n", errno); -} +extern void start_kernel(void); -void initial_thread_cb_skas(void (*proc)(void *), void *arg) +static int start_kernel_proc(void *unused) { - sigjmp_buf here; - - cb_proc = proc; - cb_arg = arg; - cb_back = &here; + int pid; block_signals(); - if(sigsetjmp(here, 1) == 0) - siglongjmp(initial_jmpbuf, 2); - unblock_signals(); - - cb_proc = NULL; - cb_arg = NULL; - cb_back = NULL; + pid = os_getpid(); + + cpu_tasks[0].pid = pid; + cpu_tasks[0].task = current; +#ifdef CONFIG_SMP + cpu_online_map = cpumask_of_cpu(0); +#endif + start_kernel(); + return(0); } -void halt_skas(void) +extern int userspace_pid[]; + +int start_uml_skas(void) { - block_signals(); - siglongjmp(initial_jmpbuf, 3); + if(proc_mm) + userspace_pid[0] = start_userspace(0); + + init_new_thread_signals(); + + init_task.thread.request.u.thread.proc = start_kernel_proc; + init_task.thread.request.u.thread.arg = NULL; + return(start_idle_thread(task_stack_page(&init_task), + &init_task.thread.mode.skas.switch_buf)); } -void reboot_skas(void) +int external_pid_skas(struct task_struct *task) { - block_signals(); - siglongjmp(initial_jmpbuf, 4); +#warning Need to look up userspace_pid by cpu + return(userspace_pid[0]); } -void switch_mm_skas(int mm_fd) +int thread_pid_skas(struct task_struct *task) { - int err; - -#warning need cpu pid in switch_mm_skas - err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_fd); - if(err) - panic("switch_mm_skas - PTRACE_SWITCH_MM failed, errno = %d\n", - errno); +#warning Need to look up userspace_pid by cpu + return(userspace_pid[0]); } void kill_off_processes_skas(void) { + if(proc_mm) #warning need to loop over userspace_pids in kill_off_processes_skas - os_kill_process(userspace_pid[0], 1); -} - -void init_registers(int pid) -{ - int err; + os_kill_ptraced_process(userspace_pid[0], 1); + else { + struct task_struct *p; + int pid, me; - if(ptrace(PTRACE_GETREGS, pid, 0, exec_regs) < 0) - panic("check_ptrace : PTRACE_GETREGS failed, errno = %d", - errno); + me = os_getpid(); + for_each_process(p){ + if(p->mm == NULL) + continue; - err = ptrace(PTRACE_GETFPXREGS, pid, 0, exec_fpx_regs); - if(!err) - return; + pid = p->mm->context.skas.id.u.pid; + os_kill_ptraced_process(pid, 1); + } + } +} - have_fpx_regs = 0; - if(errno != EIO) - panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", - errno); +unsigned long current_stub_stack(void) +{ + if(current->mm == NULL) + return(0); - err = ptrace(PTRACE_GETFPREGS, pid, 0, exec_fp_regs); - if(err) - panic("check_ptrace : PTRACE_GETFPREGS failed, errno = %d", - errno); + return(current->mm->context.skas.id.stack); } - -/* - * 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: - */