summaryrefslogtreecommitdiff
path: root/src/user/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/user/lib')
-rw-r--r--src/user/lib/esemaphore.c55
-rw-r--r--src/user/lib/esemaphore.h12
-rw-r--r--src/user/lib/malloc.c65
-rw-r--r--src/user/lib/malloc.h5
-rw-r--r--src/user/lib/stdlib.c120
-rw-r--r--src/user/lib/stdlib.h23
-rw-r--r--src/user/lib/syscall.c64
-rw-r--r--src/user/lib/syscall.c.awk51
-rw-r--r--src/user/lib/syscall.s24
9 files changed, 419 insertions, 0 deletions
diff --git a/src/user/lib/esemaphore.c b/src/user/lib/esemaphore.c
new file mode 100644
index 0000000..ed42ee9
--- /dev/null
+++ b/src/user/lib/esemaphore.c
@@ -0,0 +1,55 @@
+#include <user/lib/esemaphore.h>
+#include <user/lib/stdlib.h>
+#include <shared/flags.h>
+#include <shared/syscalls.h>
+
+void esem_signal(struct evil_sem *sem) {
+ _syscall_write(sem->signal, NULL, 0, 0);
+}
+
+void esem_wait(struct evil_sem *sem) {
+ _syscall_read(sem->wait, NULL, 0, 0);
+}
+
+struct evil_sem *esem_new(int value) {
+ handle_t ends_wait[2], ends_signal[2];
+ struct evil_sem *sem;
+
+ if (value < 0) return NULL;
+ if (_syscall_pipe(ends_wait, 0) < 0) return NULL;
+ if (_syscall_pipe(ends_signal, 0) < 0) goto fail_signal;
+ if (!(sem = malloc(sizeof *sem))) goto fail_malloc;
+
+ if (!_syscall_fork(FORK_NOREAP, NULL)) {
+ _syscall_close(ends_signal[1]);
+ while (_syscall_read(ends_signal[0], NULL, 0, 0) >= 0) {
+ if (!_syscall_fork(FORK_NOREAP, NULL)) {
+ _syscall_write(ends_wait[1], NULL, 0, 0);
+ _syscall_exit(0);
+ }
+ }
+ _syscall_exit(0);
+ }
+ _syscall_close(ends_signal[0]);
+ _syscall_close(ends_wait[1]);
+
+ sem->wait = ends_wait[0];
+ sem->signal = ends_signal[1];
+
+ while (value--) esem_signal(sem);
+ return sem;
+
+fail_malloc:
+ _syscall_close(ends_signal[0]);
+ _syscall_close(ends_signal[1]);
+fail_signal:
+ _syscall_close(ends_wait[0]);
+ _syscall_close(ends_wait[1]);
+ return NULL;
+}
+
+void esem_free(struct evil_sem *sem) {
+ _syscall_close(sem->wait);
+ _syscall_close(sem->signal);
+ free(sem);
+}
diff --git a/src/user/lib/esemaphore.h b/src/user/lib/esemaphore.h
new file mode 100644
index 0000000..e746bd7
--- /dev/null
+++ b/src/user/lib/esemaphore.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <shared/types.h>
+
+struct evil_sem {
+ handle_t wait, signal;
+};
+
+void esem_signal(struct evil_sem *sem);
+void esem_wait(struct evil_sem *sem);
+
+struct evil_sem *esem_new(int value);
+void esem_free(struct evil_sem *sem);
diff --git a/src/user/lib/malloc.c b/src/user/lib/malloc.c
new file mode 100644
index 0000000..5157e91
--- /dev/null
+++ b/src/user/lib/malloc.c
@@ -0,0 +1,65 @@
+#include <user/lib/malloc.h>
+#include <shared/flags.h>
+#include <shared/syscalls.h>
+#include <stdbool.h>
+
+#include <user/lib/stdlib.h>
+
+#define MBLOCK_MAGIC 0x1337BABE
+
+struct mblock {
+ uint32_t magic;
+ size_t length; // including this struct
+ bool used;
+ struct mblock *next;
+};
+
+static struct mblock *first = NULL, *last = NULL;
+static struct mblock *expand(size_t size);
+
+void *malloc(size_t size) {
+ struct mblock *iter = first;
+ size += sizeof(struct mblock);
+ while (iter) {
+ if (!iter->used && iter->length >= size)
+ break;
+ iter = iter->next;
+ }
+
+ if (!iter) iter = expand(size);
+ if (!iter) return NULL;
+
+ iter->used = true;
+ // TODO truncate and split
+
+ return &iter[1];
+}
+
+void free(void *ptr) {
+ struct mblock *block = ptr - sizeof(struct mblock);
+ if (block->magic != MBLOCK_MAGIC) {
+ // TODO debug log switch
+ printf("didn't find MBLOCK_MAGIC @ 0x%x\n", block);
+ return;
+ }
+
+ block->used = false;
+}
+
+static struct mblock *expand(size_t size) {
+ struct mblock *block = _syscall_memflag(0, size, MEMFLAG_PRESENT | MEMFLAG_FINDFREE);
+ if (!block) return NULL;
+
+ block->magic = MBLOCK_MAGIC;
+ block->length = size;
+ block->used = false;
+ block->next = NULL;
+
+ if (!first) first = block;
+ if (last) last->next = block;
+ last = block;
+
+ // TODO collapse
+
+ return block;
+}
diff --git a/src/user/lib/malloc.h b/src/user/lib/malloc.h
new file mode 100644
index 0000000..5916ebc
--- /dev/null
+++ b/src/user/lib/malloc.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <stddef.h>
+
+void *malloc(size_t size);
+void free(void *ptr);
diff --git a/src/user/lib/stdlib.c b/src/user/lib/stdlib.c
new file mode 100644
index 0000000..c055d04
--- /dev/null
+++ b/src/user/lib/stdlib.c
@@ -0,0 +1,120 @@
+#include <user/lib/stdlib.h>
+#include <shared/printf.h>
+#include <shared/syscalls.h>
+
+// TODO oh god this garbage - malloc, actually open, [...]
+static libc_file _stdin_null = { .fd = 0 };
+static libc_file _stdout_null = { .fd = 1 };
+
+libc_file *stdin = &_stdin_null, *stdout = &_stdout_null;
+
+static void backend_file(void *arg, const char *buf, size_t len) {
+ file_write((libc_file*)arg, buf, len);
+}
+
+int printf(const char *fmt, ...) {
+ int ret = 0;
+ va_list argp;
+ va_start(argp, fmt);
+ ret = __printf_internal(fmt, argp, backend_file, (void*)stdout);
+ va_end(argp);
+ return ret;
+}
+
+static void backend_buf(void *arg, const char *buf, size_t len) {
+ char **ptrs = arg;
+ size_t space = ptrs[1] - ptrs[0];
+ if (len > space) len = space;
+ memcpy(ptrs[0], buf, len);
+ ptrs[0] += len;
+}
+
+int snprintf(char *str, size_t len, const char *fmt, ...) {
+ int ret = 0;
+ char *ptrs[2] = {str, str + len};
+ va_list argp;
+ va_start(argp, fmt);
+ ret = __printf_internal(fmt, argp, backend_buf, &ptrs);
+ va_end(argp);
+ if (ptrs[0] < ptrs[1]) *ptrs[0] = '\0';
+ return ret;
+}
+
+int _klogf(const char *fmt, ...) {
+ // idiotic. however, this hack won't matter anyways
+ char buf[256];
+ int ret = 0;
+ char *ptrs[2] = {buf, buf + sizeof buf};
+ va_list argp;
+ va_start(argp, fmt);
+ ret = __printf_internal(fmt, argp, backend_buf, &ptrs);
+ va_end(argp);
+ if (ptrs[0] < ptrs[1]) *ptrs[0] = '\0';
+ _syscall_debug_klog(buf, ret);
+ return ret;
+}
+
+
+libc_file *file_open(const char *path, int flags) {
+ handle_t h = _syscall_open(path, strlen(path), flags);
+ libc_file *f;
+ if (h < 0) return NULL;
+
+ f = malloc(sizeof *f);
+ if (!f) {
+ _syscall_close(h);
+ return NULL;
+ }
+ f->pos = 0;
+ f->eof = false;
+ f->fd = h;
+ return f;
+}
+
+libc_file *file_reopen(libc_file *f, const char *path, int flags) {
+ /* partially based on the musl implementation of freopen */
+ libc_file *f2;
+ if (!path) goto fail;
+ f2 = file_open(path, flags);
+ if (!f2) goto fail;
+
+ /* shouldn't happen, but if it happens, let's roll with it. */
+ if (f->fd == f2->fd) f2->fd = -1;
+
+ if (_syscall_dup(f2->fd, f->fd, 0) < 0) goto fail2;
+ f->pos = f2->pos;
+ f->eof = f2->eof;
+ file_close(f2);
+ return f;
+
+fail2:
+ file_close(f2);
+fail:
+ file_close(f);
+ return NULL;
+}
+
+int file_read(libc_file *f, char *buf, size_t len) {
+ if (f->fd < 0) return -1;
+
+ int res = _syscall_read(f->fd, buf, len, f->pos);
+ if (res < 0) return res;
+ if (res == 0 && len > 0) f->eof = true;
+ f->pos += res;
+ return res;
+}
+
+int file_write(libc_file *f, const char *buf, size_t len) {
+ if (f->fd < 0) return -1;
+
+ int res = _syscall_write(f->fd, buf, len, f->pos);
+ if (res < 0) return res;
+ f->pos += res;
+ return res;
+}
+
+void file_close(libc_file *f) {
+ if (f->fd > 0) _syscall_close(f->fd);
+ if (f != &_stdin_null && f != &_stdout_null)
+ free(f);
+}
diff --git a/src/user/lib/stdlib.h b/src/user/lib/stdlib.h
new file mode 100644
index 0000000..9bbcc5f
--- /dev/null
+++ b/src/user/lib/stdlib.h
@@ -0,0 +1,23 @@
+#pragma once
+#include <user/lib/malloc.h>
+#include <shared/mem.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+int printf(const char *fmt, ...);
+int snprintf(char *str, size_t len, const char *fmt, ...);
+
+int _klogf(const char *fmt, ...); // for kernel debugging only
+
+typedef struct {
+ int fd;
+ int pos;
+ bool eof;
+} libc_file;
+libc_file *file_open(const char *path, int flags);
+libc_file *file_reopen(libc_file*, const char *path, int flags);
+int file_read(libc_file*, char *buf, size_t len);
+int file_write(libc_file*, const char *buf, size_t len);
+void file_close(libc_file*);
+
+extern libc_file *stdin, *stdout;
diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c
new file mode 100644
index 0000000..d86df19
--- /dev/null
+++ b/src/user/lib/syscall.c
@@ -0,0 +1,64 @@
+/* generated by syscall.c.awk
+ * don't modify manually, instead run:
+ * make src/user/lib/syscall.c
+ */
+#include <shared/syscalls.h>
+
+
+_Noreturn void _syscall_exit(int ret) {
+ _syscall(_SYSCALL_EXIT, ret, 0, 0, 0);
+ __builtin_unreachable();
+}
+
+int _syscall_await(void) {
+ return _syscall(_SYSCALL_AWAIT, 0, 0, 0, 0);
+}
+
+int _syscall_fork(int flags, handle_t __user *fs_front) {
+ return _syscall(_SYSCALL_FORK, flags, (int)fs_front, 0, 0);
+}
+
+handle_t _syscall_open(const char __user *path, int len, int flags) {
+ return (handle_t)_syscall(_SYSCALL_OPEN, (int)path, len, flags, 0);
+}
+
+int _syscall_mount(handle_t h, const char __user *path, int len) {
+ return _syscall(_SYSCALL_MOUNT, (int)h, (int)path, len, 0);
+}
+
+handle_t _syscall_dup(handle_t from, handle_t to, int flags) {
+ return (handle_t)_syscall(_SYSCALL_DUP, (int)from, (int)to, flags, 0);
+}
+
+int _syscall_read(handle_t h, void __user *buf, size_t len, int offset) {
+ return _syscall(_SYSCALL_READ, (int)h, (int)buf, (int)len, offset);
+}
+
+int _syscall_write(handle_t h, const void __user *buf, size_t len, int offset) {
+ return _syscall(_SYSCALL_WRITE, (int)h, (int)buf, (int)len, offset);
+}
+
+int _syscall_close(handle_t h) {
+ return _syscall(_SYSCALL_CLOSE, (int)h, 0, 0, 0);
+}
+
+int _syscall_fs_wait(char __user *buf, int max_len, struct fs_wait_response __user *res) {
+ return _syscall(_SYSCALL_FS_WAIT, (int)buf, max_len, (int)res, 0);
+}
+
+int _syscall_fs_respond(void __user *buf, int ret, int flags) {
+ return _syscall(_SYSCALL_FS_RESPOND, (int)buf, ret, flags, 0);
+}
+
+void __user *_syscall_memflag(void __user *addr, size_t len, int flags) {
+ return (void __user *)_syscall(_SYSCALL_MEMFLAG, (int)addr, (int)len, flags, 0);
+}
+
+int _syscall_pipe(handle_t __user user_ends[2], int flags) {
+ return _syscall(_SYSCALL_PIPE, (int)user_ends, flags, 0, 0);
+}
+
+void _syscall_debug_klog(const void __user *buf, size_t len) {
+ return (void)_syscall(_SYSCALL_DEBUG_KLOG, (int)buf, (int)len, 0, 0);
+}
+
diff --git a/src/user/lib/syscall.c.awk b/src/user/lib/syscall.c.awk
new file mode 100644
index 0000000..a8dd22b
--- /dev/null
+++ b/src/user/lib/syscall.c.awk
@@ -0,0 +1,51 @@
+BEGIN {
+ print "\
+/* generated by syscall.c.awk\n\
+ * don't modify manually, instead run:\n\
+ * make src/user/lib/syscall.c\n\
+ */\n\
+#include <shared/syscalls.h>\n\
+\n";
+}
+
+/_syscall\(/ { next; } # skipping _syscall(), it's implemented elsewhere
+
+/\);/ {
+ sub(/;/, " {");
+ print $0;
+
+ name = substr($0, match($0, /_syscall_[^(]+/), RLENGTH);
+ rets = substr($0, 0, RSTART - 1);
+ sub(/ *$/, "", rets)
+
+ params = substr($0, match($0, /\(.+\)/) + 1, RLENGTH - 2);
+ gsub(/\[[^\]]\]/, "", params);
+ if (params == "void") params = ""
+
+ split(params, p, /,/);
+ for (i = 0; i <= 4; i += 1) {
+ if (p[i]) {
+ # p[i] is a parameter, convert it into an expression to pass to _syscall()
+ sub(/^ */, "", p[i]); # strip
+ split(p[i], words, / /);
+ if (length(words) != 1) {
+ var = words[length(words)];
+ sub(/\*/, "", var);
+ if (words[1] != "int") var = "(int)" var;
+ }
+ p[i] = var;
+ } else {
+ p[i] = 0;
+ }
+ }
+
+ printf "\t";
+ if (!index($0, "_Noreturn")) {
+ printf "return ";
+ if (rets != "int") printf "(%s)", rets;
+ }
+ printf "_syscall(%s, %s, %s, %s, %s);\n", toupper(name), p[1], p[2], p[3], p[4];
+ if (index($0, "_Noreturn")) print "\t__builtin_unreachable();";
+
+ print "}\n";
+}
diff --git a/src/user/lib/syscall.s b/src/user/lib/syscall.s
new file mode 100644
index 0000000..0af49f3
--- /dev/null
+++ b/src/user/lib/syscall.s
@@ -0,0 +1,24 @@
+.section .text
+.global _syscall
+.type _syscall, @function
+_syscall:
+ push %ebx // preserve registers
+ push %esi
+ push %edi
+ push %ebp
+
+ mov 20(%esp), %eax
+ mov 24(%esp), %ebx
+ mov %esp, %ecx
+ mov $_syscall_ret, %edx
+ mov 28(%esp), %esi
+ mov 32(%esp), %edi
+ mov 36(%esp), %ebp
+ sysenter
+
+_syscall_ret:
+ pop %ebp
+ pop %edi
+ pop %esi
+ pop %ebx
+ ret