From fbfc078e9f44c1c1e95c9c484f1d5650bcf631b7 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Sat, 27 Jul 2024 16:57:39 -0400 Subject: lots and lots of userspace stuff --- euler/source/entry.cpp | 28 +++ euler/source/euler/entry.cpp | 11 - euler/source/euler/gcc.asm | 53 ----- euler/source/euler/heap.cpp | 66 ------ euler/source/euler/start_process.cpp | 35 --- euler/source/euler/stream.cpp | 151 ------------- euler/source/euler/syscall.asm | 144 ------------- euler/source/heap.cpp | 155 ++++++++++++++ euler/source/io/fclose.cpp | 10 - euler/source/io/fopen.cpp | 54 ----- euler/source/io/fread.cpp | 9 - euler/source/io/fseek.cpp | 11 - euler/source/memory/delete.cpp | 17 -- euler/source/memory/new.cpp | 13 -- euler/source/std/cctype.cpp | 7 + euler/source/std/cstdio.cpp | 59 ++++++ euler/source/std/cstdlib.cpp | 19 ++ euler/source/std/cstring.cpp | 36 ++++ euler/source/std/string.cpp | 86 ++++++++ euler/source/stream.cpp | 106 ++++++++++ euler/source/strings/memcpy.cpp | 14 -- euler/source/strings/strlen.cpp | 12 -- euler/source/syscall.asm | 32 +++ euler/source/syscall.cpp | 397 +++++++++++++++++++++++++++++++++++ 24 files changed, 925 insertions(+), 600 deletions(-) create mode 100644 euler/source/entry.cpp delete mode 100644 euler/source/euler/entry.cpp delete mode 100644 euler/source/euler/gcc.asm delete mode 100644 euler/source/euler/heap.cpp delete mode 100644 euler/source/euler/start_process.cpp delete mode 100644 euler/source/euler/stream.cpp delete mode 100644 euler/source/euler/syscall.asm create mode 100644 euler/source/heap.cpp delete mode 100644 euler/source/io/fclose.cpp delete mode 100644 euler/source/io/fopen.cpp delete mode 100644 euler/source/io/fread.cpp delete mode 100644 euler/source/io/fseek.cpp delete mode 100644 euler/source/memory/delete.cpp delete mode 100644 euler/source/memory/new.cpp create mode 100644 euler/source/std/cctype.cpp create mode 100644 euler/source/std/cstdio.cpp create mode 100644 euler/source/std/cstdlib.cpp create mode 100644 euler/source/std/cstring.cpp create mode 100644 euler/source/std/string.cpp create mode 100644 euler/source/stream.cpp delete mode 100644 euler/source/strings/memcpy.cpp delete mode 100644 euler/source/strings/strlen.cpp create mode 100644 euler/source/syscall.asm create mode 100644 euler/source/syscall.cpp (limited to 'euler/source') diff --git a/euler/source/entry.cpp b/euler/source/entry.cpp new file mode 100644 index 0000000..e79209c --- /dev/null +++ b/euler/source/entry.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +int main(int argc, char **argv); + +extern "C" [[noreturn]] void _start() { + + auto argc_raw = euler::syscall::try_get_environment_variable("ARGC"); + int argc = argc_raw.has_value() ? std::stoi(argc_raw.value()) : 0; + + std::vector argv; + + for (int i = 0; i < argc; ++i) { + std::string arg_name = std::string("ARGV") + std::to_string(i); + auto arg_raw = euler::syscall::try_get_environment_variable(arg_name); + argv.push_back(arg_raw.has_value() ? arg_raw.value() : ""); + } + + std::vector c_argv(argc); + for (int i = 0; i < argc; ++i) + c_argv[i] = argv[i].data(); + + int exit_code = main(argc, c_argv.data()); + + euler::syscall::end_this_process(exit_code); + +} diff --git a/euler/source/euler/entry.cpp b/euler/source/euler/entry.cpp deleted file mode 100644 index 69611b7..0000000 --- a/euler/source/euler/entry.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -int main(int argc, char **argv); - -extern "C" [[noreturn]] void _start() { - //TODO: call global constructors - //TODO: get argc, argv from environment - int exit_code = main(0, 0); - //TODO: call global destructors - __euler_end_this_thread(exit_code); -} diff --git a/euler/source/euler/gcc.asm b/euler/source/euler/gcc.asm deleted file mode 100644 index 6fc6fd5..0000000 --- a/euler/source/euler/gcc.asm +++ /dev/null @@ -1,53 +0,0 @@ -bits 64 - -global strlen - -section .text - -strlen: - xor rax, rax -.loop: - mov rdx, qword [rdi] - test edx, 0xff - jz .plus0 - test edx, 0xff00 - jz .plus1 - test edx, 0xff0000 - jz .plus2 - test edx, 0xff000000 - jz .plus3 - shr rdx, 32 - test edx, 0xff - jz .plus4 - test edx, 0xff00 - jz .plus5 - test edx, 0xff0000 - jz .plus6 - test edx, 0xff000000 - jz .plus7 - add rax, 8 - add rdi, 8 - jmp .loop -.plus0: - ret -.plus1: - add rax, 1 - ret -.plus2: - add rax, 2 - ret -.plus3: - add rax, 3 - ret -.plus4: - add rax, 4 - ret -.plus5: - add rax, 5 - ret -.plus6: - add rax, 6 - ret -.plus7: - add rax, 7 - ret diff --git a/euler/source/euler/heap.cpp b/euler/source/euler/heap.cpp deleted file mode 100644 index f7d407f..0000000 --- a/euler/source/euler/heap.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include - -namespace euler { - - static uint64_t *last_chunk_info_page = 0; - - void add_record(uint64_t start, uint64_t length) { - uint64_t dual_start = start ^ length; - for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510]) - for (int i = 0; i < 255; ++i) - if (cip[i * 2] == length && cip[i * 2 + 1] == dual_start) { - cip[i * 2] = length * 2; - cip[i * 2 + 1] &= start; - return; - } - for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510]) - for (int i = 0; i < 255; ++i) - if (cip[i * 2] == 0) { - cip[i * 2] = length; - cip[i * 2 + 1] = start; - return; - } - uint64_t *new_cip = (uint64_t *)__euler_get_new_pages(1); - new_cip[0] = length; - new_cip[1] = start; - for (int i = 1; i < 255; ++i) - new_cip[i * 2] = 0; - new_cip[510] = (uint64_t)last_chunk_info_page; - last_chunk_info_page = new_cip; - } - - void *alloc(uint64_t bytes) { - for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510]) - for (int i = 0; i < 255; ++i) - if (cip[i * 2] >= bytes) { - uint64_t start = cip[i * 2 + 1]; - uint64_t extra_length = cip[i * 2] - bytes; - cip[i * 2] = 0; - if (extra_length > 0) - dealloc((void *)(start + bytes), extra_length); - return (void *)start; - } - uint64_t pages_needed = (bytes - 1) / 4096 + 1; - uint64_t start = (uint64_t)__euler_get_new_pages(pages_needed); - if (bytes != pages_needed * 4096) - dealloc((void *)(start + bytes), pages_needed * 4096 - bytes); - return (void *)start; - } - - //this implementation is a little lazy - void dealloc(void *start, uint64_t bytes) { - uint64_t s = (uint64_t)start; - while (bytes > 0) { - uint64_t l = 1; - do - l *= 2; - while (l <= bytes && s % l == 0); - l /= 2; - add_record(s, l); - s += l; - bytes -= l; - } - } - -} diff --git a/euler/source/euler/start_process.cpp b/euler/source/euler/start_process.cpp deleted file mode 100644 index 64e16c5..0000000 --- a/euler/source/euler/start_process.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include - -namespace euler { - - start_process::start_process(const char *path) - : path(path), path_len(std::strlen(path)) {} - - void start_process::add_env_variable(const char *name, const char *value) { - env_var_specs.push_back({ - .name_len = std::strlen(name), .name = name, - .value_len = std::strlen(value), .value = value }); - } - - void start_process::gift_stream( - __euler_stream_handle to_gifter, __euler_stream_handle to_giftee) { - gift_stream_specs.push_back({ - .stream_handle_to_gifter = to_gifter, - .stream_handle_to_giftee = to_giftee }); - } - - __euler_stream_result start_process::start( - __euler_process_handle &handle_out) { - __euler_process_start_info info = { - .file_path_length = path_len, - .file_path = path, - .env_var_count = env_var_specs.size(), - .env_vars = env_var_specs.data(), - .gift_stream_count = gift_stream_specs.size(), - .gift_streams = gift_stream_specs.data(), - }; - return __euler_start_process(info, handle_out); - } - -} diff --git a/euler/source/euler/stream.cpp b/euler/source/euler/stream.cpp deleted file mode 100644 index 950f3c5..0000000 --- a/euler/source/euler/stream.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include - -namespace euler { - - stream::~stream() {} - - void file_stream::write_buffer() { - if (__euler_write_to_stream(handle, buffer_size, buffer) - != __EULER_SR_SUCCESS) - good = false;//TODO: more precise error reporting - else - buffer_dirty = false; - } - - file_stream::file_stream( - __euler_stream_handle handle, bool is_readable, bool is_writable, - bool clear, bool seek_to_end) - - : handle(handle), is_readable(is_readable), is_writable(is_writable), - buffer(0), good(true) { - - if (clear) { - if (__euler_set_stream_length(handle, 0) != __EULER_SR_SUCCESS) { - good = false; - return; - } - length = 0; - } - - else if (__euler_get_stream_length(handle, length) != __EULER_SR_SUCCESS) { - good = false; - return; - } - - if (seek_to_end) { - if (__euler_seek_stream(handle, __EULER_SF_END, 0) - != __EULER_SR_SUCCESS) { - good = false; - return; - } - offset = length; - } - - else - offset = 0; - - } - - file_stream::~file_stream() { - if (buffer) { - if (buffer_dirty) - write_buffer(); - delete[] buffer; - } - __euler_close_stream(handle); - } - - bool file_stream::try_read(void *into, uint64_t bytes) { - - if (bytes == 0) - return true; - - if (offset + bytes > length) - return false; - - if (buffer) { - uint64_t start = offset > buffer_offset ? offset : buffer_offset; - uint64_t end = offset + bytes < buffer_offset + buffer_size - ? offset + bytes : buffer_offset + buffer_size; - if (end > start) { - - uint64_t real_end = offset + bytes; - - std::memcpy( - (uint8_t *)into + start - offset, buffer + start - buffer_offset, - end - start); - if (start != offset) - if (!try_read(into, start - offset)) - return false; - - if (end != real_end) { - if (!try_seek(__EULER_SF_BEGINNING, end)) - return false; - if (!try_read((uint8_t *)into + end, real_end - end)) - return false; - } - - else if (offset != real_end) - if (!try_seek(__EULER_SF_BEGINNING, real_end)) - return false; - - return true; - - } - } - - if (buffer_dirty) { - write_buffer(); - if (!good) - return false; - } - - //1024 is arbitrary - buffer_size = length - offset < 1024 ? length - offset : 1024; - - if (buffer != 0) - delete[] buffer; - - buffer = new uint8_t[buffer_size]; - buffer_dirty = false; - buffer_offset = offset; - - if (__euler_read_from_stream(handle, buffer_size, buffer) - != __EULER_SR_SUCCESS) { - delete[] buffer; - buffer = 0; - return false; - } - - uint64_t read_this_buffer = buffer_size < bytes ? buffer_size : bytes; - std::memcpy(into, buffer, read_this_buffer); - offset += read_this_buffer; - - if (read_this_buffer == bytes) - return true; - - return try_read( - (uint8_t *)into + read_this_buffer, bytes - read_this_buffer); - - } - - bool file_stream::try_seek(__euler_seek_from from, int64_t offset) { - if (__euler_seek_stream(handle, from, offset) != __EULER_SR_SUCCESS) - return false; - switch (from) { - case __EULER_SF_BEGINNING: - this->offset = offset; - return true; - case __EULER_SF_END: - this->offset = length + offset; - return true; - case __EULER_SF_CURRENT_POSITION: - this->offset += offset; - return true; - default: - return false; - } - } - -} diff --git a/euler/source/euler/syscall.asm b/euler/source/euler/syscall.asm deleted file mode 100644 index 64f08f8..0000000 --- a/euler/source/euler/syscall.asm +++ /dev/null @@ -1,144 +0,0 @@ -bits 64 - -global __euler_open_file -global __euler_close_stream -global __euler_get_new_pages -global __euler_write_to_stream -global __euler_seek_stream -global __euler_get_stream_length -global __euler_set_stream_length -global __euler_end_this_thread -global __euler_read_from_stream -global __euler_get_framebuffer -global __euler_encode_color -global __euler_get_input_packet -global __euler_start_process -global __euler_get_other_end_process_handle - -section .text - -__euler_open_file: - mov rax, 2 - push rdx - xor rdx, rdx - or rdx, r8 - shl r9, 1 - or rdx, r9 - syscall - pop rdx - mov qword [rdx], rdi - ret - -__euler_close_stream: - mov rax, 11 - syscall - ret - -__euler_get_new_pages: - mov rax, 4 - syscall - ret - -__euler_write_to_stream: - mov rax, 14 - syscall - ret - -__euler_seek_stream: - mov rax, 12 - syscall - ret - -__euler_get_stream_length: - push rsi - mov rax, 15 - syscall - pop rsi - mov qword [rsi], rdi - ret - -__euler_set_stream_length: - mov rax, 18 - syscall - ret - -__euler_end_this_thread: - mov rax, 3 - syscall - -__euler_read_from_stream: - mov rax, 13 - syscall - ret - -__euler_get_framebuffer: - push rdi - push rsi - push rdx - mov rax, 1 - syscall - pop rdx - mov dword [rdx], esi - pop rdx - pop rsi - mov dword [rsi], edi - shr rdi, 32 - mov dword [rdx], edi - ret - -__euler_encode_color: - xor ah, ah - mov al, dl - shl ax, 8 - mov al, sil - shl eax, 8 - mov al, dil - mov edi, eax - xor rax, rax - syscall - ret - -__euler_get_input_packet: - push rdi - push rsi - push rdx - push rcx - mov rax, 5 - syscall - - test al, 0x80 - jnz .mouse_packet - - pop rcx - mov dword [rcx], edi - add rsp, 24 - mov al, 2 - ret - -.mouse_packet: - add rsp, 8 - pop rdx - mov word [rdx], si - pop rdx - mov word [rdx], di - pop rdx - and al, 0x7f - mov byte [rdx], al - mov al, 1 - ret - -__euler_start_process: - push rsi - mov rax, 16 - syscall - pop rsi - mov qword [rsi], rdi - ret - -__euler_get_other_end_process_handle: - push rsi - mov rax, 19 - syscall - pop rsi - mov qword [rsi], rdi - ret diff --git a/euler/source/heap.cpp b/euler/source/heap.cpp new file mode 100644 index 0000000..dc92e5d --- /dev/null +++ b/euler/source/heap.cpp @@ -0,0 +1,155 @@ +#include +#include +#include + +namespace euler::heap { + + struct heap_aligned_entry { + size_t start_vaddr; + size_t length; + heap_aligned_entry *prev; + heap_aligned_entry *next; + }; + + struct unused_heap_entries_page { + heap_aligned_entry *unused[510]; + unused_heap_entries_page *prev; + uint16_t unused_in_this_page; + }; + + heap_aligned_entry *first_entry = 0; + heap_aligned_entry *last_entry = 0; + unused_heap_entries_page *last_page = 0; + + void mark_entry_unused(heap_aligned_entry *entry) { + + for (unused_heap_entries_page *page = last_page; page != 0; page = page->prev) { + if (page->unused_in_this_page == 510) + continue; + for (uint16_t i = 0; i < 510; ++i) + if (page->unused[i] == 0) { + page->unused[i] = entry; + ++page->unused_in_this_page; + return; + } + } + + unused_heap_entries_page *page = (unused_heap_entries_page *)euler::syscall::get_new_pages(1); + page->prev = last_page; + last_page = page; + + page->unused_in_this_page = 1; + page->unused[0] = entry; + + for (uint16_t i = 1; i < 510; ++i) + page->unused[i] = 0; + + } + + void remove_entry(heap_aligned_entry *entry) { + + if (entry->prev) + entry->prev->next = entry->next; + else + first_entry = entry->next; + if (entry->next) + entry->next->prev = entry->prev; + else + last_entry = entry->prev; + + mark_entry_unused(entry); + + } + + heap_aligned_entry *get_new_entry() { + + for (unused_heap_entries_page *page = last_page; page != 0; page = page->prev) { + if (page->unused_in_this_page == 0) + continue; + for (uint16_t i = 0; i < 510; ++i) + if (page->unused[i] != 0) { + heap_aligned_entry *entry = page->unused[i]; + page->unused[i] = 0; + --page->unused_in_this_page; + return entry; + } + } + + heap_aligned_entry *new_entries = (heap_aligned_entry *)euler::syscall::get_new_pages(1); + + for (uint8_t i = 1; i < 128; ++i) + mark_entry_unused(new_entries + i); + + return new_entries; + + } + + void *get_block(size_t length) { + + for (heap_aligned_entry *entry = first_entry; entry != 0; entry = entry->next) { + if (entry->length == length) { + void *start = (void *)entry->start_vaddr; + remove_entry(entry); + return start; + } + if (entry->length > length) { + void *start = (void *)entry->start_vaddr; + entry->start_vaddr += length; + entry->length -= length; + return start; + } + } + + size_t pages = (length - 1) / 4096 + 1; + void *new_memory = euler::syscall::get_new_pages(pages); + if (pages * 4096 != length) + return_block((uint8_t *)new_memory + length, pages * 4096 - length); + return new_memory; + + } + + void return_block(void *start, size_t length) { + + heap_aligned_entry *after = first_entry; + while (after != 0 && after->start_vaddr < (size_t)start + length) + after = after->next; + + heap_aligned_entry *before; + if (after == 0) + before = last_entry; + else + before = after->prev; + + heap_aligned_entry *us; + + if (after && after->start_vaddr == (size_t)start + length) { + after->start_vaddr -= length; + after->length += length; + us = after; + } + + else { + us = get_new_entry(); + us->prev = before; + us->next = after; + us->start_vaddr = (size_t)start; + us->length = length; + if (before) + before->next = us; + else + first_entry = us; + if (after) + after->prev = us; + else + last_entry = us; + } + + if (before && before->start_vaddr + before->length == (size_t)start) { + us->start_vaddr -= before->length; + us->length += before->length; + remove_entry(before); + } + + } + +} diff --git a/euler/source/io/fclose.cpp b/euler/source/io/fclose.cpp deleted file mode 100644 index 6f43f85..0000000 --- a/euler/source/io/fclose.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -namespace std { - - int fclose(FILE *stream) { - delete stream; - return 0; - } - -} diff --git a/euler/source/io/fopen.cpp b/euler/source/io/fopen.cpp deleted file mode 100644 index 8d47bf0..0000000 --- a/euler/source/io/fopen.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include - -namespace std { - - FILE *fopen(const char *filename, const char *mode) { - - bool read = false; - bool write = false; - bool append = false; - bool extended = false; - bool create = false; - - for (const char *p = mode; *p; ++p) - switch (*p) { - case 'r': - read = true; - continue; - case 'w': - write = true; - continue; - case 'a': - append = true; - continue; - case '+': - extended = true; - continue; - case 'x': - create = true; - continue; - default: - continue; - } - - __euler_stream_handle handle; - __euler_stream_result res = __euler_open_file( - filename, strlen(filename), handle, write || append, create); - - if (res != __EULER_SR_SUCCESS) - return 0; - - euler::file_stream *f = new euler::file_stream(handle, read || extended, - write || extended, write && !append, append); - - if (f->good) - return f; - - delete f; - return 0; - - } - -} diff --git a/euler/source/io/fread.cpp b/euler/source/io/fread.cpp deleted file mode 100644 index e2d05b6..0000000 --- a/euler/source/io/fread.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -namespace std { - - size_t fread(void *buffer, size_t size, size_t count, FILE *stream) { - return stream->try_read(buffer, size * count) ? count : 0; - } - -} diff --git a/euler/source/io/fseek.cpp b/euler/source/io/fseek.cpp deleted file mode 100644 index 3254468..0000000 --- a/euler/source/io/fseek.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace std { - - int fseek(FILE *stream, long offset, int origin) { - if (origin < 0 || origin > 2) - return 1; - return stream->try_seek((__euler_seek_from)origin, offset) ? 0 : 2; - } - -} diff --git a/euler/source/memory/delete.cpp b/euler/source/memory/delete.cpp deleted file mode 100644 index e4cc288..0000000 --- a/euler/source/memory/delete.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -void operator delete(void *ptr) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} - -void operator delete(void *ptr, uint64_t) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} - -void operator delete[](void *ptr) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} - -void operator delete[](void *ptr, uint64_t) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} diff --git a/euler/source/memory/new.cpp b/euler/source/memory/new.cpp deleted file mode 100644 index 931328f..0000000 --- a/euler/source/memory/new.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include - -void *operator new(uint64_t size) { - void *ptr = euler::alloc(size + 8); - *(uint64_t *)ptr = size + 8; - return (uint8_t *)ptr + 8; -} - -void *operator new[](uint64_t size) { - void *ptr = euler::alloc(size + 8); - *(uint64_t *)ptr = size + 8; - return (uint8_t *)ptr + 8; -} diff --git a/euler/source/std/cctype.cpp b/euler/source/std/cctype.cpp new file mode 100644 index 0000000..a35d1a5 --- /dev/null +++ b/euler/source/std/cctype.cpp @@ -0,0 +1,7 @@ +#include + +extern "C" int isspace(int ch) { + return + ch == ' ' || ch == '\f' || ch == '\n' || + ch == '\r' || ch == '\t' || ch == '\v'; +} diff --git a/euler/source/std/cstdio.cpp b/euler/source/std/cstdio.cpp new file mode 100644 index 0000000..8c12a7c --- /dev/null +++ b/euler/source/std/cstdio.cpp @@ -0,0 +1,59 @@ +#include + +extern "C" FILE *fopen(const char *filename, const char *mode) { + + bool r = false, w = false, a = false, p = false, x = false; + for (size_t i = 0; mode[i] != '\0'; ++i) + if (mode[i] == 'r') r = true; + else if (mode[i] == 'w') w = true; + else if (mode[i] == 'a') a = true; + else if (mode[i] == 'p') p = true; + else if (mode[i] == 'x') x = true; + + euler::syscall::stream_handle handle; + if (euler::syscall::open_file(filename, !r, x, handle) != + euler::syscall::stream_result::success) + return 0; + + if (w && euler::syscall::set_stream_length(handle, 0) != + euler::syscall::stream_result::success) + return 0; + + if (a && euler::syscall::seek_stream( + handle, euler::syscall::seek_from::end, 0) != + euler::syscall::stream_result::success) { + euler::syscall::close_stream(handle); + return 0; + } + + uint64_t length; + if (euler::syscall::get_stream_length(handle, length) != + euler::syscall::stream_result::success) { + euler::syscall::close_stream(handle); + return 0; + } + + return new euler::file_stream(handle, r || p, length, a ? length : 0); + +} + +extern "C" void fclose(FILE *stream) { + stream->close(); + delete stream; +} + +extern "C" int fseek(FILE *stream, long offset, int origin) { + + if (origin < 0 || origin > 2) + return -1; + + return (int)stream->seek((euler::syscall::seek_from)origin, offset); + +} + +extern "C" size_t fread( + void *buffer, size_t size, size_t count, FILE *stream) { + + return (stream->read(size * count, buffer).first - 1) / size + 1; + +} diff --git a/euler/source/std/cstdlib.cpp b/euler/source/std/cstdlib.cpp new file mode 100644 index 0000000..cfb4b48 --- /dev/null +++ b/euler/source/std/cstdlib.cpp @@ -0,0 +1,19 @@ +#include +#include + +extern "C" [[noreturn]] void abort() noexcept { + //TODO + while (1) + ; +} + +extern "C" void *malloc(size_t size) { + size_t *block = (size_t *)euler::heap::get_block(size + 8); + *block = size; + return block + 1; +} + +extern "C" void free(void *ptr) { + size_t *block = (size_t *)ptr - 1; + euler::heap::return_block(block, *block + 8); +} diff --git a/euler/source/std/cstring.cpp b/euler/source/std/cstring.cpp new file mode 100644 index 0000000..3e5a56c --- /dev/null +++ b/euler/source/std/cstring.cpp @@ -0,0 +1,36 @@ +#include + +extern "C" void *memset(void *dest, int ch, size_t count) { + unsigned char c = static_cast(ch); + unsigned char *d = (unsigned char *)dest; + for (size_t i = 0; i < count; ++i) + d[i] = c; + return dest; +} + +extern "C" void *memcpy(void *dest, const void *src, size_t count) { + unsigned char *d = (unsigned char *)dest; + const unsigned char *s = (const unsigned char *)src; + for (size_t i = 0; i < count; ++i) + d[i] = s[i]; + return dest; +} + +extern "C" int strcmp(const char *lhs, const char *rhs) { + const unsigned char *l = (const unsigned char *)lhs; + const unsigned char *r = (const unsigned char *)rhs; + while (*l == *r) { + if (*l == 0) + return 0; + ++l; + ++r; + } + return *l < *r ? -1 : 1; +} + +extern "C" size_t strlen(const char *str) { + size_t len = 0; + while (str[len] != '\0') + ++len; + return len; +} diff --git a/euler/source/std/string.cpp b/euler/source/std/string.cpp new file mode 100644 index 0000000..31c47a5 --- /dev/null +++ b/euler/source/std/string.cpp @@ -0,0 +1,86 @@ +#include +#include + +namespace std { + + int stoi(const std::string &str, size_t *pos, int base) { + //TODO: exceptions + + size_t i = 0; + while (isspace(str[i])) + ++i; + + bool is_negative = false; + if (str[i] == '-') { + is_negative = true; + ++i; + } + else if (str[i] == '+') + ++i; + + if ((base == 16 || base == 0) && str[i] == '0' && + (str[i + 1] == 'x' || str[i + 1] == 'X')) { + base = 16; + i += 2; + } + + else if ((base == 8 || base == 0) && str[i] == '0') { + base = 8; + ++i; + } + + else if (base == 0) + base = 10; + + int value = 0; + + while (true) { + char c = str[i]; + if (c >= '0' && c < '0' + base) + value = value * base + c - '0'; + else if (c >= 'a' && c < 'a' + base - 10) + value = value * base + c - 'a' + 10; + else if (c >= 'A' && c < 'A' + base - 10) + value = value * base + c - 'A' + 10; + else + break; + } + + if (pos != 0) + *pos = i; + + return is_negative ? -value : value; + + } + + std::string to_string(int value) { + + int max_place = 1; + int places = 1; + while (max_place <= value / 10) { + max_place *= 10; + ++places; + } + + std::string s; + s.resize(places); + + for (int i = 0; i < places; ++i) { + s[i] = (value / max_place) % 10 + '0'; + max_place /= 10; + } + + return s; + + } + + std::string operator +(std::string &&lhs, std::string &&rhs) { + std::string s = std::move(lhs); + s.resize(lhs.size() + rhs.size()); + for (size_t i = 0; i < rhs.size(); ++i) + s[lhs.size() + i] = rhs[i]; + rhs.clear(); + return s; + } + +} diff --git a/euler/source/stream.cpp b/euler/source/stream.cpp new file mode 100644 index 0000000..faf2907 --- /dev/null +++ b/euler/source/stream.cpp @@ -0,0 +1,106 @@ +#include +#include + +namespace euler { + + file_stream::file_stream( + syscall::stream_handle handle, bool may_read, + uint64_t length, uint64_t position) + : handle(handle), may_read(may_read), buffer_loaded(false), + length(length), position(position) {} + + syscall::stream_result file_stream::seek( + syscall::seek_from from, int64_t offset) { + + int64_t new_position = offset + + (from == syscall::seek_from::beginning ? 0 : + from == syscall::seek_from::end ? length : position); + + if (new_position < 0 || (uint64_t)new_position > length) + return syscall::stream_result::out_of_bounds; + + position = new_position; + return syscall::stream_result::success; + + } + + std::pair + file_stream::read(uint64_t bytes, void *into) { + + if (!may_read) + return {0, syscall::stream_result::not_readable}; + + uint64_t have_read = 0; + syscall::stream_result result = syscall::stream_result::success; + + uint64_t end = position + bytes; + + if (end > length) { + end = length; + result = syscall::stream_result::out_of_bounds; + } + + uint64_t block_start = (position / 1024) * 1024; + + while (position < end) { + + uint64_t length_in_this_block = + std::min(end, block_start + 1024) - position; + + if (buffer_loaded && buffer_start == block_start) + memcpy(into, buffer + position - block_start, length_in_this_block); + + else if (length_in_this_block == 1024) { + + syscall::stream_result seek_result = + syscall::seek_stream( + handle, syscall::seek_from::beginning, block_start); + if (seek_result != syscall::stream_result::success) + return {have_read, seek_result}; + + syscall::stream_result read_result = + syscall::read_from_stream(handle, 1024, into); + if (read_result != syscall::stream_result::success) + return {have_read, read_result}; + + } + + else { + + syscall::stream_result seek_result = + syscall::seek_stream( + handle, syscall::seek_from::beginning, block_start); + if (seek_result != syscall::stream_result::success) + return {have_read, seek_result}; + + uint64_t buffer_length = std::min(1024UL, length - block_start); + + syscall::stream_result read_result = + syscall::read_from_stream(handle, buffer_length, buffer); + if (read_result != syscall::stream_result::success) { + buffer_loaded = false; + return {have_read, read_result}; + } + + buffer_loaded = true; + buffer_start = block_start; + memcpy(into, buffer + position - block_start, length_in_this_block); + + } + + into = (uint8_t *)into + length_in_this_block; + have_read += length_in_this_block; + position += length_in_this_block; + block_start += 1024; + + } + + return {have_read, result}; + + } + + void file_stream::close() { + syscall::close_stream(handle); + } + +} diff --git a/euler/source/strings/memcpy.cpp b/euler/source/strings/memcpy.cpp deleted file mode 100644 index d5a1d6c..0000000 --- a/euler/source/strings/memcpy.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -namespace std { - - void *memcpy(void *dest, const void *src, size_t count) { - uint8_t *d8 = (uint8_t *)dest; - const uint8_t *s8 = (const uint8_t *)src; - for (size_t i = 0; i < count; ++i) - d8[i] = s8[i]; - return dest; - } - -} diff --git a/euler/source/strings/strlen.cpp b/euler/source/strings/strlen.cpp deleted file mode 100644 index 7a6fe2a..0000000 --- a/euler/source/strings/strlen.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -namespace std { - - size_t strlen(const char *str) { - size_t len = 0; - while (str[len]) - ++len; - return len; - } - -} diff --git a/euler/source/syscall.asm b/euler/source/syscall.asm new file mode 100644 index 0000000..d4515bb --- /dev/null +++ b/euler/source/syscall.asm @@ -0,0 +1,32 @@ +bits 64 + +section .text + +global __euler_do_syscall +__euler_do_syscall: + + push rdi + push rsi + push rdx + push rcx + + mov rax, qword [rdi] + mov rdi, qword [rsi] + mov rsi, qword [rdx] + mov rdx, qword [rcx] + + syscall + + pop rcx + mov qword [rcx], rdx + + pop rdx + mov qword [rdx], rsi + + pop rsi + mov qword [rsi], rdi + + pop rdi + mov qword [rdi], rax + + ret diff --git a/euler/source/syscall.cpp b/euler/source/syscall.cpp new file mode 100644 index 0000000..b3ed3a8 --- /dev/null +++ b/euler/source/syscall.cpp @@ -0,0 +1,397 @@ +#include + +extern "C" void __euler_do_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx); + +namespace euler::syscall { + + encoded_color encode_color( + uint8_t r, uint8_t g, uint8_t b) { + + uint64_t rax = 0; + uint64_t rdi = (uint32_t)r | ((uint32_t)g << 8) | ((uint32_t)b << 16); + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (encoded_color)(rax & 0xffffffff); + + } + + void get_framebuffer( + encoded_color *&buffer_out, uint32_t &width_out, + uint32_t &height_out, uint32_t &pitch_out) { + + uint64_t rax = 1; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + buffer_out = (encoded_color *)rax; + width_out = rdi & 0xffffffff; + height_out = rdi >> 32; + pitch_out = rsi & 0xffffffff; + + } + + stream_result open_file( + const std::string &file_path, bool allow_creation, + bool only_allow_creation, stream_handle &handle_out) { + + uint64_t rax = 2; + uint64_t rdi = (uint64_t)file_path.data(); + uint64_t rsi = file_path.size(); + uint64_t rdx = + ( allow_creation ? 0x1 : 0x0) | + (only_allow_creation ? 0x2 : 0x0); + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + [[noreturn]] void end_this_thread(int32_t exit_code) { + + uint64_t rax = 3; + uint64_t rdi = (uint32_t)exit_code; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + __builtin_unreachable(); + + } + + void *get_new_pages(uint64_t n_pages) { + + uint64_t rax = 4; + uint64_t rdi = n_pages; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (void *)rax; + + } + + std::variant get_input_packet() { + + uint64_t rax = 5; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + if (rax & 0x80) + return (mouse_packet){ + . left_button_down = (rax & 0x01) != 0, + . right_button_down = (rax & 0x02) != 0, + .middle_button_down = (rax & 0x04) != 0, + .x_changed = (int16_t)(rdi & 0xffff), + .y_changed = (int16_t)(rsi & 0xffff) + }; + + return (key_packet){ + .was_key_up_event = (rdi & 0x40000) != 0, + . num_lock = (rdi & 0x20000) != 0, + . caps_lock = (rdi & 0x10000) != 0, + . right_win = (rdi & 0x8000) != 0, + . left_win = (rdi & 0x4000) != 0, + . right_alt = (rdi & 0x2000) != 0, + . left_alt = (rdi & 0x1000) != 0, + . right_ctrl = (rdi & 0x800) != 0, + . left_ctrl = (rdi & 0x400) != 0, + . right_shift = (rdi & 0x200) != 0, + . left_shift = (rdi & 0x100) != 0, + . key_code = (uint8_t)(rdi & 0xff) + }; + + } + + void create_private_socket( + stream_handle &end_1_out, stream_handle &end_2_out) { + + uint64_t rax = 6; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + end_1_out = rax; + end_2_out = rdi; + + } + + stream_result create_socket_listener( + const std::string &id, listener_handle &handle_out) { + + uint64_t rax = 7; + uint64_t rdi = (uint64_t)id.data(); + uint64_t rsi = id.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + void stop_socket_listener(listener_handle handle) { + + uint64_t rax = 8; + uint64_t rdi = handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + stream_result accept_socket_connection( + listener_handle listener, stream_handle &stream_out) { + + uint64_t rax = 9; + uint64_t rdi = listener; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + stream_out = rdi; + return (stream_result)rax; + + } + + stream_result connect_to_socket( + const std::string &id, stream_handle &handle_out) { + + uint64_t rax = 10; + uint64_t rdi = (uint64_t)id.data(); + uint64_t rsi = id.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + void close_stream(stream_handle handle) { + + uint64_t rax = 11; + uint64_t rdi = handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + stream_result seek_stream( + stream_handle handle, seek_from from, int64_t offset) { + + uint64_t rax = 12; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = (uint8_t)from; + uint64_t rdx = (uint64_t)offset; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result read_from_stream( + stream_handle handle, uint64_t bytes, void *into) { + + uint64_t rax = 13; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = bytes; + uint64_t rdx = (uint64_t)into; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result write_to_stream( + stream_handle handle, uint64_t bytes, const void *from) { + + uint64_t rax = 14; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = bytes; + uint64_t rdx = (uint64_t)from; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result get_stream_length( + stream_handle handle, uint64_t &length_out) { + + uint64_t rax = 15; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + length_out = rdi; + return (stream_result)rax; + + } + + stream_result start_process( + const std::string &file_path, + const std::vector> &environment_variables, + const std::vector> &gifted_streams, + process_handle &handle_out) { + + std::vector ev_structs(environment_variables.size() * 4); + for (size_t i = 0; i < environment_variables.size(); ++i) { + ev_structs[i * 4] = environment_variables[i]. first.size(); + ev_structs[i * 4 + 1] = (uint64_t)environment_variables[i]. first.data(); + ev_structs[i * 4 + 2] = environment_variables[i].second.size(); + ev_structs[i * 4 + 3] = (uint64_t)environment_variables[i].second.data(); + } + + std::vector gs_structs(gifted_streams.size() * 2); + for (size_t i = 0; i < environment_variables.size(); ++i) { + gs_structs[i * 2] = (uint64_t)gifted_streams[i]. first; + gs_structs[i * 2 + 1] = (uint64_t)gifted_streams[i].second; + } + + uint64_t psi_struct[] = { + file_path.size(), (uint64_t) file_path.data(), + environment_variables.size(), (uint64_t)ev_structs.data(), + gifted_streams.size(), (uint64_t)gs_structs.data() + }; + + uint64_t rax = 16; + uint64_t rdi = (uint64_t)psi_struct; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + [[noreturn]] void end_this_process(int32_t exit_code) { + + uint64_t rax = 17; + uint64_t rdi = (uint32_t)exit_code; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + __builtin_unreachable(); + + } + + stream_result set_stream_length( + stream_handle handle, uint64_t new_length) { + + uint64_t rax = 18; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = new_length; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result get_other_end_process_handle( + stream_handle stream, process_handle &process_out) { + + uint64_t rax = 19; + uint64_t rdi = (uint64_t)stream; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + process_out = (process_handle)rdi; + return (stream_result)rax; + + } + + //entry_point must not return + void start_thread(void (*entry_point)(uint64_t), uint64_t arg) { + + uint64_t rax = 20; + uint64_t rdi = (uint64_t)entry_point; + uint64_t rsi = arg; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + //entry_point must not return + void start_thread(void (*entry_point)()) { + + uint64_t rax = 20; + uint64_t rdi = (uint64_t)entry_point; + uint64_t rsi = 0; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + //return value is number of bytes cleared + uint64_t clear_socket_read_queue(stream_handle handle) { + + uint64_t rax = 21; + uint64_t rdi = handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return rax; + + } + + //this function has a race condition if the variable is changed + //or unset by another thread between the two syscalls. + std::optional try_get_environment_variable( + const std::string &name) { + + uint64_t rax = 22; + uint64_t rdi = (uint64_t)name.data(); + uint64_t rsi = name.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + if (rax == (uint64_t)-1) + return {}; + + std::string s; + s.resize(rax); + + rax = 23; + rdi = (uint64_t)name.data(); + rsi = name.size(); + rdx = (uint64_t)s.data(); + + __euler_do_syscall(rax, rdi, rsi, rdx); + //do i need to tell gcc that s is modified? + + return s; + + } + +} -- cgit v1.2.3