summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/init/syscalls.c2
-rw-r--r--src/init/tests/main.c20
-rw-r--r--src/kernel/arch/generic.h2
-rw-r--r--src/kernel/arch/i386/pagedir.c20
-rw-r--r--src/kernel/syscalls.c29
-rw-r--r--src/shared/flags.h1
-rw-r--r--src/shared/syscalls.h11
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);