diff options
Diffstat (limited to 'src/user/lib')
-rw-r--r-- | src/user/lib/esemaphore.c | 55 | ||||
-rw-r--r-- | src/user/lib/esemaphore.h | 12 | ||||
-rw-r--r-- | src/user/lib/malloc.c | 65 | ||||
-rw-r--r-- | src/user/lib/malloc.h | 5 | ||||
-rw-r--r-- | src/user/lib/stdlib.c | 120 | ||||
-rw-r--r-- | src/user/lib/stdlib.h | 23 | ||||
-rw-r--r-- | src/user/lib/syscall.c | 64 | ||||
-rw-r--r-- | src/user/lib/syscall.c.awk | 51 | ||||
-rw-r--r-- | src/user/lib/syscall.s | 24 |
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 |