diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/kernel/execbuf.c | 38 | ||||
-rw-r--r-- | src/kernel/execbuf.h | 4 | ||||
-rw-r--r-- | src/kernel/proc.c | 11 | ||||
-rw-r--r-- | src/kernel/proc.h | 6 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 26 | ||||
-rw-r--r-- | src/shared/execbuf.h | 5 | ||||
-rw-r--r-- | src/shared/syscalls.h | 5 | ||||
-rw-r--r-- | src/user/lib/syscall.c | 4 | ||||
-rw-r--r-- | src/user/tests/main.c | 15 |
9 files changed, 112 insertions, 2 deletions
diff --git a/src/kernel/execbuf.c b/src/kernel/execbuf.c new file mode 100644 index 0000000..6136d0e --- /dev/null +++ b/src/kernel/execbuf.c @@ -0,0 +1,38 @@ +#include <kernel/execbuf.h> +#include <kernel/mem/alloc.h> +#include <kernel/panic.h> +#include <shared/execbuf.h> +#include <shared/mem.h> + +_Noreturn static void halt(struct process *proc) { + kfree(proc->execbuf.buf); + proc->execbuf.buf = NULL; + process_switch_any(); +} + +static void try_fetch(struct process *proc, uint64_t *buf, size_t amt) { + size_t bytes = amt * sizeof(uint64_t); + if (proc->execbuf.pos + bytes > proc->execbuf.len) + halt(proc); + memcpy(buf, proc->execbuf.buf + proc->execbuf.pos, bytes); + proc->execbuf.pos += bytes; +} + +_Noreturn void execbuf_run(struct process *proc) { + uint64_t buf[5]; + assert(proc == process_current); // idiotic, but needed because of _syscall. + assert(proc->state == PS_RUNNING); + assert(proc->execbuf.buf); + + for (;;) { + try_fetch(proc, buf, 1); + switch (buf[0]) { + case EXECBUF_SYSCALL: + try_fetch(proc, buf, 5); + _syscall(buf[0], buf[1], buf[2], buf[3], buf[4]); + break; + default: + halt(proc); + } + } +} diff --git a/src/kernel/execbuf.h b/src/kernel/execbuf.h new file mode 100644 index 0000000..02ada92 --- /dev/null +++ b/src/kernel/execbuf.h @@ -0,0 +1,4 @@ +#pragma once +#include <kernel/proc.h> + +_Noreturn void execbuf_run(struct process *proc); diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 324f0b4..9f6f6b7 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -1,4 +1,5 @@ #include <kernel/arch/generic.h> +#include <kernel/execbuf.h> #include <kernel/main.h> #include <kernel/mem/alloc.h> #include <kernel/mem/virt.h> @@ -135,6 +136,11 @@ void process_kill(struct process *p, int ret) { process_transition(p, PS_DEAD); p->death_msg = ret; + if (p->execbuf.buf) { + kfree(p->execbuf.buf); + p->execbuf.buf = NULL; + } + if (p->parent) pagedir_free(p->pages); // TODO put init's pages in the allocator @@ -199,7 +205,10 @@ static _Noreturn void process_switch(struct process *proc) { assert(proc->state == PS_RUNNING); process_current = proc; pagedir_switch(proc->pages); - sysexit(proc->regs); + if (proc->execbuf.buf) + execbuf_run(proc); + else + sysexit(proc->regs); } _Noreturn void process_switch_any(void) { diff --git a/src/kernel/proc.h b/src/kernel/proc.h index c400f7d..c642a42 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -57,6 +57,12 @@ struct process { struct vfs_mount *mount; + struct { + void *buf; + size_t len; + size_t pos; + } execbuf; + struct handle *handles[HANDLE_MAX]; }; diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 786578a..a2ae5dd 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -361,6 +361,25 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags) { SYSCALL_RETURN(0); } +long _syscall_execbuf(void __user *ubuf, size_t len) { + if (len == 0) SYSCALL_RETURN(0); + if (len > sizeof(uint64_t) * 6 * 4) // TODO specify max size somewhere + SYSCALL_RETURN(-1); + if (process_current->execbuf.buf) + SYSCALL_RETURN(-1); /* no nesting */ + // actually TODO, nesting makes sense for infinite loops. maybe + + char *kbuf = kmalloc(len); + if (!virt_cpy_from(process_current->pages, kbuf, ubuf, len)) { + kfree(kbuf); + SYSCALL_RETURN(-1); + } + process_current->execbuf.buf = kbuf; + process_current->execbuf.len = len; + process_current->execbuf.pos = 0; + SYSCALL_RETURN(0); +} + void _syscall_debug_klog(const void __user *buf, size_t len) { (void)buf; (void)len; // static char kbuf[256]; @@ -371,10 +390,12 @@ void _syscall_debug_klog(const void __user *buf, size_t len) { } long _syscall(long num, long a, long b, long c, long d) { + /* note: this isn't the only place where syscalls get called from! + * see execbuf */ switch (num) { case _SYSCALL_EXIT: _syscall_exit(a); - // _syscall_exit doesn't exit + // _syscall_exit doesn't return case _SYSCALL_AWAIT: _syscall_await(); break; @@ -411,6 +432,9 @@ long _syscall(long num, long a, long b, long c, long d) { case _SYSCALL_PIPE: _syscall_pipe((userptr_t)a, b); break; + case _SYSCALL_EXECBUF: + _syscall_execbuf((userptr_t)a, b); + break; case _SYSCALL_DEBUG_KLOG: _syscall_debug_klog((userptr_t)a, b); break; diff --git a/src/shared/execbuf.h b/src/shared/execbuf.h new file mode 100644 index 0000000..70ea35c --- /dev/null +++ b/src/shared/execbuf.h @@ -0,0 +1,5 @@ +#pragma once +/* the instruction format is bound to change, atm it's extremely inefficient */ + +/* takes 5 arguments */ +#define EXECBUF_SYSCALL 0xF0000001 diff --git a/src/shared/syscalls.h b/src/shared/syscalls.h index 2b8bd14..e3b879e 100644 --- a/src/shared/syscalls.h +++ b/src/shared/syscalls.h @@ -28,6 +28,8 @@ enum { _SYSCALL_MEMFLAG, _SYSCALL_PIPE, + _SYSCALL_EXECBUF, + _SYSCALL_DEBUG_KLOG, }; @@ -85,4 +87,7 @@ long _syscall_fs_respond(void __user *buf, long ret, int flags); void __user *_syscall_memflag(void __user *addr, size_t len, int flags); long _syscall_pipe(handle_t __user user_ends[2], int flags); +/* see shared/execbuf.h */ +long _syscall_execbuf(void __user *buf, size_t len); + void _syscall_debug_klog(const void __user *buf, size_t len); diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c index 23b82f7..e105638 100644 --- a/src/user/lib/syscall.c +++ b/src/user/lib/syscall.c @@ -58,6 +58,10 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags) { return _syscall(_SYSCALL_PIPE, (long)user_ends, (long)flags, 0, 0); } +long _syscall_execbuf(void __user *buf, size_t len) { + return _syscall(_SYSCALL_EXECBUF, (long)buf, (long)len, 0, 0); +} + void _syscall_debug_klog(const void __user *buf, size_t len) { return (void)_syscall(_SYSCALL_DEBUG_KLOG, (long)buf, (long)len, 0, 0); } diff --git a/src/user/tests/main.c b/src/user/tests/main.c index ab6b4a8..97714e7 100644 --- a/src/user/tests/main.c +++ b/src/user/tests/main.c @@ -1,5 +1,6 @@ #define TEST_MACROS #include <shared/errno.h> +#include <shared/execbuf.h> #include <shared/flags.h> #include <shared/syscalls.h> #include <user/lib/stdlib.h> @@ -221,6 +222,19 @@ static void test_efault(void) { close(h); } +static void test_execbuf(void) { + // not really a test, TODO + const char str1[] = "test_execbuf: string 1\n"; + const char str2[] = "test_execbuf: and 2\n"; + uint64_t buf[] = { + EXECBUF_SYSCALL, _SYSCALL_WRITE, 1, (uintptr_t)str1, sizeof(str1) - 1, -1, + EXECBUF_SYSCALL, _SYSCALL_WRITE, 1, (uintptr_t)str2, sizeof(str2) - 1, -1, + EXECBUF_SYSCALL, _SYSCALL_EXIT, 0, 0, 0, 0, + }; + _syscall_execbuf(buf, sizeof buf); + test_fail(); +} + static void test_misc(void) { assert(_syscall(~0, 0, 0, 0, 0) < 0); /* try making an invalid syscall */ } @@ -237,5 +251,6 @@ void test_all(void) { run_forked(test_pipe); run_forked(test_semaphore); run_forked(test_efault); + run_forked(test_execbuf); run_forked(test_misc); } |