diff options
author | jdike <jdike> | 2004-04-07 20:45:13 +0000 |
---|---|---|
committer | jdike <jdike> | 2004-04-07 20:45:13 +0000 |
commit | 9b6d714a6b7112e77328ceade6b2e5d64a6db4dc (patch) | |
tree | ba302e3f4b1291b0130ac1a451a125117904e7af | |
parent | 4c71bbd87ef2ba262e8be1832765100eaa93828d (diff) | |
download | uml-history-9b6d714a6b7112e77328ceade6b2e5d64a6db4dc.tar.gz |
Fixed the kmem bug by handling kernel-space faults.
-rw-r--r-- | arch/um/include/user_util.h | 1 | ||||
-rw-r--r-- | arch/um/kernel/skas/uaccess.c | 57 | ||||
-rw-r--r-- | arch/um/kernel/user_util.c | 28 | ||||
-rw-r--r-- | drivers/char/mem.c | 5 |
4 files changed, 70 insertions, 21 deletions
diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h index a94c461..892691d 100644 --- a/arch/um/include/user_util.h +++ b/arch/um/include/user_util.h @@ -88,6 +88,7 @@ extern int arch_handle_signal(int sig, union uml_pt_regs *regs); extern int arch_fixup(unsigned long address, void *sc_ptr); extern int can_do_skas(void); extern void arch_init_thread(void); +extern int setjmp_wrapper(void (*proc)(void *, void *), ...); #endif diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c index 0720ed5..972cdf9 100644 --- a/arch/um/kernel/skas/uaccess.c +++ b/arch/um/kernel/skas/uaccess.c @@ -12,6 +12,7 @@ #include "asm/pgtable.h" #include "asm/uaccess.h" #include "kern_util.h" +#include "user_util.h" extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, pte_t *pte_out); @@ -49,37 +50,67 @@ static int do_op(unsigned long addr, int len, int is_write, return(n); } -static int buffer_op(unsigned long addr, int len, int is_write, - int (*op)(unsigned long addr, int len, void *arg), - void *arg) +static void do_buffer_op(void *jmpbuf, void *arg_ptr) { + va_list args = *((va_list *) arg_ptr); + unsigned long addr = va_arg(args, unsigned long); + int len = va_arg(args, int); + int is_write = va_arg(args, int); + int (*op)(unsigned long, int, void *) = va_arg(args, void *); + void *arg = va_arg(args, void *); + int *res = va_arg(args, int *); int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); int remain = len, n; + current->thread.fault_catcher = jmpbuf; n = do_op(addr, size, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); + if(n != 0){ + *res = (n < 0 ? remain : 0); + goto out; + } addr += size; remain -= size; - if(remain == 0) - return(0); + if(remain == 0){ + *res = 0; + goto out; + } while(addr < ((addr + remain) & PAGE_MASK)){ n = do_op(addr, PAGE_SIZE, is_write, op, arg); - if(n != 0) - return(n < 0 ? remain : 0); + if(n != 0){ + *res = (n < 0 ? remain : 0); + goto out; + } addr += PAGE_SIZE; remain -= PAGE_SIZE; } - if(remain == 0) - return(0); + if(remain == 0){ + *res = 0; + goto out; + } n = do_op(addr, remain, is_write, op, arg); if(n != 0) - return(n < 0 ? remain : 0); - return(0); + *res = (n < 0 ? remain : 0); + else *res = 0; + out: + current->thread.fault_catcher = NULL; +} + +static int buffer_op(unsigned long addr, int len, int is_write, + int (*op)(unsigned long addr, int len, void *arg), + void *arg) +{ + int faulted, res; + + faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg, + &res); + if(!faulted) + return(res); + + return(addr + len - (unsigned long) current->thread.fault_addr); } static int copy_chunk_from_user(unsigned long from, int len, void *arg) diff --git a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c index 41da13e..f3afe27 100644 --- a/arch/um/kernel/user_util.c +++ b/arch/um/kernel/user_util.c @@ -7,6 +7,7 @@ #include <stdlib.h> #include <unistd.h> #include <limits.h> +#include <setjmp.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/ptrace.h> @@ -89,11 +90,11 @@ int wait_for_stop(int pid, int sig, int cont_type, void *relay) errno); } else if(WIFEXITED(status)) - printk("process exited with status %d\n", - WEXITSTATUS(status)); + printk("process %d exited with status %d\n", + pid, WEXITSTATUS(status)); else if(WIFSIGNALED(status)) - printk("process exited with signal %d\n", - WTERMSIG(status)); + printk("process %d exited with signal %d\n", + pid, WTERMSIG(status)); else if((WSTOPSIG(status) == SIGVTALRM) || (WSTOPSIG(status) == SIGALRM) || (WSTOPSIG(status) == SIGIO) || @@ -109,8 +110,8 @@ int wait_for_stop(int pid, int sig, int cont_type, void *relay) ptrace(cont_type, pid, 0, WSTOPSIG(status)); continue; } - else printk("process stopped with signal %d\n", - WSTOPSIG(status)); + else printk("process %d stopped with signal %d\n", + pid, WSTOPSIG(status)); panic("wait_for_stop failed to wait for %d to stop " "with %d\n", pid, sig); } @@ -152,6 +153,21 @@ void setup_hostinfo(void) host.release, host.version, host.machine); } +int setjmp_wrapper(void (*proc)(void *, void *), ...) +{ + va_list args; + sigjmp_buf buf; + int n; + + n = sigsetjmp(buf, 1); + if(n == 0){ + va_start(args, proc); + (*proc)(&buf, &args); + } + va_end(args); + return(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 diff --git a/drivers/char/mem.c b/drivers/char/mem.c index bd5bef5..9b934ed 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -221,7 +221,6 @@ static ssize_t read_kmem(struct file *file, char *buf, ssize_t virtr = 0; char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ - p = (unsigned long) __va(p); if (p < (unsigned long) high_memory) { read = count; if (count > (unsigned long) high_memory - p) @@ -270,7 +269,7 @@ static ssize_t read_kmem(struct file *file, char *buf, } free_page((unsigned long)kbuf); } - *ppos = __pa((void *) p); + *ppos = p; return virtr + read; } @@ -293,6 +292,8 @@ static ssize_t write_kmem(struct file * file, const char * buf, wrote = (unsigned long) high_memory - p; wrote = do_write_mem(file, (void*)p, p, buf, wrote, ppos); + if(wrote < 0) + return(wrote); p += wrote; buf += wrote; |