diff options
Diffstat (limited to 'kern/usermem.c')
| -rw-r--r-- | kern/usermem.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/kern/usermem.c b/kern/usermem.c new file mode 100644 index 0000000..717cdbe --- /dev/null +++ b/kern/usermem.c @@ -0,0 +1,153 @@ +/** @file usermem.c + * + * @brief userspace r/w functions. + * + * @author Hanjie Wu (hanjiew) + * @bug No functional bugs + */ + +#include <stdlib.h> +#include <string.h> + +#include <common_kern.h> +#include <x86/asm.h> + +#include <console.h> +#include <mm.h> +#include <paging.h> +#include <sched.h> + +/** + * @brief try to read a byte from userspace + * @param addr address + * @param output output byte if successful + * @return 0 on success, jump to usermem_fail on failure + */ +int try_read(va_t addr, char* output); +/** + * @brief try to write a byte to userspace + * @param addr address + * @param c byte to write + * @return 0 on success, jump to usermem_fail on failure + */ +int try_write(va_t addr, char c); + +/** + * @brief tell the calling function the r/w failed + * @return -1 + */ +int usermem_fail(); + +/** + * @brief setup fault handler before r/w + * @return old fault handler + */ +static reg_t usermem_setup() { + thread_t* current = get_current(); + reg_t old_eip0 = current->eip0; + current->eip0 = (reg_t)usermem_fail; + return old_eip0; +} + +/** + * @brief restore old fault handler + * @param old_eip0 old fault handler + */ +static void usermem_finish(reg_t old_eip0) { + get_current()->eip0 = old_eip0; +} + +int copy_from_user(va_t addr, int size, void* buf) { + va_t end = addr + size; + if (addr < USER_MEM_START || end < addr || end >= DEFAULT_STACK_END) { + return -1; + } + reg_t old_eip0 = usermem_setup(); + int i, result = 0; + for (i = 0; i < size; i++) { + if (try_read(addr + i, (char*)buf + i) != 0) { + result = -1; + break; + } + } + usermem_finish(old_eip0); + return result; +} + +int copy_to_user(va_t addr, int size, void* buf) { + va_t end = addr + size; + if (addr < USER_MEM_START || end < addr || end >= DEFAULT_STACK_END) { + return -1; + } + reg_t old_eip0 = usermem_setup(); + int i, result = 0; + for (i = 0; i < size; i++) { + if (try_write(addr + i, *((char*)buf + i)) != 0) { + result = -1; + break; + } + } + usermem_finish(old_eip0); + return result; +} + +char* copy_string_from_user(va_t addr, int maxlen) { + if (addr < USER_MEM_START || addr >= DEFAULT_STACK_END) { + return NULL; + } + if (DEFAULT_STACK_END - addr < maxlen) { + maxlen = DEFAULT_STACK_END - addr; + } + reg_t old_eip0 = usermem_setup(); + int len = 0, buflen = 3 * sizeof(int); + char* buf = malloc(buflen); + char c; + if (buf == NULL) { + goto read_string_finished; + } + while (1) { + if (len >= maxlen || try_read(addr + len, &c) != 0) { + free(buf); + buf = NULL; + break; + } + + buf[len++] = c; + if (c == '\0') { + break; + } + + if (len >= buflen) { + buflen <<= 1; + char* result = realloc(buf, buflen); + if (result == NULL) { + free(buf); + buf = NULL; + break; + } + buf = result; + } + } +read_string_finished: + usermem_finish(old_eip0); + return buf; +} + +int print_buf_from_user(va_t addr, int len) { + va_t end = addr + len; + if (addr < USER_MEM_START || end < addr || end >= DEFAULT_STACK_END) { + return -1; + } + reg_t old_eip0 = usermem_setup(); + int i, result = 0; + char c; + for (i = 0; i < len; i++) { + if (try_read(addr + i, &c) != 0) { + result = -1; + break; + } + putbyte(c); + } + usermem_finish(old_eip0); + return result; +} |
