summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordzwdz2022-07-18 23:55:58 +0200
committerdzwdz2022-07-18 23:55:58 +0200
commit96f41ff64d8113307f1b60d2eb6852423db34d14 (patch)
tree5a85a9706a2a11d047f7f5c91f4964bd2167b1ee /src
parent121794214fd5ae36609c30418dfaf1a073b8784c (diff)
syscalls: implement execbuf
i have been planning to implement something like this for a while now. it should be faster when doing consecutive syscalls (to be tested). it will also be helpful in writing the elf loader
Diffstat (limited to 'src')
-rw-r--r--src/kernel/execbuf.c38
-rw-r--r--src/kernel/execbuf.h4
-rw-r--r--src/kernel/proc.c11
-rw-r--r--src/kernel/proc.h6
-rw-r--r--src/kernel/syscalls.c26
-rw-r--r--src/shared/execbuf.h5
-rw-r--r--src/shared/syscalls.h5
-rw-r--r--src/user/lib/syscall.c4
-rw-r--r--src/user/tests/main.c15
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);
}