diff options
-rw-r--r-- | src/init/syscalls.c | 2 | ||||
-rw-r--r-- | src/init/tests/main.c | 20 | ||||
-rw-r--r-- | src/kernel/arch/generic.h | 2 | ||||
-rw-r--r-- | src/kernel/arch/i386/pagedir.c | 20 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 29 | ||||
-rw-r--r-- | src/shared/flags.h | 1 | ||||
-rw-r--r-- | src/shared/syscalls.h | 11 |
7 files changed, 69 insertions, 16 deletions
diff --git a/src/init/syscalls.c b/src/init/syscalls.c index ed7420e..7c3664c 100644 --- a/src/init/syscalls.c +++ b/src/init/syscalls.c @@ -46,7 +46,7 @@ int _syscall_fs_respond(char __user *buf, int ret) { return _syscall(_SYSCALL_FS_RESPOND, (int)buf, ret, 0, 0); } -int _syscall_memflag(void __user *addr, size_t len, int flags) { +void __user *_syscall_memflag(void __user *addr, size_t len, int flags) { return _syscall(_SYSCALL_MEMFLAG, (int)addr, (int)len, flags, 0); } diff --git a/src/init/tests/main.c b/src/init/tests/main.c index 29f6c61..d895289 100644 --- a/src/init/tests/main.c +++ b/src/init/tests/main.c @@ -105,13 +105,25 @@ static void test_memflag(void) { assert(_syscall_await() != 0); // test if the process crashed } - _syscall_memflag((void*)0x100000, 4096, 0); // try to free kernel memory - // TODO the kernel shouldn't even be mapped in userland + char *pages[4]; + for (size_t i = 0; i < 4; i++) { + pages[i] = _syscall_memflag(NULL, 4096, MEMFLAG_FINDFREE); + printf("[test_memflag] findfree: 0x%x\n", pages[i]); + + assert(pages[i] == _syscall_memflag(NULL, 4096, MEMFLAG_FINDFREE | MEMFLAG_PRESENT)); + if (i > 0) assert(pages[i] != pages[i-1]); + *pages[i] = 0x77; + } + + for (size_t i = 0; i < 4; i++) + _syscall_memflag(pages, 4096, MEMFLAG_PRESENT); + + // TODO check if reclaims } static void test_malloc(void) { // not really a test - void *p1, *p2, *p3; + void *p1, *p2; p1 = malloc(420); printf("p1 = 0x%x\n", p1); @@ -145,5 +157,5 @@ void test_all(void) { run_forked(test_orphaned_fs); run_forked(test_memflag); run_forked(test_malloc); -// run_forked(stress_fork); + run_forked(stress_fork); } diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h index 3e98001..a9e1ac9 100644 --- a/src/kernel/arch/generic.h +++ b/src/kernel/arch/generic.h @@ -35,6 +35,8 @@ void pagedir_map(struct pagedir *dir, void __user *virt, void *phys, bool user, bool writeable); bool pagedir_iskern(struct pagedir *, const void __user *virt); +void __user *pagedir_findfree(struct pagedir *dir, char __user *start, size_t len); + void pagedir_switch(struct pagedir *); // return 0 on failure diff --git a/src/kernel/arch/i386/pagedir.c b/src/kernel/arch/i386/pagedir.c index 28608dc..e607cc7 100644 --- a/src/kernel/arch/i386/pagedir.c +++ b/src/kernel/arch/i386/pagedir.c @@ -184,3 +184,23 @@ void *pagedir_virt2phys(struct pagedir *dir, const void __user *virt, phys |= ((uintptr_t)virt) & 0xFFF; return (void*)phys; } + +void __user *pagedir_findfree(struct pagedir *dir, char __user *start, size_t len) { + struct pagetable_entry *page; + char __user *iter; + start = (userptr_t)(((uintptr_t __force)start + PAGE_MASK) & ~PAGE_MASK); // round up to next page + iter = start; + + while (iter < (char __user *)0xFFF00000) { // TODO better boundary + page = get_entry(dir, iter); + if (page && page->present) { + start = iter + PAGE_SIZE; + } else { + if ((size_t)(iter + PAGE_SIZE - start) >= len) + return start; + } + iter += PAGE_SIZE; + } + + return NULL; +} diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 4d64d32..540611e 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -250,34 +250,42 @@ int _syscall_fs_respond(char __user *buf, int ret) { SYSCALL_RETURN(0); } -int _syscall_memflag(void __user *addr, size_t len, int flags) { - userptr_t goal = addr + len; +void __user *_syscall_memflag(void __user *addr, size_t len, int flags) { struct pagedir *pages = process_current->pages; void *phys; - addr = (userptr_t)((int __force)addr & ~PAGE_MASK); // align to page boundary - for (; addr < goal; addr += PAGE_SIZE) { - if (pagedir_iskern(pages, addr)) { + + if (flags & MEMFLAG_FINDFREE) { + addr = pagedir_findfree(pages, addr, len); + if (!(flags & MEMFLAG_PRESENT)) goto ret; + } + + + for (userptr_t iter = addr; iter < addr + len; iter += PAGE_SIZE) { + if (pagedir_iskern(pages, iter)) { // TODO reflect failure in return value continue; } - phys = pagedir_virt2phys(pages, addr, false, false); + phys = pagedir_virt2phys(pages, iter, false, false); if (!(flags & MEMFLAG_PRESENT)) { if (phys) - page_free(pagedir_unmap(pages, addr), 1); + page_free(pagedir_unmap(pages, iter), 1); continue; } if (!phys) { phys = page_alloc(1); memset(phys, 0, PAGE_SIZE); // TODO somehow test this - pagedir_map(pages, addr, phys, true, true); + pagedir_map(pages, iter, phys, true, true); } } - SYSCALL_RETURN(-1); +ret: // the macro is too stupid to handle returning pointers + assert(process_current->state == PS_RUNNING); // TODO move to regs_savereturn + regs_savereturn(&process_current->regs, (uintptr_t)addr); + return addr; } int _syscall(int num, int a, int b, int c, int d) { @@ -303,7 +311,8 @@ int _syscall(int num, int a, int b, int c, int d) { case _SYSCALL_FS_RESPOND: return _syscall_fs_respond((userptr_t)a, b); case _SYSCALL_MEMFLAG: - return _syscall_memflag((userptr_t)a, b, c); + _syscall_memflag((userptr_t)a, b, c); + return -1; // unused anyways default: kprintf("unknown syscall "); panic_unimplemented(); // TODO fail gracefully diff --git a/src/shared/flags.h b/src/shared/flags.h index 9ab8d9d..dd20a3f 100644 --- a/src/shared/flags.h +++ b/src/shared/flags.h @@ -2,4 +2,5 @@ enum { MEMFLAG_PRESENT = 1 << 0, + MEMFLAG_FINDFREE = 1 << 1, }; diff --git a/src/shared/syscalls.h b/src/shared/syscalls.h index 8dee783..5d98429 100644 --- a/src/shared/syscalls.h +++ b/src/shared/syscalls.h @@ -66,4 +66,13 @@ struct fs_wait_response { int _syscall_fs_wait(char __user *buf, int max_len, struct fs_wait_response __user *res); int _syscall_fs_respond(char __user *buf, int ret); -int _syscall_memflag(void __user *addr, size_t len, int flags); +/** Modifies the virtual address space. + * + * If the MEMFLAG_PRESENT flag is present - mark the memory region as allocated. + * Otherwise, free it. + * + * MEMFLAG_FINDFREE tries to find the first free region of length `len`. + * + * @return address of the first affected page (usually == addr) + */ +void __user *_syscall_memflag(void __user *addr, size_t len, int flags); |