diff options
90 files changed, 2935 insertions, 2105 deletions
@@ -1,5 +1,10 @@ -.vscode/ -limine -mintsuki-freestanding-headers -obj -out +.vscode/* + +build/* +dependencies/* +toolchain/* + +euler/build/* +kernel/build/* +applications/*/build/* +libraries/*/build/* diff --git a/applications/init/main.cpp b/applications/init/main.cpp deleted file mode 100644 index 57ebd02..0000000 --- a/applications/init/main.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include <daguerre/image.hpp> - -int main(int, char **) { - - auto fb = daguerre::get_hilbert_framebuffer(); - - auto white = daguerre::to_hilbert_color({.r = 255, .g = 255, .b = 255}); - auto gray = daguerre::to_hilbert_color({.r = 127, .g = 127, .b = 127}); - - for (unsigned y = 0; y < fb.get_height(); ++y) - for (unsigned x = 0; x < fb.get_width(); ++x) { - uint8_t v = (y / 16) % 2 == (x / 16) % 2; - fb.get(x, y) = v ? white : gray; - } - - daguerre::image<daguerre::color24> img; - - std::FILE *file = std::fopen("/init/burdon.ppm", "r"); - assert(file != 0); - assert(daguerre::try_load_ppm(file, img)); - std::fclose(file); - - unsigned width = - img.get_width() < fb.get_width() ? img.get_width() : fb.get_width(); - unsigned height = - img.get_height() < fb.get_height() ? img.get_height() : fb.get_height(); - - unsigned x_off = (fb.get_width() - width) / 2; - unsigned y_off = (fb.get_height() - height) / 2; - - daguerre::copy_image(img, fb, 0, 0, x_off, y_off, width, height); - - while (true) { - - uint32_t kp = _syscall_read_key_packet(); - if ((kp & 0x0400ff) == 0x04005a) { - for (unsigned y = 0; y < img.get_height(); ++y) - for (unsigned x = 0; x < img.get_width(); ++x) { - img.get(x, y).r = ~img.get(x, y).r; - img.get(x, y).g = ~img.get(x, y).g; - img.get(x, y).b = ~img.get(x, y).b; - } - daguerre::copy_image(img, fb, 0, 0, x_off, y_off, width, height); - } - - } - - return 0; - -} diff --git a/applications/init/makefile b/applications/init/makefile new file mode 100644 index 0000000..6799406 --- /dev/null +++ b/applications/init/makefile @@ -0,0 +1,12 @@ +SOURCES = \ + main.cpp + +build/%.cpp.o: source/%.cpp + @mkdir -p $(@D) + $(HILBERT_CC) -c $^ -o $@ + +build/init.elf: $(SOURCES:%=build/%.o) + $(HILBERT_CC) $^ -ldaguerre -o $@ + +clean: + rm -rf build diff --git a/applications/init/source/main.cpp b/applications/init/source/main.cpp new file mode 100644 index 0000000..ed0ef8e --- /dev/null +++ b/applications/init/source/main.cpp @@ -0,0 +1,28 @@ +#include <daguerre.hpp> + +int main(int, char **) { + + auto hfb = daguerre::get_hilbert_framebuffer(); + daguerre::image<daguerre::rgb24> bim; + + std::FILE *burdon = std::fopen("/assets/burden.ppm", "r"); + daguerre::try_load_ppm(burdon, bim); + std::fclose(burdon); + + unsigned width = bim.width < hfb.width ? bim.width : hfb.width; + unsigned height = bim.height < hfb.height ? bim.height : hfb.height; + unsigned x = (hfb.width - width) / 2; + unsigned y = (hfb.height - height) / 2; + + while (1) { + daguerre::copy_region<>(hfb, bim, 0, 0, x, y, width, height); + while ((__euler_read_key_packet() & 0x0400ff) != 0x00005a) + ; + for (unsigned i = 0; i < bim.width * bim.height; ++i) { + bim.buffer[i].r = 255 - bim.buffer[i].r; + bim.buffer[i].g = 255 - bim.buffer[i].g; + bim.buffer[i].b = 255 - bim.buffer[i].b; + } + } + +} diff --git a/applications/link.ld b/applications/link.ld deleted file mode 100644 index f8c09a2..0000000 --- a/applications/link.ld +++ /dev/null @@ -1,38 +0,0 @@ -OUTPUT_FORMAT(elf64-x86-64) -OUTPUT_ARCH(i386:x86-64) - -ENTRY(_entry) - -PHDRS { - rx PT_LOAD FLAGS(5); - ro PT_LOAD FLAGS(4); - rw PT_LOAD FLAGS(6); -} - -SECTIONS { - - /* see also ../documentation/memory.txt */ - . = 0x200000; - - .text : { - *(.text .text.*) - } : rx - - . = ALIGN(4096); - - .rodata : { - *(.rodata .rodata.*) - } : ro - - . = ALIGN(4096); - - .data : { - *(.data .data.*) - } : rw - - .bss : { - *(.bss .bss.*) - *(COMMON) - } : rw - -} diff --git a/documentation/compositor.txt b/documentation/compositor.txt new file mode 100644 index 0000000..8946eff --- /dev/null +++ b/documentation/compositor.txt @@ -0,0 +1,26 @@ +compositors listen on the socket id "hilbert.compositor". + +data types: + + color24: + byte: red + byte: green + byte: blue + + color24 rectangle: + multiple color24's, top to bottom by row, left to right within row + +messages from applications to compositor: + + open window: + byte: 0x00 + dword: window width + dword: window height + + update window region: + byte: 0x01 + dword: start x + dword: start y + dword: width + dword: height + color24 rectangle: the data diff --git a/documentation/euler/heap.txt b/documentation/euler/heap.txt new file mode 100644 index 0000000..de1deec --- /dev/null +++ b/documentation/euler/heap.txt @@ -0,0 +1,32 @@ +this file documents dynamic memory allocation and deallocation in userspace. +the unused areas of a process's usable mapped memory are divided into "chunks" +with a start and a length, satisfying the following properties: + a) the length of a chunk is a positive power of 2 + b) the start of a chunk is a multiple of its length + c) let s be a power of 2 and k be an integer. there are never two chunks with + length s and starts s * (2 * k) and s * (2 * k + 1). if ever an operation + would result in two such chunks, they are combined into one chunk with + length 2 * s and start 2 * s * k. + +a "chunk info page" is divided into 512 64-bit integers describing up to 255 +chunks and a pointer to another chunk info page. for each n from 0 to 254: + if the (2 * n)'th integer (where the first integer is the 0th one) is 0: + the (2 * n + 1)'th integer is unused. + if the (2 * n)'th integer is not 0: + the (2 * n)'th integer describes the length of a chunk + the (2 * n + 1)'th integer describes the start of the same chunk +the 510th integer is a pointer to the next chunk info page, and the 511th +integer is never used. + +when a program calls new or malloc with needed size s, we find a free chunk of +length at least s + 8. we then remove that chunk from the list of free chunks, +and add back in whatever is left after the first s + 8 bytes, if anything. +in the first 8 bytes of the original chunk, we store the value s + 8. the +remainder of the orginal chunk is returned to the program. + +during that process, if there isn't a chunk with the needed size, one or more +new pages are requested from the kernel to create such a chunk. + +when a program calls delete or free with pointer ptr, we read the integer in +the 8 bytes starting at ptr - 8 into a variable s. we then add the region +starting at ptr - 8 with length s to the free memory. diff --git a/documentation/panics.txt b/documentation/panics.txt new file mode 100644 index 0000000..e1b6ec1 --- /dev/null +++ b/documentation/panics.txt @@ -0,0 +1,8 @@ +when the kernel panics, it fills the screen with a color indicating the type of +panic. the following are the defined colors so far: + #48a6ed - failed to get root node of initfs + #5f8860 - no initfs module was given + #7e874d - failed to look up /bin/init + #9af5e6 - an unimplemented path in the kernel + #ba40bb - cpu exception occurred + #c39db3 - failed to parse /bin/init diff --git a/documentation/sockets.txt b/documentation/sockets.txt new file mode 100644 index 0000000..73dade6 --- /dev/null +++ b/documentation/sockets.txt @@ -0,0 +1,28 @@ +in hilbert os, a "socket" is a two-way byte-wise communication construct. each +socket has two ends, which can be either open or closed. each process has a +number of handles to sockets. sockets can be created in one of two ways: either +creating a private socket or connecting to a socket listener. + +private sockets: + a private socket is created with the "create private socket" system call. the + process creating the socket gets both ends of the socket. + +socket listeners: + a socket listener is created with the "create socket listener" system call. + an id string is passed to that system call and remains associated with the + listener throughout its lifetime. only one socket listener may have a given + id at once. while a socket listener exists, the owner of the listener can + call the "accept socket connection" system call, and any process can call the + "connect to socket" system call with that id passed. each of these system + calls blocks until the other occurs, at which point a socket is created with + the two process as its endpoints, and then both system calls return. the + listener remains alive after the socket is created, and can be used to create + more sockets until stopped with the "stop socket listener" system call. + +when a process is created, an end of a socket can be "gifted" to that process. +when that happens, the end remains open, and is now accessible by the giftee +and not by the gifter. + +when either end of a socket is closed, the other end of the socket remains +valid, and can be read from until empty. when both ends of a socket are closed, +the socket disappears. diff --git a/documentation/syscalls.txt b/documentation/syscalls.txt index 15aabfa..52e909d 100644 --- a/documentation/syscalls.txt +++ b/documentation/syscalls.txt @@ -3,19 +3,32 @@ on application entry: executable with guard pages on either side, and rsp is set to the top of that. all other registers are set to 0. +the ARGC environment variable holds the number of arguments to main. +the ARGV0, ARGV1, ARGV2, etc environment variables hold those arguments. + for all system calls: rax, rdi, rsi, rdx are in/out paramters. rbx, rbp, rsp, r12-r15 are preserved. rcx, rflags, r8-r11 are clobbered. -file result: - 0 = success - 1 = bad file handle - 2 = device error - 3 = file system corrupt - 4 = tried to read out of bounds - 5 = file does not exist - 6 = tried to open directory +interrupts (including the timer!) are disabled during system calls. + +stream result: + 0 = success + 1 = bad handle + 2 = io error + 3 = out of bounds + 4 = does not exist + 5 = not a regular file + 6 = not an executable + 7 = not writable + 8 = not seekable + 9 = socket id already used + 10 = socket id not in use + 11 = socket listener closed + 12 = other end closed + 13 = already exists + 14 = not sized encode color: rax in: 0 @@ -34,39 +47,122 @@ open file: rax in: 2 rdi in: pointer to file path rsi in: file path length - rax out: file result - rdi out: file handle (if rax = success) + rdx in: + bit 0: allow creation + bit 1: only allow creation + rax out: stream result + rdi out: stream handle (if rax = success) -get file length: +end this thread: rax in: 3 - rdi in: file handle - rax out: file result - rdi out: file length (if rax = success) + edi in: exit code (signed, only used if this was last thread) -read from file: +get new pages: rax in: 4 - rdi in: pointer to request struct - rax out: file result - request struct: - qword: file handle - qword: start offset in bytes - qword: length in bytes - qword: pointer to buffer + rdi in: number of pages to allocate + rax out: start of first page + the allocated pages are next to each other, writable, and not executable. -end this process: +read key packet: rax in: 5 - edi in: exit code (signed) + eax out: key packet -get new pages: +create private socket: rax in: 6 - rdi in: number of pages to allocate - rax out: start of first page - the allocated pages are next to each other, writable, and not executable. + rax out: end 1 stream handle + rdi out: end 2 stream handle -close file: +create socket listener: rax in: 7 - rdi in: file handle + rdi in: pointer to id string + rsi in: id string length + rax out: stream result + rdi out: listener handle (if rax = 0) -read key packet: +stop socket listener: rax in: 8 - eax out: key packet + rdi in: listener handle + +accept socket connection: + rax in: 9 + rdi in: listener handle + rax out: stream result + rdi out: stream handle + +connect to socket: + rax in: 10 + rdi in: pointer to id string + rsi in: id string length + rax out: stream result + rdi out: stream handle (if rax = 0) + if the listener is closed while this syscall is blocked, rax is + set to "socket id not in use" and not "socket listener closed" + +close stream: + rax in: 11 + rdi in: stream handle + +seek stream: + returns "not seekable" for sockets + rax in: 12 + rdi in: stream handle + sil in: + 0 = relative to beginning + 1 = relative to end + 2 = relative to current position + rdx in: offset (signed) + rax out: stream result + +read from stream: + rax in: 13 + rdi in: stream handle + rsi in: count + rdx in: pointer to buffer + rax out: stream result + +write to stream: + rax in: 14 + rdi in: stream handle + rsi in: count + rdx in: pointer to buffer + rax out: stream result + +get stream length: + returns "not sized" for sockets + rax in: 15 + rdi in: stream handle + rax out: stream result + rdi out: stream length (if rax = success) + +start process: + rax in: 16 + rdi in: pointer to process start info struct + rax out: stream result + rdi out: process handle (if rax = success) + process start info struct: + qword: file path length + qword: pointer to file path + qword: count of environment variables + qword: pointer to array of environment variable structs + qword: count of gifted stream ends + qword: pointer to array of gifted stream structs + environment variable struct: + qword: name length + qword: pointer to name + qword: value length + qword: pointer to value + gifted stream struct: + qword: stream handle here + qword: new stream handle in child + new handle must be < 65536 + +end this process: + rax in: 17 + edi in: exit code (signed) + +set stream length: + returns "not sized" for sockets + rax in: 18 + rdi in: stream handle + rsi in: new length + rax out: stream result diff --git a/euler/include/cstdio b/euler/include/cstdio new file mode 100644 index 0000000..27073db --- /dev/null +++ b/euler/include/cstdio @@ -0,0 +1,15 @@ +#pragma once + +#include <euler/stream.hpp> +#include <stddef.h> + +namespace std { + + typedef euler::stream FILE; + + FILE *fopen(const char *filename, const char *mode); + int fclose(FILE *stream); + + size_t fread(void *buffer, size_t size, size_t count, FILE *stream); + +} diff --git a/euler/include/cstring b/euler/include/cstring new file mode 100644 index 0000000..45bc94e --- /dev/null +++ b/euler/include/cstring @@ -0,0 +1,8 @@ +#pragma once + +#include <stddef.h> + +namespace std { + size_t strlen(const char *str); + void *memcpy(void *dest, const void *src, size_t count); +} diff --git a/euler/include/euler/heap.hpp b/euler/include/euler/heap.hpp new file mode 100644 index 0000000..e2a4852 --- /dev/null +++ b/euler/include/euler/heap.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include <stdint.h> + +namespace euler { + void *alloc(uint64_t bytes); + void dealloc(void *start, uint64_t bytes); +} diff --git a/euler/include/euler/stream.hpp b/euler/include/euler/stream.hpp new file mode 100644 index 0000000..a364ec4 --- /dev/null +++ b/euler/include/euler/stream.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include <euler/syscall.hpp> + +namespace euler { + + class stream { + + public: + virtual ~stream(); + virtual bool try_read(void *into, uint64_t bytes) = 0; + virtual bool try_seek(__euler_seek_from from, int64_t offset) = 0; + + }; + + class file_stream : public stream { + + private: + + __euler_stream_handle handle; + bool is_readable; + bool is_writable; + uint64_t length; + uint64_t offset; + + uint8_t *buffer; + uint64_t buffer_offset; + uint64_t buffer_size; + bool buffer_dirty; + + void write_buffer(); + + public: + + bool good; + + file_stream( + __euler_stream_handle handle, bool is_readable, bool is_writable, + bool clear, bool seek_to_end); + + ~file_stream(); + + bool try_read(void *into, uint64_t bytes) override; + bool try_seek(__euler_seek_from from, int64_t offset) override; + + }; + +} diff --git a/euler/include/euler/syscall.hpp b/euler/include/euler/syscall.hpp new file mode 100644 index 0000000..9255642 --- /dev/null +++ b/euler/include/euler/syscall.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <stdint.h> + +enum __euler_stream_result : uint64_t { + __EULER_SR_SUCCESS = 0, + __EULER_SR_BAD_HANDLE, + __EULER_SR_IO_ERROR, + __EULER_SR_OUT_OF_BOUNDS, + __EULER_SR_DOES_NOT_EXIST, + __EULER_SR_NOT_A_REGULAR_FILE, + __EULER_SR_NOT_AN_EXECUTABLE, + __EULER_SR_NOT_WRITABLE, + __EULER_SR_NOT_SEEKABLE, + __EULER_SR_SOCKET_ID_ALREADY_IN_USE, + __EULER_SR_SOCKET_ID_NOT_IN_USE, + __EULER_SR_SOCKET_LISTENER_CLOSED, + __EULER_SR_OTHER_END_CLOSED, + __EULER_SR_ALREADY_EXISTS, + __EULER_SR_NOT_SIZED +}; + +enum __euler_seek_from : uint8_t { + __EULER_SF_BEGINNING = 0, + __EULER_SF_END, + __EULER_SF_CURRENT_POSITION +}; + +typedef uint64_t __euler_stream_handle; + +extern "C" __euler_stream_result __euler_open_file( + const char *path, uint64_t path_len, __euler_stream_handle &handle_out, + bool allow_creation, bool only_allow_creation); + +extern "C" [[noreturn]] void __euler_end_this_thread(int32_t exit_code); + +extern "C" __euler_stream_result __euler_seek_stream( + __euler_stream_handle handle, __euler_seek_from from, int64_t offset); + +extern "C" __euler_stream_result __euler_set_stream_length( + __euler_stream_handle handle, uint64_t new_length); + +extern "C" void __euler_close_stream(__euler_stream_handle handle); + +extern "C" __euler_stream_result __euler_get_stream_length( + __euler_stream_handle handle, uint64_t &length_out); + +extern "C" void *__euler_get_new_pages(uint64_t count); + +extern "C" __euler_stream_result __euler_write_to_stream( + __euler_stream_handle handle, uint64_t count, const void *buffer); + +extern "C" __euler_stream_result __euler_read_from_stream( + __euler_stream_handle handle, uint64_t count, void *buffer); + +extern "C" uint32_t *__euler_get_framebuffer( + uint32_t &width_out, uint32_t &height_out, uint32_t &pitch_out); + +extern "C" uint32_t __euler_encode_color(uint8_t r, uint8_t g, uint8_t b); + +extern "C" uint32_t __euler_read_key_packet(); diff --git a/euler/makefile b/euler/makefile new file mode 100644 index 0000000..3b8022d --- /dev/null +++ b/euler/makefile @@ -0,0 +1,29 @@ +LIBSTDCPP_SOURCES = euler/stream.cpp strings/strlen.cpp euler/syscall.asm \ + euler/entry.cpp io/fopen.cpp euler/gcc.asm memory/delete.cpp euler/heap.cpp \ + memory/new.cpp io/fclose.cpp io/fread.cpp strings/memcpy.cpp + +clean: + rm -rf build + +build/%.asm.o: source/%.asm + @mkdir -p $(@D) + $(HILBERT_NASM) $^ -o $@ + +build/%.cpp.o: source/%.cpp + @mkdir -p $(@D) + $(HILBERT_CC) -c $^ -o $@ + +build/crt0.o: build/empty.asm.o + cp $^ $@ + +build/libc.a: build/empty.asm.o + $(HILBERT_AR) rcs $@ $^ + +build/libg.a: build/empty.asm.o + $(HILBERT_AR) rcs $@ $^ + +build/libm.a: build/empty.asm.o + $(HILBERT_AR) rcs $@ $^ + +build/libstdc++.a: ${LIBSTDCPP_SOURCES:%=build/%.o} + $(HILBERT_AR) rcs $@ $^ diff --git a/euler/source/empty.asm b/euler/source/empty.asm new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/euler/source/empty.asm diff --git a/euler/source/euler/entry.cpp b/euler/source/euler/entry.cpp new file mode 100644 index 0000000..69611b7 --- /dev/null +++ b/euler/source/euler/entry.cpp @@ -0,0 +1,11 @@ +#include <euler/syscall.hpp> + +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 new file mode 100644 index 0000000..6fc6fd5 --- /dev/null +++ b/euler/source/euler/gcc.asm @@ -0,0 +1,53 @@ +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 new file mode 100644 index 0000000..f7d407f --- /dev/null +++ b/euler/source/euler/heap.cpp @@ -0,0 +1,66 @@ +#include <euler/syscall.hpp> +#include <euler/heap.hpp> + +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/stream.cpp b/euler/source/euler/stream.cpp new file mode 100644 index 0000000..950f3c5 --- /dev/null +++ b/euler/source/euler/stream.cpp @@ -0,0 +1,151 @@ +#include <euler/stream.hpp> +#include <cstring> + +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 new file mode 100644 index 0000000..34f2735 --- /dev/null +++ b/euler/source/euler/syscall.asm @@ -0,0 +1,102 @@ +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_read_key_packet + +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_read_key_packet: + mov rax, 5 + syscall + ret diff --git a/euler/source/io/fclose.cpp b/euler/source/io/fclose.cpp new file mode 100644 index 0000000..6f43f85 --- /dev/null +++ b/euler/source/io/fclose.cpp @@ -0,0 +1,10 @@ +#include <cstdio> + +namespace std { + + int fclose(FILE *stream) { + delete stream; + return 0; + } + +} diff --git a/euler/source/io/fopen.cpp b/euler/source/io/fopen.cpp new file mode 100644 index 0000000..8d47bf0 --- /dev/null +++ b/euler/source/io/fopen.cpp @@ -0,0 +1,54 @@ +#include <stdint.h> +#include <cstring> +#include <cstdio> + +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 new file mode 100644 index 0000000..e2d05b6 --- /dev/null +++ b/euler/source/io/fread.cpp @@ -0,0 +1,9 @@ +#include <cstdio> + +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/memory/delete.cpp b/euler/source/memory/delete.cpp new file mode 100644 index 0000000..e4cc288 --- /dev/null +++ b/euler/source/memory/delete.cpp @@ -0,0 +1,17 @@ +#include <euler/heap.hpp> + +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 new file mode 100644 index 0000000..931328f --- /dev/null +++ b/euler/source/memory/new.cpp @@ -0,0 +1,13 @@ +#include <euler/heap.hpp> + +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/strings/memcpy.cpp b/euler/source/strings/memcpy.cpp new file mode 100644 index 0000000..d5a1d6c --- /dev/null +++ b/euler/source/strings/memcpy.cpp @@ -0,0 +1,14 @@ +#include <stdint.h> +#include <cstring> + +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 new file mode 100644 index 0000000..7a6fe2a --- /dev/null +++ b/euler/source/strings/strlen.cpp @@ -0,0 +1,12 @@ +#include <cstring> + +namespace std { + + size_t strlen(const char *str) { + size_t len = 0; + while (str[len]) + ++len; + return len; + } + +} diff --git a/kernel/application.cpp b/kernel/application.cpp deleted file mode 100644 index af31a09..0000000 --- a/kernel/application.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include <hilbert/kernel/application.hpp> -#include <hilbert/kernel/paging.hpp> - -//TODO - scheduling. - -namespace hilbert::kernel::application { - - app_instance::app_instance() - : state(app_state::paused), framebuffer_vaddr(0) { - - uint64_t p4_vaddr; - paging::map_new_kernel_page(p4_vaddr, p4_paddr); - p4 = (uint64_t *)p4_vaddr; - - uint64_t p3_paddr; - uint64_t p3_vaddr; - paging::map_new_kernel_page(p3_vaddr, p3_paddr); - p3 = (uint64_t *)p3_vaddr; - - for (int i = 1; i < 511; ++i) - p4[i] = 0; - p4[0] = paging::encode_pte(p3_paddr, true, true, true); - p4[511] = paging::kernel_p4e; - - for (int i = 0; i < 512; ++i) { - p3[i] = 0; - p2s[i] = 0; - p1s[i] = 0; - p1es_to_free_on_exit[i] = 0; - } - - } - - void app_instance::map_page(uint64_t vaddr, uint64_t paddr, - bool write, bool execute, bool free_pram_on_exit - ) { - - uint64_t i = ((vaddr / 4096) / 512) / 512; - uint64_t j = ((vaddr / 4096) / 512) % 512; - uint64_t k = (vaddr / 4096) % 512; - - if (p2s[i] == 0) { - uint64_t p2_paddr; - uint64_t p2_vaddr; - paging::map_new_kernel_page(p2_vaddr, p2_paddr); - p3[i] = paging::encode_pte(p2_paddr, true, true, true); - p2s[i] = (uint64_t *)p2_vaddr; - p1s[i] = new uint64_t *[512]; - p1es_to_free_on_exit[i] = new bool *[512]; - for (int u = 0; u < 512; ++u) { - p2s[i][u] = 0; - p1s[i][u] = 0; - p1es_to_free_on_exit[i][u] = 0; - } - } - - if (p2s[i][j] == 0) { - uint64_t p1_paddr; - uint64_t p1_vaddr; - paging::map_new_kernel_page(p1_vaddr, p1_paddr); - p2s[i][j] = paging::encode_pte(p1_paddr, true, true, true); - p1s[i][j] = (uint64_t *)p1_vaddr; - p1es_to_free_on_exit[i][j] = new bool[512]; - for (int u = 0; u < 512; ++u) { - p1s[i][j][u] = 0; - p1es_to_free_on_exit[i][j][u] = false; - } - } - - p1s[i][j][k] = paging::encode_pte(paddr, true, write, execute); - p1es_to_free_on_exit[i][j][k] = free_pram_on_exit; - - } - - bool app_instance::is_page_owned(uint64_t vaddr) { - uint64_t i = ((vaddr / 4096) / 512) / 512; - uint64_t j = ((vaddr / 4096) / 512) % 512; - uint64_t k = (vaddr / 4096) % 512; - return - i < 512 && p1s[i] != 0 && p1s[i][j] != 0 && - p1s[i][j][k] != 0 && p1es_to_free_on_exit[i][j][k]; - } - - uint64_t app_instance::get_free_vaddr_pages(uint64_t count) { - uint64_t start = 0x200000 / 4096; - uint64_t length = 0; - while (start + length <= 0x8000000000 / 4096) { - if (length == count) - return start * 4096; - int i = ((start + length) / 512) / 512; - int j = ((start + length) / 512) % 512; - int k = (start + length) % 512; - if (p1s[i] == 0 || p1s[i][j] == 0 || p1s[i][j][k] == 0) - ++length; - else { - start += length + 1; - length = 0; - } - } - //TODO: handle out of memory - return 0; - } - - uint64_t app_instance::count_mapped_vram_pages() { - uint64_t count = 0; - for (int i = 0; i < 512; ++i) - if (p1s[i] != 0) - for (int j = 0; j < 512; ++j) - if (p1s[i][j] != 0) - for (int k = 0; k < 512; ++k) - if (p1s[i][j][k] != 0) - ++count; - return count; - } - - app_instance *running_app; - - static uint8_t correct_magic[16] = { - 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, - 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00 - }; - -#define READ(a, b, c) \ - { \ - storage::fs_result _result = file.read_file(a, b, c); \ - if (_result == storage::fs_result::device_error) \ - return create_app_result::device_error; \ - if (_result == storage::fs_result::fs_corrupt) \ - return create_app_result::fs_corrupt; \ - } - - struct load_info { - uint64_t foffset; - uint64_t fsize; - uint64_t vaddr; - uint64_t vpages; - bool writable; - bool executable; - }; - - create_app_result create_app( - const vfile::vfile &file, app_instance *&out, - const vfile::vfile &working_dir - ) { - - uint8_t magic[16]; - if (file.dir_entry.length < 64) - return create_app_result::app_corrupt; - READ(0, 8, magic) - READ(16, 8, magic + 8) - for (int i = 0; i < 16; ++i) - if (magic[i] != correct_magic[i]) - return create_app_result::app_corrupt; - - uint64_t entry_point; - uint64_t phead_start; - uint16_t phead_entry_size; - uint16_t phead_entry_count; - - READ(24, 8, &entry_point) - READ(32, 8, &phead_start) - READ(54, 2, &phead_entry_size) - READ(56, 2, &phead_entry_count) - - if (file.dir_entry.length < - phead_start + phead_entry_size * phead_entry_count) - return create_app_result::app_corrupt; - - utility::vector<load_info> load_infos; - - for (uint16_t i = 0; i < phead_entry_count; ++i) { - - uint64_t entry_start = phead_start + phead_entry_size * i; - - uint32_t seg_type; - READ(entry_start, 4, &seg_type) - if (seg_type != 1) - continue; - - uint64_t foffset; - uint64_t vaddr; - uint64_t fsize; - uint64_t vsize; - uint32_t flags; - - READ(entry_start + 8, 8, &foffset) - READ(entry_start + 16, 8, &vaddr) - READ(entry_start + 32, 8, &fsize) - READ(entry_start + 40, 8, &vsize) - READ(entry_start + 4, 4, &flags) - - if (vaddr & 4095) - return create_app_result::app_corrupt; - if (file.dir_entry.length < foffset + fsize) - return create_app_result::app_corrupt; - if (fsize > vsize) - return create_app_result::app_corrupt; - - if (vaddr < 0x200000) - return create_app_result::app_corrupt; - - uint64_t vpages = (vsize - 1) / 4096 + 1; - - if (vaddr + vpages * 4096 > 0x8000000000) - return create_app_result::app_corrupt; - - load_info info = { - .foffset = foffset, - .fsize = fsize, - .vaddr = vaddr, - .vpages = vpages, - .writable = (flags & 2) == 2, - .executable = (flags & 1) == 1 - }; - load_infos.add_end(info); - - } - - out = new app_instance(); - - for (unsigned i = 0; i < load_infos.count; ++i) { - const auto &info = load_infos.buffer[i]; - for (uint64_t j = 0; j < info.vpages; ++j) { - uint64_t paddr = paging::take_pram_page(); - out->map_page(info.vaddr + j * 4096, paddr, - info.writable, info.executable, true); - uint64_t kvaddr = paging::find_unmapped_vram_region(1); - paging::map_kernel_page(paddr, kvaddr, true, false); - storage::fs_result result = storage::fs_result::success; - if (info.fsize > j * 4096) { - if (info.fsize >= j * 4096 + 4096) - result = file.read_file( - info.foffset + j * 4096, 4096, (void *)kvaddr); - else { - int to_read = info.fsize - j * 4096; - result = file.read_file( - info.foffset + j * 4096, to_read, (void *)kvaddr); - uint8_t *blank = (uint8_t *)(kvaddr + to_read); - for (int i = 0; i < 4096 - to_read; ++i) - blank[i] = 0; - } - } - else { - uint8_t *blank = (uint8_t *)kvaddr; - for (int i = 0; i < 4096; ++i) - blank[i] = 0; - } - paging::unmap_kernel_page(kvaddr); - if (result == storage::fs_result::device_error) { - delete out; - return create_app_result::device_error; - } - if (result == storage::fs_result::fs_corrupt) { - delete out; - return create_app_result::fs_corrupt; - } - } - } - - for (uint64_t vaddr = 0x1000; vaddr < 0x1ff000; vaddr += 4096) { - uint64_t paddr = paging::take_pram_page(); - uint64_t kvaddr = paging::find_unmapped_vram_region(1); - paging::map_kernel_page(paddr, kvaddr, true, false); - uint8_t *p = (uint8_t *)kvaddr; - for (int i = 0; i < 4096; ++i) - p[i] = 0; - paging::unmap_kernel_page(kvaddr); - out->map_page(vaddr, paddr, true, false, true); - } - - out->saved_regs.rsp = 0x1ff000; - out->saved_regs.rip = entry_point; - - out->working_dir = working_dir; - - return create_app_result::success; - - } - -} diff --git a/kernel/framebuffer.cpp b/kernel/framebuffer.cpp deleted file mode 100644 index 08d0e16..0000000 --- a/kernel/framebuffer.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include <hilbert/kernel/application.hpp> -#include <hilbert/kernel/framebuffer.hpp> - -namespace hilbert::kernel::framebuffer { - - uint64_t paddr; - static uint32_t *vaddr; - int width; - int height; - int dword_pitch; - - void init_framebuffer(uint64_t paddr, uint64_t vaddr, - uint64_t width, uint64_t height, uint64_t pitch - ) { - - //TODO: assumes 32-bpp rgb - - framebuffer::paddr = paddr; - framebuffer::vaddr = (uint32_t *)vaddr; - framebuffer::width = width; - framebuffer::height = height; - dword_pitch = pitch / 4; - - } - - color encode_color(uint8_t r, uint8_t g, uint8_t b) { - return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; - } - - void set_pixel(int x, int y, color c) { - vaddr[y * dword_pitch + x] = c; - } - - void move_region( - int from_start_x, int from_start_y, int from_end_x, - int from_end_y, int to_start_x, int to_start_y - ) { - - int region_width = from_end_x - from_start_x; - int region_height = from_end_y - from_start_y; - - int from_start_offset = from_start_y * dword_pitch + from_start_x; - int to_start_offset = to_start_y * dword_pitch + to_start_x; - - if (from_start_offset > to_start_offset) - for (int y = 0; y < region_height; ++y) - for (int x = 0; x < region_width; ++x) - vaddr[to_start_offset + y * dword_pitch + x] = - vaddr[from_start_offset + y * dword_pitch + x]; - - else if (from_start_offset < to_start_offset) - for (int y = region_height - 1; y >= 0; --y) - for (int x = region_width - 1; x >= 0; --x) - vaddr[to_start_offset + y * dword_pitch + x] = - vaddr[from_start_offset + y * dword_pitch + x]; - - } - - void fill_region(int start_x, int start_y, int end_x, int end_y, color c) { - for (int y = start_y; y < end_y; ++y) - for (int x = start_x; x < end_x; ++x) - vaddr[y * dword_pitch + x] = c; - } - -} diff --git a/kernel/include/hilbert/kernel/application.hpp b/kernel/include/hilbert/kernel/application.hpp index 51f304d..7d48d8d 100644 --- a/kernel/include/hilbert/kernel/application.hpp +++ b/kernel/include/hilbert/kernel/application.hpp @@ -7,21 +7,165 @@ namespace hilbert::kernel::application { - void init_syscalls(); + class process; + class thread; - enum class app_state { + enum class thread_state { running, paused, - zombie + waiting }; - struct app_instance { + enum class stream_result { + success, + bad_handle, + io_error, + out_of_bounds, + does_not_exist, + not_a_regular_file, + not_an_executable, + not_writable, + not_seekable, + socket_id_already_used, + socket_id_not_in_use, + socket_listener_closed, + other_end_closed, + already_exists, + not_sized + }; + + enum class seek_origin { + beginning, + end, + current_position + }; + + class stream { + public: + virtual ~stream() {} + virtual stream_result seek(seek_origin origin, int64_t offset) = 0; + virtual stream_result read(uint64_t count, void *into) = 0; + virtual stream_result write(uint64_t count, const void *from) = 0; + virtual stream_result get_length(uint64_t &out) = 0; + virtual stream_result set_length(uint64_t to) = 0; + }; + + class vfile_stream : public stream { - utility::id_allocator<vfile::vfile> open_files; + private: + vfile::vfile file; + uint64_t offset; - vfile::vfile working_dir; + public: + vfile_stream(vfile::vfile &&file); + virtual stream_result seek(seek_origin origin, int64_t offset) override; + virtual stream_result read(uint64_t count, void *into) override; + virtual stream_result write(uint64_t count, const void *from) override; + virtual stream_result get_length(uint64_t &out) override; + virtual stream_result set_length(uint64_t to) override; - app_state state; + }; + + struct socket { + utility::queue<thread *> process_a_threads_waiting_to_read; + utility::queue<thread *> process_b_threads_waiting_to_read; + utility::queue<uint8_t> a_to_b; + utility::queue<uint8_t> b_to_a; + bool a_closed; + bool b_closed; + }; + + class socket_stream : public stream { + + private: + socket *sock; + bool are_we_b; + + utility::queue<thread *> &our_threads_waiting_to_read; + utility::queue<thread *> &their_threads_waiting_to_read; + utility::queue<uint8_t> &them_to_us; + utility::queue<uint8_t> &us_to_them; + bool &them_closed; + bool &us_closed; + + public: + socket_stream(socket *sock, bool are_we_b); + ~socket_stream(); + virtual stream_result seek(seek_origin origin, int64_t offset) override; + virtual stream_result read(uint64_t count, void *into) override; + virtual stream_result write(uint64_t count, const void *from) override; + virtual stream_result get_length(uint64_t &out) override; + virtual stream_result set_length(uint64_t to) override; + + }; + + struct string_pair { + utility::string a; + utility::string b; + }; + + struct socket_listener { + utility::string id; + utility::queue<thread *> waiting_to_accept_connection; + utility::queue<thread *> waiting_to_connect; + bool is_listening; + }; + + struct [[gnu::packed]] cpu_state { + + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + + uint64_t rflags; + uint64_t rip; + uint64_t cr3; + + //only used if in_syscall is true. starts at rsp. no red zone + //needs to be saved since save_thread_state doesn't use it. + void *kernel_stack_copy; + bool in_syscall; + + }; + + struct thread { + + //propogated to process on destruction if this is the last thread. + int exit_code; + ~thread(); + + process *the_process; + thread_state state; + //only valid if paused or waiting + cpu_state cpu; + + stream *just_connected_to; + stream *just_accepted; + + }; + + struct process { + + void end_process(unsigned exit_code); + void cleanup(); + + utility::list<thread *> threads; + utility::vector<string_pair> environment; + utility::id_allocator<stream *> open_streams; + utility::id_allocator<socket_listener *> socket_listeners; uint64_t *p4; uint64_t *p3; @@ -35,17 +179,10 @@ namespace hilbert::kernel::application { //set to 0 if none uint64_t framebuffer_vaddr; - //only valid if state is zombie + //only valid if there are no threads int32_t exit_code; - //only valid if state is paused - struct { - uint64_t rip; - uint64_t rsp; - //TODO: etc. - } saved_regs; - - app_instance(); + process(); //vaddr and paddr must be aligned, and vaddr must be < 0x0080.0000.0000 void map_page(uint64_t vaddr, uint64_t paddr, @@ -62,16 +199,22 @@ namespace hilbert::kernel::application { }; - extern app_instance *running_app; + extern utility::id_allocator<process *> *processes; + extern utility::queue<thread *> *paused_threads; + extern utility::queue<thread *> *threads_waiting_for_input; + extern thread *running_thread; + extern utility::list<socket_listener *> *all_socket_listeners; - enum class create_app_result { - success, - device_error, - app_corrupt, - fs_corrupt - }; + stream_result create_application( + const vfile::vfile &file, process *&process_out, thread *&thread_out); + + void init_applications(); + + //returns true when resumed, false right now. + //must be called from non-interruptable syscall context. + extern "C" bool save_thread_state(cpu_state &into); - create_app_result create_app(const vfile::vfile &file, - app_instance *&out, const vfile::vfile &working_dir); + //must be called from non-interruptable context + [[noreturn]] void resume_next(); } diff --git a/kernel/include/hilbert/kernel/framebuffer.hpp b/kernel/include/hilbert/kernel/framebuffer.hpp index fb28462..d4d6b1c 100644 --- a/kernel/include/hilbert/kernel/framebuffer.hpp +++ b/kernel/include/hilbert/kernel/framebuffer.hpp @@ -18,14 +18,6 @@ namespace hilbert::kernel::framebuffer { void set_pixel(int x, int y, color c); - //[from_start_x, from_end_x) x [from_start_y, from_end_y) - // -> [to_start_x, ...) x [to_start_y, ...). - //we assume from_start_x < from_end_x and from_start_y < from_end_y. - void move_region( - int from_start_x, int from_start_y, int from_end_x, - int from_end_y, int to_start_x, int to_start_y); - - //[start_x, end_x) x [start_y, end_y) - void fill_region(int start_x, int start_y, int end_x, int end_y, color c); + void fill_color(color c); } diff --git a/kernel/include/hilbert/kernel/input.hpp b/kernel/include/hilbert/kernel/input.hpp index cccb71f..2209ddc 100644 --- a/kernel/include/hilbert/kernel/input.hpp +++ b/kernel/include/hilbert/kernel/input.hpp @@ -19,6 +19,8 @@ namespace hilbert::kernel::input { }; extern utility::queue<uint32_t> *key_queue; + //notify a process waiting for input + void got_input(); //must be post switch to kernel page tables and mounting of file systems void init_input(); diff --git a/kernel/include/hilbert/kernel/panic.hpp b/kernel/include/hilbert/kernel/panic.hpp index 545d703..0478142 100644 --- a/kernel/include/hilbert/kernel/panic.hpp +++ b/kernel/include/hilbert/kernel/panic.hpp @@ -1,6 +1,5 @@ #pragma once namespace hilbert::kernel { - //prints to terminal and then halts. - [[noreturn]] void panic(const char *string_sz); + [[noreturn]] void panic(uint32_t code); } diff --git a/kernel/include/hilbert/kernel/terminal.hpp b/kernel/include/hilbert/kernel/terminal.hpp deleted file mode 100644 index 350d79b..0000000 --- a/kernel/include/hilbert/kernel/terminal.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include <hilbert/kernel/framebuffer.hpp> -#include <hilbert/kernel/utility.hpp> -#include <stddef.h> -#include <stdint.h> - -namespace hilbert::kernel::terminal { - - extern uint8_t *termfont; - extern uint64_t termfont_len; - - void init_terminal(); - - extern int width; - extern int height; - - extern int cursor_x; - extern int cursor_y; - - extern framebuffer::color bg_color; - extern framebuffer::color fg_color; - - void put_char(char ch); - void put_string(const utility::string &str); - void put_string_sz(const char *str); - - void put_int_decimal(uint64_t n, bool with_commas = true); - - void put_int_hex(uint64_t n, int digits, bool with_dots = true); - -} diff --git a/kernel/input.cpp b/kernel/input.cpp deleted file mode 100644 index 6bed7f5..0000000 --- a/kernel/input.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include <hilbert/kernel/input.hpp> -#include <hilbert/kernel/panic.hpp> -#include <hilbert/kernel/vfile.hpp> - -namespace hilbert::kernel::input { - - utility::queue<uint32_t> *key_queue; - - void init_input() { - key_queue = new utility::queue<uint32_t>(); - } - -} diff --git a/kernel/makefile b/kernel/makefile new file mode 100644 index 0000000..1cb2d52 --- /dev/null +++ b/kernel/makefile @@ -0,0 +1,19 @@ +SOURCES = \ + storage/bd/memory.cpp storage/fs/tarfs.cpp application.asm application.cpp \ + framebuffer.cpp interrupts.asm interrupts.cpp allocator.cpp storage.cpp \ + syscall.cpp utility.cpp paging.asm paging.cpp entry.cpp input.cpp panic.cpp \ + vfile.cpp + +build/%.asm.o: source/%.asm + @mkdir -p $(@D) + $(HILBERT_NASM) $^ -o $@ + +build/%.cpp.o: source/%.cpp + @mkdir -p $(@D) + $(HILBERT_CC) -c -ffreestanding -mcmodel=kernel -I ${LIMINE_DIR} $^ -o $@ + +build/kernel.elf: $(SOURCES:%=build/%.o) + $(HILBERT_LD) -T link.ld $^ -o $@ + +clean: + rm -rf build diff --git a/kernel/panic.cpp b/kernel/panic.cpp deleted file mode 100644 index 233fcf4..0000000 --- a/kernel/panic.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include <hilbert/kernel/terminal.hpp> -#include <hilbert/kernel/panic.hpp> - -namespace hilbert::kernel { - [[noreturn]] void panic(const char *string_sz) { - terminal::put_string_sz(string_sz); - while (1) - asm ("hlt"); - } -} diff --git a/kernel/allocator.cpp b/kernel/source/allocator.cpp index 324f992..324f992 100644 --- a/kernel/allocator.cpp +++ b/kernel/source/allocator.cpp diff --git a/kernel/source/application.asm b/kernel/source/application.asm new file mode 100644 index 0000000..ed8b190 --- /dev/null +++ b/kernel/source/application.asm @@ -0,0 +1,171 @@ +bits 64 + +extern do_syscall + +section .text + +syscall_entry: + mov r11, rsp + mov rsp, 0xfffffffffffff000 + push r11 + push rcx + + push rdx + push rsi + push rdi + push rax + + mov rdi, rsp + lea rsi, [rsp + 8] + lea rdx, [rsp + 16] + lea rcx, [rsp + 24] + + call do_syscall + + pop rax + pop rdi + pop rsi + pop rdx + + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + or r11, 0x200 + pop rcx + pop rsp + + o64 sysret + +global init_applications_asm +init_applications_asm: + + ;efer <- efer | 0x1 + mov rcx, 0xc0000080 + rdmsr + or al, 1 + wrmsr + + ;lstar <- syscall_entry + mov rdx, syscall_entry + mov eax, edx + shr rdx, 32 + mov ecx, 0xc0000082 + wrmsr + + ;star <- 0x0030.0028.0000.0000 + mov edx, 0x00300028 + xor eax, eax + mov ecx, 0xc0000081 + wrmsr + + ;sfmask <- 0x0000.0000.0000.0200 (if) + xor edx, edx + mov eax, 0x200 + mov ecx, 0xc0000084 + wrmsr + + ret + +section .bss + +resume_stack: + resb 4096 + +section .text + +extern restore_syscall_stack +;rdi = pointer to copy +;rsi = intended rsp + +global resume_thread +resume_thread: +;rdi = ptr to cpu_state +;rdi is not inside stack +;interrupts are disabled + + mov al, byte [rdi + 160] ;in_syscall + test al, al + jnz .in_syscall + + mov rax, 0x3b + mov rbx, 0x43 + +.common: + push rax + mov rax, qword [rdi + 56] ;rsp + push rax + mov rax, qword [rdi + 128] ;rflags + push rax + push rbx + mov rax, qword [rdi + 136] ;rip + push rax + + mov rax, qword [rdi + 144] ;cr3 + mov cr3, rax + + mov rax, qword [rdi] + mov rbx, qword [rdi + 8] + mov rcx, qword [rdi + 16] + mov rdx, qword [rdi + 24] + mov rsi, qword [rdi + 40] + mov rbp, qword [rdi + 48] + mov r8, qword [rdi + 64] + mov r9, qword [rdi + 72] + mov r10, qword [rdi + 80] + mov r11, qword [rdi + 88] + mov r12, qword [rdi + 96] + mov r13, qword [rdi + 104] + mov r14, qword [rdi + 112] + mov r15, qword [rdi + 120] + mov rdi, qword [rdi + 32] + + iretq + +.in_syscall: + mov rsp, resume_stack + 4096 + + push rdi + mov rsi, qword [rdi + 56] ;rsp + mov rdi, qword [rdi + 152] ;kernel_stack_copy + call restore_syscall_stack + pop rdi + + mov rax, 0x30 + mov rbx, 0x28 + jmp .common + +extern copy_syscall_stack +;rdi = bottom + +global save_thread_state +save_thread_state: +;rdi = pointer to cpu state structure + + ;only saving registers that need to be preserved by this function + mov qword [rdi + 8], rbx + mov qword [rdi + 48], rbp + mov qword [rdi + 56], rsp + mov qword [rdi + 96], r12 + mov qword [rdi + 104], r13 + mov qword [rdi + 112], r14 + mov qword [rdi + 120], r15 + + mov qword [rdi + 136], .resume_to ;rip + mov rax, cr3 + mov qword [rdi + 144], rax ;cr3 + + push rdi + lea rdi, [rsp + 8] + call copy_syscall_stack + pop rdi + + mov qword [rdi + 152], rax ;kernel_stack_copy + mov byte [rdi + 160], 0x01 ;in_syscall + + xor al, al + ret + +.resume_to: + mov al, 0x01 + ret diff --git a/kernel/source/application.cpp b/kernel/source/application.cpp new file mode 100644 index 0000000..c3ce2f1 --- /dev/null +++ b/kernel/source/application.cpp @@ -0,0 +1,550 @@ +#include <hilbert/kernel/application.hpp> +#include <hilbert/kernel/paging.hpp> +#include <hilbert/kernel/panic.hpp> + +//TODO - scheduling. + +namespace hilbert::kernel::application { + + process::process() : framebuffer_vaddr(0) { + + uint64_t p4_vaddr; + paging::map_new_kernel_page(p4_vaddr, p4_paddr); + p4 = (uint64_t *)p4_vaddr; + + uint64_t p3_paddr; + uint64_t p3_vaddr; + paging::map_new_kernel_page(p3_vaddr, p3_paddr); + p3 = (uint64_t *)p3_vaddr; + + for (int i = 1; i < 511; ++i) + p4[i] = 0; + p4[0] = paging::encode_pte(p3_paddr, true, true, true); + p4[511] = paging::kernel_p4e; + + for (int i = 0; i < 512; ++i) { + p3[i] = 0; + p2s[i] = 0; + p1s[i] = 0; + p1es_to_free_on_exit[i] = 0; + } + + } + + void process::map_page(uint64_t vaddr, uint64_t paddr, + bool write, bool execute, bool free_pram_on_exit + ) { + + uint64_t i = ((vaddr / 4096) / 512) / 512; + uint64_t j = ((vaddr / 4096) / 512) % 512; + uint64_t k = (vaddr / 4096) % 512; + + if (p2s[i] == 0) { + uint64_t p2_paddr; + uint64_t p2_vaddr; + paging::map_new_kernel_page(p2_vaddr, p2_paddr); + p3[i] = paging::encode_pte(p2_paddr, true, true, true); + p2s[i] = (uint64_t *)p2_vaddr; + p1s[i] = new uint64_t *[512]; + p1es_to_free_on_exit[i] = new bool *[512]; + for (int u = 0; u < 512; ++u) { + p2s[i][u] = 0; + p1s[i][u] = 0; + p1es_to_free_on_exit[i][u] = 0; + } + } + + if (p2s[i][j] == 0) { + uint64_t p1_paddr; + uint64_t p1_vaddr; + paging::map_new_kernel_page(p1_vaddr, p1_paddr); + p2s[i][j] = paging::encode_pte(p1_paddr, true, true, true); + p1s[i][j] = (uint64_t *)p1_vaddr; + p1es_to_free_on_exit[i][j] = new bool[512]; + for (int u = 0; u < 512; ++u) { + p1s[i][j][u] = 0; + p1es_to_free_on_exit[i][j][u] = false; + } + } + + p1s[i][j][k] = paging::encode_pte(paddr, true, write, execute); + p1es_to_free_on_exit[i][j][k] = free_pram_on_exit; + + } + + bool process::is_page_owned(uint64_t vaddr) { + uint64_t i = ((vaddr / 4096) / 512) / 512; + uint64_t j = ((vaddr / 4096) / 512) % 512; + uint64_t k = (vaddr / 4096) % 512; + return + i < 512 && p1s[i] != 0 && p1s[i][j] != 0 && + p1s[i][j][k] != 0 && p1es_to_free_on_exit[i][j][k]; + } + + uint64_t process::get_free_vaddr_pages(uint64_t count) { + uint64_t start = 0x200000 / 4096; + uint64_t length = 0; + while (start + length <= 0x8000000000 / 4096) { + if (length == count) + return start * 4096; + int i = ((start + length) / 512) / 512; + int j = ((start + length) / 512) % 512; + int k = (start + length) % 512; + if (p1s[i] == 0 || p1s[i][j] == 0 || p1s[i][j][k] == 0) + ++length; + else { + start += length + 1; + length = 0; + } + } + //TODO: handle out of memory + return 0; + } + + uint64_t process::count_mapped_vram_pages() { + uint64_t count = 0; + for (int i = 0; i < 512; ++i) + if (p1s[i] != 0) + for (int j = 0; j < 512; ++j) + if (p1s[i][j] != 0) + for (int k = 0; k < 512; ++k) + if (p1s[i][j][k] != 0) + ++count; + return count; + } + + utility::id_allocator<process *> *processes; + utility::queue<thread *> *paused_threads; + utility::queue<thread *> *threads_waiting_for_input; + thread *running_thread; + utility::list<socket_listener *> *all_socket_listeners; + + static uint8_t correct_magic[16] = { + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, + 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00 + }; + +#define READ(a, b, c) \ + { \ + storage::fs_result _result = file.read_file(a, b, c); \ + if (_result != storage::fs_result::success) \ + return stream_result::io_error; \ + } + +#define TRY_MAR(expr) \ + { \ + storage::fs_result _result = expr; \ + if (_result != storage::fs_result::success) { \ + delete process_out; \ + return stream_result::io_error; \ + } \ + } + + struct load_info { + uint64_t foffset; + uint64_t fsize; + uint64_t vaddr; + uint64_t voffset; + uint64_t vpages; + bool writable; + bool executable; + }; + + storage::fs_result map_and_read( + const vfile::vfile &file, process *process, uint64_t vaddr, uint64_t faddr, + uint64_t len, bool writable, bool executable + ) { + + uint64_t page_vaddr = vaddr & ~4095; + int at_start = vaddr & 4095; + int at_end = 4096 - len - at_start; + + uint64_t page_paddr = paging::take_pram_page(); + process->map_page(page_vaddr, page_paddr, writable, executable, true); + uint64_t page_kvaddr = paging::find_unmapped_vram_region(1); + paging::map_kernel_page(page_paddr, page_kvaddr, true, false); + + storage::fs_result result = storage::fs_result::success; + + if (at_start) { + uint8_t *blank = (uint8_t *)page_kvaddr; + for (int i = 0; i < at_start; ++i) + blank[i] = 0; + } + + if (len != 0) + result = file.read_file(faddr, len, (void *)(page_kvaddr + at_start)); + + if (at_end) { + uint8_t *blank = (uint8_t *)(page_kvaddr + at_start + len); + for (int i = 0; i < at_end; ++i) + blank[i] = 0; + } + + paging::unmap_kernel_page(page_kvaddr); + return result; + + } + + stream_result create_application( + const vfile::vfile &file, process *&process_out, thread *&thread_out + ) { + + uint8_t magic[16]; + if (file.dir_entry.type != storage::file_type::regular_file) + return stream_result::not_a_regular_file; + if (file.dir_entry.length < 64) + return stream_result::not_an_executable; + READ(0, 8, magic) + READ(16, 8, magic + 8) + for (int i = 0; i < 16; ++i) + if (magic[i] != correct_magic[i]) + return stream_result::not_an_executable; + + uint64_t entry_point; + uint64_t phead_start; + uint16_t phead_entry_size; + uint16_t phead_entry_count; + + READ(24, 8, &entry_point) + READ(32, 8, &phead_start) + READ(54, 2, &phead_entry_size) + READ(56, 2, &phead_entry_count) + + if (file.dir_entry.length < + phead_start + phead_entry_size * phead_entry_count) + return stream_result::not_an_executable; + + utility::vector<load_info> load_infos; + + for (uint16_t i = 0; i < phead_entry_count; ++i) { + + uint64_t entry_start = phead_start + phead_entry_size * i; + + uint32_t seg_type; + READ(entry_start, 4, &seg_type) + if (seg_type != 1) + continue; + + uint64_t foffset; + uint64_t vaddr; + uint64_t voffset; + uint64_t fsize; + uint64_t vsize; + uint32_t flags; + + READ(entry_start + 8, 8, &foffset) + READ(entry_start + 16, 8, &vaddr) + READ(entry_start + 32, 8, &fsize) + READ(entry_start + 40, 8, &vsize) + READ(entry_start + 4, 4, &flags) + + voffset = vaddr % 4096; + vaddr -= voffset; + + if (vsize == 0) + continue; + + if (file.dir_entry.length < foffset + fsize) + return stream_result::not_an_executable; + if (fsize > vsize) + return stream_result::not_an_executable; + + if (vaddr < 0x200000) + return stream_result::not_an_executable; + + uint64_t vpages = (voffset + vsize - 1) / 4096 + 1; + + if (vaddr + vpages * 4096 > 0x8000000000) + return stream_result::not_an_executable; + + load_info info = { + .foffset = foffset, + .fsize = fsize, + .vaddr = vaddr, + .voffset = voffset, + .vpages = vpages, + .writable = (flags & 2) == 2, + .executable = (flags & 1) == 1 + }; + load_infos.add_end(info); + + } + + process_out = new process(); + + for (unsigned i = 0; i < load_infos.count; ++i) { + const auto &info = load_infos.buffer[i]; + + uint64_t vaddr = info.vaddr + info.voffset; + uint64_t faddr = info.foffset; + uint64_t v_remaining = info.vpages * 4096 - info.voffset; + uint64_t f_remaining = info.fsize; + + if (info.voffset != 0) { + int to_read = info.fsize < 4096 - info.voffset + ? info.fsize : 4096 - info.voffset; + if (to_read > 0) { + TRY_MAR(map_and_read(file, process_out, vaddr, faddr, to_read, + info.writable, info.executable)) + vaddr += to_read; + faddr += to_read; + v_remaining -= to_read; + f_remaining -= to_read; + } + } + + while (f_remaining > 0) { + int to_read = f_remaining < 4096 ? f_remaining : 4096; + TRY_MAR(map_and_read(file, process_out, vaddr, faddr, to_read, + info.writable, info.executable)) + vaddr += to_read; + faddr += to_read; + v_remaining -= to_read; + f_remaining -= to_read; + } + + if (vaddr & 4095) { + v_remaining -= 4096 - (vaddr & 4095); + vaddr += 4096 - (vaddr & 4095); + } + + while (v_remaining > 0) { + map_and_read( + file, process_out, vaddr, 0, 0, info.writable, info.executable); + vaddr += 4096; + v_remaining -= 4096; + } + + } + + for (uint64_t vaddr = 0x1000; vaddr < 0x1ff000; vaddr += 4096) { + uint64_t paddr = paging::take_pram_page(); + uint64_t kvaddr = paging::find_unmapped_vram_region(1); + paging::map_kernel_page(paddr, kvaddr, true, false); + uint8_t *p = (uint8_t *)kvaddr; + for (int i = 0; i < 4096; ++i) + p[i] = 0; + paging::unmap_kernel_page(kvaddr); + process_out->map_page(vaddr, paddr, true, false, true); + } + + thread_out = new thread(); + process_out->threads.insert_end(thread_out); + thread_out->the_process = process_out; + + thread_out->state = thread_state::paused; + + thread_out->cpu.rax = 0; + thread_out->cpu.rbx = 0; + thread_out->cpu.rcx = 0; + thread_out->cpu.rdx = 0; + thread_out->cpu.rdi = 0; + thread_out->cpu.rsi = 0; + thread_out->cpu.rbp = 0; + thread_out->cpu.rsp = 0x1ff000; + thread_out->cpu.r8 = 0; + thread_out->cpu.r9 = 0; + thread_out->cpu.r10 = 0; + thread_out->cpu.r11 = 0; + thread_out->cpu.r12 = 0; + thread_out->cpu.r13 = 0; + thread_out->cpu.r14 = 0; + thread_out->cpu.r15 = 0; + + thread_out->cpu.rflags = 0x200; + thread_out->cpu.rip = entry_point; + thread_out->cpu.cr3 = process_out->p4_paddr; + thread_out->cpu.in_syscall = false; + + return stream_result::success; + + } + + extern "C" void init_applications_asm(); + + void init_applications() { + processes = new utility::id_allocator<process *>(); + paused_threads = new utility::queue<thread *>(); + threads_waiting_for_input = new utility::queue<thread *>(); + all_socket_listeners = new utility::list<socket_listener *>(); + init_applications_asm(); + } + + //only called from non-interruptable contexts. + //cpu argument not on stack. + extern "C" [[noreturn]] void resume_thread(const cpu_state &cpu); + + extern "C" void *copy_syscall_stack(uint8_t *rsp) { + uint64_t size = 0xfffffffffffff000 - (uint64_t)rsp; + uint8_t *buffer = new uint8_t[size]; + for (uint64_t i = 0; i < size; ++i) + buffer[i] = rsp[i]; + return buffer; + } + + extern "C" void restore_syscall_stack(const uint8_t *from, uint8_t *rsp) { + uint64_t size = 0xfffffffffffff000 - (uint64_t)rsp; + for (uint64_t i = 0; i < size; ++i) + rsp[i] = from[i]; + delete[] from; + } + + thread::~thread() { + for (auto *p = the_process->threads.first; p; p = p->next) + if (p->value == this) { + the_process->threads.remove(p); + break; + } + if (the_process->threads.first == 0) { + the_process->exit_code = exit_code; + the_process->cleanup(); + } + if (state != thread_state::running) + panic(0x9af5e6); + } + + [[noreturn]] void resume_next() { + while (paused_threads->count == 0) + asm volatile ("sti\nhlt\ncli"); + auto *t = paused_threads->take(); + running_thread = t; + t->state = thread_state::running; + resume_thread(t->cpu); + } + + void process::end_process(unsigned exit_code) { + while (threads.first != 0) + delete threads.first->value; + this->exit_code = exit_code; + cleanup(); + } + + void process::cleanup() { + //TODO + panic(0x9af5e6); + } + + socket_stream::socket_stream(socket *sock, bool are_we_b) + : sock(sock), are_we_b(are_we_b), + our_threads_waiting_to_read(are_we_b + ? sock->process_b_threads_waiting_to_read + : sock->process_a_threads_waiting_to_read), + their_threads_waiting_to_read(are_we_b + ? sock->process_a_threads_waiting_to_read + : sock->process_b_threads_waiting_to_read), + them_to_us(are_we_b ? sock->a_to_b : sock->b_to_a), + us_to_them(are_we_b ? sock->b_to_a : sock->a_to_b), + them_closed(are_we_b ? sock->a_closed : sock->b_closed), + us_closed(are_we_b ? sock->b_closed : sock->a_closed) {} + + stream_result socket_stream::seek(seek_origin, int64_t) { + return stream_result::not_seekable; + } + + stream_result socket_stream::read(uint64_t count, void *into) { + uint8_t *buffer = (uint8_t *)into; + for (uint64_t i = 0; i < count; ++i) { + while (them_to_us.count == 0) { + if (them_closed) + return stream_result::other_end_closed; + if (!save_thread_state(running_thread->cpu)) { + running_thread->state = thread_state::waiting; + our_threads_waiting_to_read.insert(running_thread); + resume_next(); + } + } + buffer[i] = them_to_us.take(); + } + return stream_result::success; + } + + stream_result socket_stream::write(uint64_t count, const void *from) { + if (them_closed) + return stream_result::other_end_closed; + const uint8_t *buffer = (const uint8_t *)from; + for (uint64_t i = 0; i < count; ++i) { + if (their_threads_waiting_to_read.count > 0) { + auto *ot = their_threads_waiting_to_read.take(); + ot->state = thread_state::paused; + paused_threads->insert(ot); + } + us_to_them.insert(buffer[i]); + } + return stream_result::success; + } + + stream_result socket_stream::get_length(uint64_t &) { + return stream_result::not_sized; + } + + stream_result socket_stream::set_length(uint64_t) { + return stream_result::not_sized; + } + + socket_stream::~socket_stream() { + if (our_threads_waiting_to_read.count > 0) + panic(0x9af5e6); + if (them_closed) + delete sock; + else { + us_closed = true; + while (their_threads_waiting_to_read.count > 0) { + auto *t = their_threads_waiting_to_read.take(); + t->state = thread_state::paused; + paused_threads->insert(t); + } + } + } + + vfile_stream::vfile_stream(vfile::vfile &&file) + : file(utility::move(file)), offset(0) {} + + stream_result vfile_stream::seek(seek_origin origin, int64_t offset) { + uint64_t start_at = {}; + switch (origin) { + case seek_origin::beginning: + start_at = 0; + break; + case seek_origin::end: + start_at = file.dir_entry.length; + break; + case seek_origin::current_position: + start_at = this->offset; + break; + } + if (offset < 0 && (uint64_t)-offset > start_at) + return stream_result::out_of_bounds; + if (offset + start_at > file.dir_entry.length) + return stream_result::out_of_bounds; + this->offset = start_at + offset; + return stream_result::success; + } + + stream_result vfile_stream::read(uint64_t count, void *into) { + if (offset + count > file.dir_entry.length) + return stream_result::out_of_bounds; + if (file.read_file(offset, count, into) != storage::fs_result::success) + return stream_result::io_error; + offset += count; + return stream_result::success; + } + + stream_result vfile_stream::write(uint64_t count, const void *from) { + if (offset + count > file.dir_entry.length) + return stream_result::out_of_bounds; + (void)from; + panic(0x9af5e6); + } + + stream_result vfile_stream::get_length(uint64_t &out) { + out = file.dir_entry.length; + return stream_result::success; + } + + stream_result vfile_stream::set_length(uint64_t to) { + (void)to; + panic(0x9af5e6); + } + +} diff --git a/kernel/entry.cpp b/kernel/source/entry.cpp index cc74e69..820b107 100644 --- a/kernel/entry.cpp +++ b/kernel/source/entry.cpp @@ -2,12 +2,11 @@ #include <hilbert/kernel/storage/fs/tarfs.hpp> #include <hilbert/kernel/application.hpp> #include <hilbert/kernel/framebuffer.hpp> -#include <hilbert/kernel/terminal.hpp> #include <hilbert/kernel/paging.hpp> #include <hilbert/kernel/input.hpp> #include <hilbert/kernel/panic.hpp> #include <hilbert/kernel/vfile.hpp> -#include "../limine/limine.h" +#include <limine.h> using namespace hilbert::kernel; @@ -37,34 +36,20 @@ static volatile limine_hhdm_request hhdm_request { .response = 0 }; -static limine_internal_module initfs_module = { - .path = "initfs.tgz", - .cmdline = "initfs", - .flags = LIMINE_INTERNAL_MODULE_REQUIRED | LIMINE_INTERNAL_MODULE_COMPRESSED -}; - -static limine_internal_module termfont_module = { - .path = "termfont.psf", - .cmdline = "termfont", - .flags = LIMINE_INTERNAL_MODULE_REQUIRED -}; - -static limine_internal_module *internal_modules[] = { - &initfs_module, &termfont_module -}; - static volatile limine_module_request module_request = { .id = LIMINE_MODULE_REQUEST, .revision = 2, .response = 0, - .internal_module_count = 2, - .internal_modules = internal_modules + .internal_module_count = 0, + .internal_modules = 0 }; bool try_map_module_by_cmdline( const char *cmdline, void *&vaddr_out, uint64_t &len_out ) { auto response = module_request.response; + if (!response) + return false; for (uint64_t i = 0; i < response->module_count; ++i) { limine_file *file = response->modules[i]; for (uint64_t j = 0; cmdline[j] == file->cmdline[j]; ++j) @@ -108,6 +93,8 @@ uint64_t initfs_len; extern "C" void load_gdt_and_idt(); +static bool have_initfs; + extern "C" [[noreturn]] void entry() { //TODO?: maybe we should check if the limine requests were @@ -166,11 +153,8 @@ extern "C" [[noreturn]] void entry() { for (uint64_t i = 0; i < fb_end - fb_start; i += 4096) paging::map_kernel_page(fb_start + i, fb_vaddr + i, true, false); - //initfs and termfont - these are required modules - //so there is no worry about them not being present. - try_map_module_by_cmdline("initfs", (void *&)initfs, initfs_len); - try_map_module_by_cmdline( - "termfont", (void *&)terminal::termfont, terminal::termfont_len); + have_initfs = + try_map_module_by_cmdline("initfs", (void *&)initfs, initfs_len); //set up framebuffer and terminal: //TODO: assumes framebuffer is 32-bpp rgb @@ -186,12 +170,13 @@ extern "C" [[noreturn]] void entry() { } -extern "C" [[noreturn]] void start_user_mode( - uint64_t rip, uint64_t rsp, uint64_t p4_paddr); - [[noreturn]] static void with_kernel_p4() { - terminal::init_terminal(); + if (!have_initfs) + panic(0x5f8860); + + input::init_input(); + application::init_applications(); auto *initfs_bd = new storage::bd::memory(initfs, initfs_len); auto *initfs_fs = new storage::fs::tarfs_instance(initfs_bd); @@ -204,27 +189,34 @@ extern "C" [[noreturn]] void start_user_mode( if (initfs_fs->get_root_node(initfs_root.dir_entry.node) != storage::fs_result::success) - panic("failed to get root node of initfs."); + panic(0x48a6ed); vfile::set_root(initfs_root); - input::init_input(); - - utility::string init_path_string("/bin/init.elf", 13); + utility::string init_path_string("/bin/init", 9); vfile::canon_path init_path; vfile::canonize_path(init_path_string, init_path); vfile::vfile init_file; if (vfile::lookup_path(init_path, init_file, true) != storage::fs_result::success) - panic("failed to look up /bin/init.elf."); - - application::app_instance *init; - if (application::create_app(init_file, init, initfs_root) != - application::create_app_result::success) - panic("failed to parse /bin/init.elf."); - - application::running_app = init; - start_user_mode(init->saved_regs.rip, init->saved_regs.rsp, init->p4_paddr); + panic(0x7e874d); + + application::process *init_process; + application::thread *init_thread; + if (application::create_application(init_file, init_process, init_thread) != + application::stream_result::success) + panic(0xc39db3); + + init_process->environment.add_end({ + .a = utility::string("ARGC", 4), + .b = utility::string("1", 1)}); + init_process->environment.add_end({ + .a = utility::string("ARGV0", 5), + .b = utility::string("/bin/init", 9)}); + + init_thread->state = application::thread_state::paused; + application::paused_threads->insert(init_thread); + application::resume_next(); } diff --git a/kernel/source/framebuffer.cpp b/kernel/source/framebuffer.cpp new file mode 100644 index 0000000..ab1b3d7 --- /dev/null +++ b/kernel/source/framebuffer.cpp @@ -0,0 +1,40 @@ +#include <hilbert/kernel/application.hpp> +#include <hilbert/kernel/framebuffer.hpp> + +namespace hilbert::kernel::framebuffer { + + uint64_t paddr; + static uint32_t *vaddr; + int width; + int height; + int dword_pitch; + + void init_framebuffer(uint64_t paddr, uint64_t vaddr, + uint64_t width, uint64_t height, uint64_t pitch + ) { + + //TODO: assumes 32-bpp rgb + + framebuffer::paddr = paddr; + framebuffer::vaddr = (uint32_t *)vaddr; + framebuffer::width = width; + framebuffer::height = height; + dword_pitch = pitch / 4; + + } + + color encode_color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; + } + + void set_pixel(int x, int y, color c) { + vaddr[y * dword_pitch + x] = c; + } + + void fill_color(color c) { + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + vaddr[y * dword_pitch + x] = c; + } + +} diff --git a/kernel/source/input.cpp b/kernel/source/input.cpp new file mode 100644 index 0000000..696cb13 --- /dev/null +++ b/kernel/source/input.cpp @@ -0,0 +1,22 @@ +#include <hilbert/kernel/application.hpp> +#include <hilbert/kernel/input.hpp> +#include <hilbert/kernel/panic.hpp> +#include <hilbert/kernel/vfile.hpp> + +namespace hilbert::kernel::input { + + utility::queue<uint32_t> *key_queue; + + void init_input() { + key_queue = new utility::queue<uint32_t>(); + } + + void got_input() { + if (application::threads_waiting_for_input->count > 0) { + auto *t = application::threads_waiting_for_input->take(); + t->state = application::thread_state::paused; + application::paused_threads->insert(t); + } + } + +} diff --git a/kernel/interrupts.asm b/kernel/source/interrupts.asm index c096ddb..babc020 100644 --- a/kernel/interrupts.asm +++ b/kernel/source/interrupts.asm @@ -192,20 +192,18 @@ exception_common: set_isr: ;rdi - index -;sil - 1 if this is a trap, 0 if it is an interrupt -;rdx - isr pointer +;rsi - isr pointer shl rdi, 4 add rdi, idt - mov word [rdi], dx - shr rdx, 16 - mov word [rdi + 6], dx - shr rdx, 16 - mov dword [rdi + 8], edx + mov word [rdi], si + shr rsi, 16 + mov word [rdi + 6], si + shr rsi, 16 + mov dword [rdi + 8], esi - or sil, 0x8e - mov byte [rdi + 5], sil + mov byte [rdi + 5], 0x8e mov word [rdi + 2], 0x28 mov byte [rdi + 4], 1 @@ -281,8 +279,7 @@ load_gdt_and_idt: mov rdi, rcx dec rdi - mov sil, 1 - mov rdx, qword [exception_isrs + rdi * 8] + mov rsi, qword [exception_isrs + rdi * 8] call set_isr loop .loop @@ -312,8 +309,7 @@ load_gdt_and_idt: out 0xa1, al mov rdi, 0x21 - xor sil, sil - mov rdx, keyboard_isr + mov rsi, keyboard_isr call set_isr ;set keyboard config diff --git a/kernel/interrupts.cpp b/kernel/source/interrupts.cpp index cd57c4f..6e22121 100644 --- a/kernel/interrupts.cpp +++ b/kernel/source/interrupts.cpp @@ -1,4 +1,3 @@ -#include <hilbert/kernel/terminal.hpp> #include <hilbert/kernel/input.hpp> #include <hilbert/kernel/panic.hpp> @@ -36,93 +35,15 @@ struct [[gnu::packed]] exception_info_t { extern exception_info_t exception_info; -static const char *exception_types[] = { - "division error", - "", - "non-maskable interrupt", - "", - "", - "", - "invalid opcode", - "", - "double fault (uh oh)", - "", - "", - "", - "stack fault", - "general protection fault", - "page fault", - "" -}; - -static const char *flag_names[] = { - " cf", - "", - " pf", - "", - " af", - "", - " zf", - " sf", - " tf", - " if", - " df", - " of", - "", - "", - " nt", - " md" -}; - -static void print_line(const char *r1, const char *r2, uint64_t r1v, uint64_t r2v) { - terminal::put_string_sz("\n "); - terminal::put_string_sz(r1); - terminal::put_string_sz(": 0x"); - terminal::put_int_hex(r1v, 16); - terminal::put_string_sz(" "); - terminal::put_string_sz(r2); - terminal::put_string_sz(": 0x"); - terminal::put_int_hex(r2v, 16); -} - extern "C" [[noreturn]] void print_exception() { - terminal::put_string_sz("exception handler:\n type: "); - terminal::put_string_sz(exception_types[exception_info.exception_number]); - terminal::put_string_sz(" (0x"); - terminal::put_int_hex(exception_info.exception_number, 2); - terminal::put_char(')'); - - if (exception_info.has_error == 1) { - terminal::put_string_sz("\n error code: 0x"); - terminal::put_int_hex(exception_info.error, 16); - } - - terminal::put_string_sz("\n flags:"); - if (exception_info.rflags == 0) - terminal::put_string_sz(" [none]"); - else - for (int i = 0; i < 16; ++i) - if (((exception_info.rflags >> i) & 1) == 1) - terminal::put_string_sz(flag_names[i]); - - if (exception_info.exception_number == 0x0e) { - terminal::put_string_sz("\n cr2: 0x"); - terminal::put_int_hex(exception_info.cr2, 16); - } + //so exception_info's type is known by gdb + exception_info_t the_exception_info = exception_info; + (void)the_exception_info; - print_line("cr3", "rip", exception_info.cr3, exception_info.rip); - print_line("rax", "rbx", exception_info.rax, exception_info.rbx); - print_line("rcx", "rdx", exception_info.rcx, exception_info.rdx); - print_line("rdi", "rsi", exception_info.rdi, exception_info.rsi); - print_line("rbp", "rsp", exception_info.rbp, exception_info.rsp); - print_line("r8 ", "r9 ", exception_info.r8 , exception_info.r9 ); - print_line("r10", "r11", exception_info.r10, exception_info.r11); - print_line("r12", "r13", exception_info.r12, exception_info.r13); - print_line("r14", "r15", exception_info.r14, exception_info.r15); + //TODO: log exception, and recover if possible. - while (1) - asm ("hlt"); + panic(0xba40bb); } @@ -134,6 +55,7 @@ static uint32_t current_flags = 0; static void got_key(uint32_t key) { input::key_queue->insert(current_flags | key); + input::got_input(); if (key == (input::BREAK | 0x77)) current_flags ^= input::NUM_LOCK; diff --git a/kernel/paging.asm b/kernel/source/paging.asm index f1047a9..f1047a9 100644 --- a/kernel/paging.asm +++ b/kernel/source/paging.asm diff --git a/kernel/paging.cpp b/kernel/source/paging.cpp index d8869fc..d8869fc 100644 --- a/kernel/paging.cpp +++ b/kernel/source/paging.cpp diff --git a/kernel/source/panic.cpp b/kernel/source/panic.cpp new file mode 100644 index 0000000..d99be91 --- /dev/null +++ b/kernel/source/panic.cpp @@ -0,0 +1,11 @@ +#include <hilbert/kernel/framebuffer.hpp> +#include <hilbert/kernel/panic.hpp> + +namespace hilbert::kernel { + [[noreturn]] void panic(uint32_t code) { + framebuffer::fill_color(framebuffer::encode_color( + code >> 16, (code >> 8) & 0xff, code & 0xff)); + while (1) + asm ("hlt"); + } +} diff --git a/kernel/storage.cpp b/kernel/source/storage.cpp index b6b1a04..b6b1a04 100644 --- a/kernel/storage.cpp +++ b/kernel/source/storage.cpp diff --git a/kernel/storage/bd/memory.cpp b/kernel/source/storage/bd/memory.cpp index d6a6719..d6a6719 100644 --- a/kernel/storage/bd/memory.cpp +++ b/kernel/source/storage/bd/memory.cpp diff --git a/kernel/storage/fs/tarfs.cpp b/kernel/source/storage/fs/tarfs.cpp index 5986f62..5986f62 100644 --- a/kernel/storage/fs/tarfs.cpp +++ b/kernel/source/storage/fs/tarfs.cpp diff --git a/kernel/source/syscall.cpp b/kernel/source/syscall.cpp new file mode 100644 index 0000000..768ff0d --- /dev/null +++ b/kernel/source/syscall.cpp @@ -0,0 +1,538 @@ +#include <hilbert/kernel/application.hpp> +#include <hilbert/kernel/framebuffer.hpp> +#include <hilbert/kernel/paging.hpp> +#include <hilbert/kernel/input.hpp> +#include <hilbert/kernel/panic.hpp> +#include <hilbert/kernel/vfile.hpp> + +namespace hilbert::kernel::syscall { + + enum file_result : uint64_t { + file_result_success, + file_result_bad_file_handle, + file_result_device_error, + file_result_file_system_corrupt, + file_result_out_of_bounds, + file_result_does_not_exist, + file_result_directory + }; + + bool is_range_owned_by_application(uint64_t start, uint64_t end) { + auto *process = application::running_thread->the_process; + uint64_t pstart = (start / 4096) * 4096; + uint64_t pend = ((end - 1) / 4096 + 1) * 4096; + for (uint64_t p = pstart; p < pend; p += 4096) + if (!process->is_page_owned(p)) + return false; + return true; + } + + void set_zero(uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { + rax = 0; + rdi = 0; + rsi = 0; + rdx = 0; + } + + void encode_color_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + rax = (uint64_t)framebuffer::encode_color( + rdi & 0xff, (rdi >> 8) & 0xff, (rdi >> 16) & 0xff); + rdi = 0; + rsi = 0; + rdx = 0; + } + + void get_framebuffer_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + auto *process = application::running_thread->the_process; + if (process->framebuffer_vaddr == 0) { + uint64_t pages_needed = + (framebuffer::dword_pitch * framebuffer::height * 4 - 1) / 4096 + 1; + uint64_t vaddr = process->get_free_vaddr_pages(pages_needed); + for (uint64_t i = 0; i < pages_needed; ++i) + process->map_page( + vaddr + i * 4096, framebuffer::paddr + i * 4096, true, false, false); + process->framebuffer_vaddr = vaddr; + } + + rax = process->framebuffer_vaddr; + rdi = + (uint64_t)(uint32_t)framebuffer::width | + ((uint64_t)(uint32_t)framebuffer::height << 32); + rsi = (uint32_t)framebuffer::dword_pitch; + rdx = 0; + + } + + void open_file_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + if (!is_range_owned_by_application(rdi, rdi + rsi)) { + set_zero(rax, rdi, rsi, rdx); + return; + } + + utility::string path_string((const char *)rdi, rsi); + + set_zero(rax, rdi, rsi, rdx); + + vfile::canon_path cp; + vfile::vfile file; + vfile::canonize_path(path_string, cp); + + switch (vfile::lookup_path(cp, file, true)) { + + case storage::fs_result::device_error: + case storage::fs_result::fs_corrupt: + + rax = (uint64_t)application::stream_result::io_error; + return; + + case storage::fs_result::does_not_exist: + + if (!(rdx & 1)) { + rax = (uint64_t)application::stream_result::does_not_exist; + return; + } + + //TODO: create the file + panic(0x9af5e6); + + case storage::fs_result::success: + + if (rdx & 2) { + rax = (uint64_t)application::stream_result::already_exists; + return; + } + + if (file.dir_entry.type != storage::file_type::regular_file) { + rax = (uint64_t)application::stream_result::not_a_regular_file; + return; + } + + rax = (uint64_t)application::stream_result::success; + rdi = application::running_thread->the_process->open_streams.add_new( + new application::vfile_stream(utility::move(file))); + + return; + + } + + } + + void end_this_thread_syscall( + uint64_t &, uint64_t &rdi, uint64_t &, uint64_t & + ) { + application::running_thread->exit_code = (int)(uint32_t)rdi; + delete application::running_thread; + application::resume_next(); + } + + void get_new_pages_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + uint64_t count = rdi; + set_zero(rax, rdi, rsi, rdx); + + auto *p = application::running_thread->the_process; + uint64_t vaddr = p->get_free_vaddr_pages(count); + + for (uint64_t i = 0; i < count; ++i) { + uint64_t kvaddr; + uint64_t paddr; + paging::map_new_kernel_page(kvaddr, paddr); + for (int i = 0; i < 4096; ++i) + ((uint8_t *)kvaddr)[i] = 0; + paging::unmap_kernel_page((uint64_t)kvaddr); + p->map_page(vaddr + i * 4096, paddr, true, false, true); + } + + rax = vaddr; + + } + + void read_key_packet_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + set_zero(rax, rdi, rsi, rdx); + auto *t = application::running_thread; + + do + if (input::key_queue->count > 0) { + rax = (uint64_t)input::key_queue->take(); + return; + } + while (application::save_thread_state(t->cpu)); + + t->state = application::thread_state::waiting; + application::threads_waiting_for_input->insert(t); + application::resume_next(); + + } + + void create_private_socket_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + auto *s = new application::socket; + auto *ss1 = new application::socket_stream(s, false); + auto *ss2 = new application::socket_stream(s, true); + set_zero(rax, rdi, rsi, rdx); + auto *p = application::running_thread->the_process; + rax = (uint64_t)p->open_streams.add_new(ss1); + rdi = (uint64_t)p->open_streams.add_new(ss2); + } + + void create_socket_listener_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + if (!is_range_owned_by_application(rdi, rdi + rsi)) { + set_zero(rax, rdi, rsi, rdx); + return; + } + + utility::string id_string((const char *)rdi, rsi); + set_zero(rax, rdi, rsi, rdx); + + for (auto *p = application::all_socket_listeners->first; p; p = p->next) + if (p->value->id == id_string) { + rax = (uint64_t)application::stream_result::socket_id_already_used; + return; + } + + auto *sl = new application::socket_listener(); + sl->id = utility::move(id_string); + sl->is_listening = true; + rax = (uint64_t)application::stream_result::success; + rdi = (uint64_t)application::running_thread->the_process + ->socket_listeners.add_new(utility::move(sl)); + + } + + void stop_socket_listener_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + set_zero(rax, rdi, rsi, rdx); + auto *p = application::running_thread->the_process; + + if (p->socket_listeners.has_id(handle)) { + auto *sl = p->socket_listeners.get(handle); + p->socket_listeners.remove_id(handle); + if (sl->waiting_to_accept_connection.count > 0 || + sl->waiting_to_connect.count > 0) { + sl->is_listening = false; + while (sl->waiting_to_accept_connection.count > 0) { + auto *t = sl->waiting_to_accept_connection.take(); + t->state = application::thread_state::paused; + application::paused_threads->insert(t); + } + while (sl->waiting_to_connect.count > 0) { + auto *t = sl->waiting_to_connect.take(); + t->state = application::thread_state::paused; + application::paused_threads->insert(t); + } + } + else + delete sl; + } + + } + + void accept_socket_connection_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + set_zero(rax, rdi, rsi, rdx); + auto *t = application::running_thread; + auto *p = t->the_process; + + if (!p->socket_listeners.has_id(handle)) { + rax = (uint64_t)application::stream_result::socket_id_not_in_use; + return; + } + + auto *sl = p->socket_listeners.get(handle); + + if (sl->waiting_to_connect.count > 0) { + auto *ot = sl->waiting_to_connect.take(); + auto *sock = new application::socket(); + application::stream *s1 = new application::socket_stream(sock, false); + application::stream *s2 = new application::socket_stream(sock, true); + unsigned handle = p->open_streams.add_new(utility::move(s1)); + ot->just_connected_to = s2; + ot->state = application::thread_state::paused; + application::paused_threads->insert(ot); + rax = (uint64_t)application::stream_result::success; + rdi = handle; + return; + } + + if (application::save_thread_state(t->cpu)) { + if (sl->is_listening) { + rax = (uint64_t)application::stream_result::success; + rdi = p->open_streams.add_new(utility::move(t->just_accepted)); + } + else { + if (sl->waiting_to_accept_connection.count == 0 && + sl->waiting_to_connect.count == 0) + delete sl; + rax = (uint64_t)application::stream_result::socket_listener_closed; + } + return; + } + + t->state = application::thread_state::waiting; + sl->waiting_to_accept_connection.insert(t); + application::resume_next(); + + } + + void connect_to_socket_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + if (!is_range_owned_by_application(rdi, rdi + rsi)) { + set_zero(rax, rdi, rsi, rdx); + return; + } + + utility::string id_string((const char *)rdi, rsi); + set_zero(rax, rdi, rsi, rdx); + + for (auto *i = application::all_socket_listeners->first; i; i = i->next) + if (i->value->id == id_string) { + auto *sl = i->value; + auto *t = application::running_thread; + auto *p = t->the_process; + + if (sl->waiting_to_accept_connection.count > 0) { + auto *ot = sl->waiting_to_accept_connection.take(); + auto *sock = new application::socket(); + auto *s1 = new application::socket_stream(sock, false); + auto *s2 = new application::socket_stream(sock, true); + unsigned handle = p->open_streams.add_new(utility::move(s1)); + ot->just_accepted = s2; + ot->state = application::thread_state::paused; + application::paused_threads->insert(ot); + rax = (uint64_t)application::stream_result::success; + rdi = handle; + return; + } + + if (application::save_thread_state(t->cpu)) { + if (sl->is_listening) { + rax = (uint64_t)application::stream_result::success; + rdi = p->open_streams.add_new(utility::move(t->just_connected_to)); + } + else { + if (sl->waiting_to_accept_connection.count == 0 && + sl->waiting_to_connect.count == 0) + delete sl; + rax = (uint64_t)application::stream_result::socket_id_not_in_use; + } + return; + } + + t->state = application::thread_state::waiting; + sl->waiting_to_connect.insert(t); + application::resume_next(); + + } + + rax = (uint64_t)application::stream_result::socket_id_not_in_use; + + } + + void close_stream_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + set_zero(rax, rdi, rsi, rdx); + auto *p = application::running_thread->the_process; + + if (p->open_streams.has_id(handle)) { + application::stream *s = p->open_streams.get(handle); + p->open_streams.remove_id(handle); + delete s; + } + + } + + void seek_stream_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + uint8_t origin = (uint8_t)rsi; + int64_t offset = (int64_t)rdx; + set_zero(rax, rdi, rsi, rdx); + + if (origin >= 3) + return; + + auto *p = application::running_thread->the_process; + + if (!p->open_streams.has_id(handle)) { + rax = (uint64_t)application::stream_result::bad_handle; + return; + } + + rax = (uint64_t)p->open_streams.get(handle) + ->seek((application::seek_origin)origin, offset); + + } + + void read_from_stream_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + uint64_t count = (uint64_t)rsi; + uint64_t buffer = (uint64_t)rdx; + set_zero(rax, rdi, rsi, rdx); + + if (!is_range_owned_by_application(buffer, buffer + count)) + return; + + auto *p = application::running_thread->the_process; + + if (!p->open_streams.has_id(handle)) { + rax = (uint64_t)application::stream_result::bad_handle; + return; + } + + rax = (uint64_t)p->open_streams.get(handle)->read(count, (void *)buffer); + + } + + void write_to_stream_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + uint64_t count = (uint64_t)rsi; + uint64_t buffer = (uint64_t)rdx; + set_zero(rax, rdi, rsi, rdx); + + if (!is_range_owned_by_application(buffer, buffer + count)) + return; + + auto *p = application::running_thread->the_process; + + if (!p->open_streams.has_id(handle)) { + rax = (uint64_t)application::stream_result::bad_handle; + return; + } + + rax = (uint64_t)p->open_streams.get(handle) + ->write(count, (const void *)buffer); + + } + + void get_stream_length_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + set_zero(rax, rdi, rsi, rdx); + + auto *p = application::running_thread->the_process; + + if (!p->open_streams.has_id(handle)) { + rax = (uint64_t)application::stream_result::bad_handle; + return; + } + + rax = (uint64_t)p->open_streams.get(handle)->get_length(rdi); + + } + + void start_process_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + //TODO + (void)rax; + (void)rdi; + (void)rsi; + (void)rdx; + panic(0x9af5e6); + } + + void end_this_process_syscall( + uint64_t &, uint64_t &rdi, uint64_t &, uint64_t & + ) { + application::running_thread->the_process->end_process((unsigned)rdi); + application::resume_next(); + } + + void set_stream_length_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx + ) { + + unsigned handle = (unsigned)rdi; + uint64_t new_length = rsi; + set_zero(rax, rdi, rsi, rdx); + + auto *p = application::running_thread->the_process; + + if (!p->open_streams.has_id(handle)) { + rax = (uint64_t)application::stream_result::bad_handle; + return; + } + + rax = (uint64_t)p->open_streams.get(handle)->set_length(new_length); + + } + + typedef void (*syscall_handler)( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx); + + syscall_handler handlers[] = { + &encode_color_syscall, + &get_framebuffer_syscall, + &open_file_syscall, + &end_this_thread_syscall, + &get_new_pages_syscall, + &read_key_packet_syscall, + &create_private_socket_syscall, + &create_socket_listener_syscall, + &stop_socket_listener_syscall, + &accept_socket_connection_syscall, + &connect_to_socket_syscall, + &close_stream_syscall, + &seek_stream_syscall, + &read_from_stream_syscall, + &write_to_stream_syscall, + &get_stream_length_syscall, + &start_process_syscall, + &end_this_process_syscall, + &set_stream_length_syscall + }; + + static constexpr int max_syscall_number = 18; + +} + +using namespace hilbert::kernel::syscall; + +extern "C" void do_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx +) { + + if (rax <= max_syscall_number && handlers[rax] != 0) + handlers[rax](rax, rdi, rsi, rdx); + else + set_zero(rax, rdi, rsi, rdx); + +} diff --git a/kernel/utility.cpp b/kernel/source/utility.cpp index 12e88fd..12e88fd 100644 --- a/kernel/utility.cpp +++ b/kernel/source/utility.cpp diff --git a/kernel/vfile.cpp b/kernel/source/vfile.cpp index 89c95e6..89c95e6 100644 --- a/kernel/vfile.cpp +++ b/kernel/source/vfile.cpp diff --git a/kernel/syscall.asm b/kernel/syscall.asm deleted file mode 100644 index c293402..0000000 --- a/kernel/syscall.asm +++ /dev/null @@ -1,88 +0,0 @@ -bits 64 - -global start_user_mode - -section .text - -extern do_syscall - -syscall_entry: - mov r11, rsp - mov rsp, 0xfffffffffffff000 - push r11 - push rcx - - push rdx - push rsi - push rdi - push rax - - mov rdi, rsp - lea rsi, [rsp + 8] - lea rdx, [rsp + 16] - lea rcx, [rsp + 24] - - call do_syscall - - pop rax - pop rdi - pop rsi - pop rdx - - xor r8, r8 - xor r9, r9 - xor r10, r10 - xor r11, r11 - or r11, 0x200 - pop rcx - pop rsp - - o64 sysret - -start_user_mode: -;intended rip in rdi -;intended rsp in rsi -;intended p4_paddr in rdx - - mov rax, rdx - mov cr3, rax - - ;efer <- efer | 0x1 - mov rcx, 0xc0000080 - rdmsr - or al, 1 - wrmsr - - ;lstar <- syscall_entry - mov rdx, syscall_entry - mov eax, edx - shr rdx, 32 - mov ecx, 0xc0000082 - wrmsr - - ;star <- 0x0030.0028.0000.0000 - mov edx, 0x00300028 - xor eax, eax - mov ecx, 0xc0000081 - wrmsr - - mov rcx, rdi - mov rsp, rsi - xor r11, r11 - or r11, 0x200 - - xor rax, rax - xor rbx, rbx - xor rdx, rdx - xor rdi, rdi - xor rsi, rsi - xor rbp, rbp - xor r8, r8 - xor r9, r9 - xor r10, r10 - xor r12, r12 - xor r13, r13 - xor r14, r14 - xor r15, r15 - - o64 sysret diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp deleted file mode 100644 index e194eb1..0000000 --- a/kernel/syscall.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include <hilbert/kernel/application.hpp> -#include <hilbert/kernel/framebuffer.hpp> -#include <hilbert/kernel/paging.hpp> -#include <hilbert/kernel/input.hpp> -#include <hilbert/kernel/vfile.hpp> - -namespace hilbert::kernel::syscall { - - enum file_result : uint64_t { - file_result_success, - file_result_bad_file_handle, - file_result_device_error, - file_result_file_system_corrupt, - file_result_out_of_bounds, - file_result_does_not_exist, - file_result_directory - }; - - bool is_range_owned_by_application(uint64_t start, uint64_t end) { - auto *app = application::running_app; - uint64_t pstart = (start / 4096) * 4096; - uint64_t pend = ((end - 1) / 4096 + 1) * 4096; - for (uint64_t p = pstart; p < pend; p += 4096) - if (!app->is_page_owned(p)) - return false; - return true; - } - - void set_zero(uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { - rax = 0; - rdi = 0; - rsi = 0; - rdx = 0; - } - - void encode_color_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - rax = (uint64_t)framebuffer::encode_color( - rdi & 0xff, (rdi >> 8) & 0xff, (rdi >> 16) & 0xff); - rdi = 0; - rsi = 0; - rdx = 0; - } - - void get_framebuffer_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - auto *app = application::running_app; - if (app->framebuffer_vaddr == 0) { - uint64_t pages_needed = - (framebuffer::dword_pitch * framebuffer::height * 4 - 1) / 4096 + 1; - uint64_t vaddr = app->get_free_vaddr_pages(pages_needed); - for (uint64_t i = 0; i < pages_needed; ++i) - app->map_page( - vaddr + i * 4096, framebuffer::paddr + i * 4096, true, false, false); - app->framebuffer_vaddr = vaddr; - } - - rax = app->framebuffer_vaddr; - rdi = - (uint64_t)(uint32_t)framebuffer::width | - ((uint64_t)(uint32_t)framebuffer::height << 32); - rsi = (uint32_t)framebuffer::dword_pitch; - rdx = 0; - - } - - void open_file_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - if (!is_range_owned_by_application(rdi, rdi + rsi)) { - set_zero(rax, rdi, rsi, rdx); - return; - } - - utility::string path((const char *)rdi, rsi); - vfile::canon_path cp; - vfile::canonize_path(path, cp); - - set_zero(rax, rdi, rsi, rdx); - - vfile::vfile file; - switch (vfile::lookup_path(cp, file, true)) { - case storage::fs_result::success: - break; - case storage::fs_result::device_error: - rax = file_result_device_error; - return; - case storage::fs_result::fs_corrupt: - rax = file_result_file_system_corrupt; - return; - case storage::fs_result::does_not_exist: - rax = file_result_does_not_exist; - return; - } - - if (file.dir_entry.type != storage::file_type::regular_file) { - rax = file_result_directory; - return; - } - - unsigned handler = - application::running_app->open_files.add_new(utility::move(file)); - rax = file_result_success; - rdi = (uint64_t)handler; - - } - - void get_file_length_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - auto &open_files = application::running_app->open_files; - unsigned handle = (unsigned)rdi; - - set_zero(rax, rdi, rsi, rdx); - - if (!open_files.has_id(handle)) { - rax = file_result_bad_file_handle; - return; - } - - rax = file_result_success; - rdi = open_files.get(handle).dir_entry.length; - - } - - void read_from_file_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - if (!is_range_owned_by_application(rdi, rdi + 32)) { - set_zero(rax, rdi, rsi, rdx); - return; - } - - const uint64_t *request = (const uint64_t *)rdi; - unsigned handle = (unsigned)request[0]; - uint64_t start = request[1]; - uint64_t length = request[2]; - uint64_t buffer_vaddr = request[3]; - - set_zero(rax, rdi, rsi, rdx); - - if (!is_range_owned_by_application(buffer_vaddr, buffer_vaddr + length)) - return; - - auto &open_files = application::running_app->open_files; - - if (!open_files.has_id(handle)) - rax = file_result_bad_file_handle; - - vfile::vfile &file = open_files.get(handle); - - if (start + length > file.dir_entry.length) - rax = file_result_out_of_bounds; - - switch (file.read_file(start, length, (void *)buffer_vaddr)) { - case storage::fs_result::success: - rax = file_result_success; - return; - case storage::fs_result::device_error: - rax = file_result_device_error; - return; - case storage::fs_result::fs_corrupt: - case storage::fs_result::does_not_exist: - rax = file_result_file_system_corrupt; - return; - } - - } - - [[noreturn]] void end_this_process_syscall( - uint64_t &, uint64_t &, uint64_t &, uint64_t & - ) { - - //TODO - while (1) - ; - - } - - void get_new_pages_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - uint64_t count = rdi; - set_zero(rax, rdi, rsi, rdx); - - auto *app = application::running_app; - uint64_t vaddr = app->get_free_vaddr_pages(count); - - for (uint64_t i = 0; i < count; ++i) { - uint64_t paddr = paging::take_pram_page(); - app->map_page(vaddr + i * 4096, paddr, true, false, true); - } - - rax = vaddr; - - } - - void close_file_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - unsigned handle = rdi; - set_zero(rax, rdi, rsi, rdx); - application::running_app->open_files.remove_id(handle); - - } - - void read_key_packet_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - - set_zero(rax, rdi, rsi, rdx); - - asm ("cli"); - - while (input::key_queue->count == 0) - asm ("sti\nhlt\ncli"); - - rax = (uint64_t)input::key_queue->take(); - - asm ("sti"); - - } - - typedef void (*syscall_handler)( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx); - - syscall_handler handlers[] = { - &encode_color_syscall, - &get_framebuffer_syscall, - &open_file_syscall, - &get_file_length_syscall, - &read_from_file_syscall, - &end_this_process_syscall, - &get_new_pages_syscall, - &close_file_syscall, - &read_key_packet_syscall - }; - - static constexpr int max_syscall_number = 8; - -} - -using namespace hilbert::kernel::syscall; - -extern "C" void do_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx -) { - - if (rax <= max_syscall_number && handlers[rax] != 0) - handlers[rax](rax, rdi, rsi, rdx); - else - set_zero(rax, rdi, rsi, rdx); - -} diff --git a/kernel/terminal.cpp b/kernel/terminal.cpp deleted file mode 100644 index 167e6cf..0000000 --- a/kernel/terminal.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include <hilbert/kernel/framebuffer.hpp> -#include <hilbert/kernel/terminal.hpp> - -namespace hilbert::kernel::terminal { - - uint8_t *termfont; - uint64_t termfont_len; - - int width; - int height; - - int cursor_x; - int cursor_y; - - framebuffer::color bg_color; - framebuffer::color fg_color; - - static uint8_t glyph_height; - - void init_terminal() { - //TODO - verify that termfont fits inside termfont_len (i.e. that no other - // functions in this file will try to access memory outside termfont) - //TODO - check magic header to verify that this is actually a font and to - // see whether this is a psf1 font or a psf2 font. - //TODO - support psf2 fonts. currently psf1 is assumed. - //TODO - read unicode table if there is one. currently it is assumed that - // all 256 codepoints have glyphs, and that they appear in order. - - glyph_height = termfont[3]; - width = framebuffer::width / 8; - height = framebuffer::height / glyph_height; - cursor_x = 0; - cursor_y = 0; - bg_color = framebuffer::encode_color(0, 0, 0); - fg_color = framebuffer::encode_color(255, 255, 255); - - } - - static void cursor_down() { - if (++cursor_y == height) { - --cursor_y; - framebuffer::move_region( - 0, glyph_height, width * 8, height * glyph_height, 0, 0); - framebuffer::fill_region(0, (height - 1) * glyph_height, - width * 8, height * glyph_height, bg_color); - } - } - - static void cursor_right() { - if (++cursor_x == width) { - cursor_x = 0; - cursor_down(); - } - } - - void draw_char(char ch, int x, int y) { - const uint8_t *glyph = termfont + 4 + glyph_height * (unsigned)ch; - for (int i = 0; i < glyph_height; ++i) - for (int j = 0; j < 8; ++j) - framebuffer::set_pixel(x * 8 + j, y * glyph_height + i, - ((glyph[i] << j) & 0x80) ? fg_color : bg_color); - } - - void put_char(char ch) { - switch (ch) { - case '\n': - cursor_x = 0; - cursor_down(); - break; - default: - draw_char(ch, cursor_x, cursor_y); - cursor_right(); - break; - } - } - - void put_string(const utility::string &str) { - for (size_t i = 0; i < str.count; ++i) - put_char(str.buffer[i]); - } - - void put_string_sz(const char *str) { - for (size_t i = 0; str[i]; ++i) - put_char(str[i]); - } - - void put_int_decimal(uint64_t n, bool with_commas) { - - if (n == 0) { - put_char('0'); - return; - } - - uint64_t d = 1; - int i = 0; - while (d <= n / 10) { - d *= 10; - ++i; - } - - while (d) { - put_char('0' + ((n / d) % 10)); - d /= 10; - if (with_commas && (i % 3 == 0) && (i != 0)) - put_char(','); - --i; - } - - } - - static char hex_digits[] = "0123456789abcdef"; - - void put_int_hex(uint64_t n, int digits, bool with_dots) { - for (int digit = digits - 1; digit >= 0; --digit) { - put_char(hex_digits[(n >> (digit * 4)) & 0xf]); - if (with_dots && digit % 4 == 0 && digit != 0) - put_char('.'); - } - } - -} diff --git a/libraries/daguerre/include/daguerre.hpp b/libraries/daguerre/include/daguerre.hpp new file mode 100644 index 0000000..274e257 --- /dev/null +++ b/libraries/daguerre/include/daguerre.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include <stdint.h> +#include <cstdio> + +namespace daguerre { + + typedef uint32_t hilbert_color; + + struct rgb24 { + uint8_t r; + uint8_t g; + uint8_t b; + }; + + template <class to_type, class from_type> + to_type convert_color(const from_type &from); + + template <> + inline hilbert_color convert_color<hilbert_color, rgb24>(const rgb24 &from) { + return __euler_encode_color(from.r, from.g, from.b); + } + + template <class color_t> + class image { + + public: + bool delete_buffer_on_destruct; + color_t *buffer; + unsigned width; + unsigned height; + unsigned pitch;//in sizeof(color_t) + + image() + : delete_buffer_on_destruct(false), buffer(0), width(0), height(0), + pitch(0) {} + + image(unsigned width, unsigned height) + : delete_buffer_on_destruct(true), buffer(new color_t[width * height]), + width(width), height(height), pitch(width) {} + + image( + color_t *buffer, unsigned width, unsigned height, unsigned pitch, + bool delete_buffer_on_destruct) + : delete_buffer_on_destruct(delete_buffer_on_destruct), buffer(buffer), + width(width), height(height), pitch(pitch) {} + + ~image() { + if (delete_buffer_on_destruct && buffer) + delete[] buffer; + } + + image(const image<color_t> &other) = delete; + + image(image<color_t> &&other) + : delete_buffer_on_destruct(other.delete_buffer_on_destruct), + buffer(other.buffer), width(other.width), height(other.height), + pitch(other.pitch) { + other.buffer = 0; + } + + image<color_t> &operator =(const image<color_t> &other) = delete; + + image<color_t> &operator =(image<color_t> &&other) { + if (delete_buffer_on_destruct && buffer) + delete[] buffer; + delete_buffer_on_destruct = other.delete_buffer_on_destruct; + buffer = other.buffer; + width = other.width; + height = other.height; + pitch = other.pitch; + other.buffer = 0; + return *this; + } + + color_t get(unsigned x, unsigned y) const { + return buffer[y * pitch + x]; + } + + void set(unsigned x, unsigned y, const color_t &c) { + buffer[y * pitch + x] = c; + } + + }; + + //it is assumed that the regions do not overlap in memory. + //copies into [to_x, to_x + width) x [to_y, to_y + height) + //from [from_x, from_x + width) x [from_y, from_y + height). + template <class color_t> + void copy_region( + image<color_t> &to, const image<color_t> &from, unsigned from_x, + unsigned from_y, unsigned to_x, unsigned to_y, unsigned width, + unsigned height) { + + color_t *to_start = to.buffer + to.pitch * to_y + to_x; + const color_t *from_start = from.buffer + from.pitch * from_y + from_x; + + for (unsigned y = 0; y < height; ++y) + memcpy( + to_start + to.pitch * y, from_start + from.pitch * y, + width * sizeof(color_t)); + + } + + //it is assumed that the regions do not overlap in memory. + //copies into [to_x, to_x + width) x [to_y, to_y + height) + //from [from_x, from_x + width) x [from_y, from_y + height). + template < + class to_color_t, class from_color_t, + to_color_t converter(const from_color_t &) = + convert_color<to_color_t, from_color_t>> + void copy_region( + image<to_color_t> &to, const image<from_color_t> &from, unsigned from_x, + unsigned from_y, unsigned to_x, unsigned to_y, unsigned width, + unsigned height) { + + to_color_t *to_start = to.buffer + to.pitch * to_y + to_x; + const from_color_t *from_start = + from.buffer + from.pitch * from_y + from_x; + + for (unsigned y = 0; y < height; ++y) + for (unsigned x = 0; x < width; ++x) + to_start[to.pitch * y + x] = converter(from_start[from.pitch * y + x]); + + } + + image<hilbert_color> get_hilbert_framebuffer(); + + bool try_load_ppm(std::FILE *input, image<rgb24> &into); + +} diff --git a/libraries/daguerre/include/daguerre/image.hpp b/libraries/daguerre/include/daguerre/image.hpp deleted file mode 100644 index 6129306..0000000 --- a/libraries/daguerre/include/daguerre/image.hpp +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once - -#include <euler/syscall.hpp> -#include <cassert> -#include <cstdint> -#include <cstdio> - -namespace daguerre { - - struct color24 { - uint8_t r; - uint8_t g; - uint8_t b; - }; - - using hilbert_color = euler::syscall::encoded_color; - - static inline hilbert_color to_hilbert_color(const color24 &c) { - return _syscall_encode_color(c.r, c.g, c.b); - } - - template <class pixel_type = color24> - class image { - - pixel_type *buffer; - unsigned width; - unsigned height; - unsigned pitch; //in pixels - - bool buffer_owned; - - public: - image() : buffer(0) {} - - image(pixel_type *buffer, unsigned width, - unsigned height, unsigned pitch, bool buffer_owned - ) : buffer(buffer), width(width), height(height), - pitch(pitch), buffer_owned(buffer_owned) {} - - image(unsigned width, unsigned height) - : buffer(new pixel_type[width * height]), width(width), - height(height), pitch(width), buffer_owned(true) {} - - image(const image<pixel_type> &other) = delete; - - image(image<pixel_type> &&other) - : buffer(other.buffer), width(other.width), height(other.height), - pitch(other.pitch), buffer_owned(other.buffer_owned) { - other.buffer = 0; - } - - ~image() { - if (buffer && buffer_owned) - delete[] buffer; - } - - image &operator =(const image<pixel_type> &other) = delete; - - image &operator =(image<pixel_type> &&other) { - if (buffer && buffer_owned) - delete[] buffer; - buffer = other.buffer; - width = other.width; - height = other.height; - pitch = other.pitch; - buffer_owned = other.buffer_owned; - other.buffer = 0; - return *this; - } - - pixel_type &get(unsigned x, unsigned y) { - return buffer[y * pitch + x]; - } - - const pixel_type &get(unsigned x, unsigned y) const { - return buffer[y * pitch + x]; - } - - pixel_type *get_buffer() { - return buffer; - } - - const pixel_type *get_buffer() const { - return buffer; - } - - unsigned get_width() const { - return width; - } - - unsigned get_height() const { - return height; - } - - unsigned get_pitch() const { - return pitch; - } - - }; - - template <class pixel_type> - void default_converter(const pixel_type &i, pixel_type &o) { - o = i; - } - - static inline void default_converter(const color24 &i, hilbert_color &o) { - o = to_hilbert_color(i); - } - - //copies a rectangle of size width x height from source to destination. the - //rectangle starts in source at (source_x, source_y), and in destination at - //(destination_x, destination_y). every pixel is passed through the converter - //template argument. - template < - class source_pixel_type, class destination_pixel_type, - void (*converter)(const source_pixel_type &, destination_pixel_type &) = - &default_converter> - void copy_image(const image<source_pixel_type> &source, - image<destination_pixel_type> &destination, unsigned source_x, - unsigned source_y, unsigned destination_x, unsigned destination_y, - unsigned width, unsigned height - ) { - - assert(source_x + width <= source.get_width()); - assert(source_y + height <= source.get_height()); - assert(destination_x + width <= destination.get_width()); - assert(destination_y + height <= destination.get_height()); - - unsigned source_pitch = source.get_pitch(); - unsigned destination_pitch = destination.get_pitch(); - - const source_pixel_type *source_buffer = - &source.get_buffer()[source_y * source_pitch + source_x]; - destination_pixel_type *destination_buffer = &destination.get_buffer()[ - destination_y * destination_pitch + destination_x]; - - for (unsigned y = 0; y < height; ++y) - for (unsigned x = 0; x < width; ++x) - converter(source_buffer[y * source_pitch + x], - destination_buffer[y * destination_pitch + x]); - - } - - static inline image<hilbert_color> get_hilbert_framebuffer() { - hilbert_color *ptr; - uint32_t width; - uint32_t height; - uint32_t pitch; - _syscall_get_framebuffer(ptr, width, height, pitch); - return image<hilbert_color>(ptr, width, height, pitch, false); - } - - bool try_load_ppm(std::FILE *from, image<color24> &into); - -} diff --git a/libraries/daguerre/makefile b/libraries/daguerre/makefile new file mode 100644 index 0000000..3505d35 --- /dev/null +++ b/libraries/daguerre/makefile @@ -0,0 +1,12 @@ +SOURCES = \ + daguerre.cpp + +build/%.cpp.o: source/%.cpp + @mkdir -p $(@D) + $(HILBERT_CC) -c $^ -o $@ + +build/libdaguerre.a: $(SOURCES:%=build/%.o) + $(HILBERT_AR) rcs $@ $^ + +clean: + rm -rf build diff --git a/libraries/daguerre/ppm.cpp b/libraries/daguerre/ppm.cpp deleted file mode 100644 index e909503..0000000 --- a/libraries/daguerre/ppm.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include <daguerre/image.hpp> -#include <cctype> - -namespace daguerre { - - static unsigned read_int(std::FILE *from) { - unsigned out = 0; - int ch; - do - ch = std::fgetc(from); - while (std::isspace(ch)); - while (true) { - if (ch == EOF) - return out; - if (ch < '0' || ch > '9') { - std::ungetc(ch, from); - return out; - } - out = out * 10 + (ch - '0'); - ch = std::fgetc(from); - } - } - - bool try_load_ppm(std::FILE *from, image<color24> &into) { - - if (std::fgetc(from) != 'P' || std::fgetc(from) != '6' || - std::fgetc(from) != '\n') - return false; - - unsigned width = read_int(from); - unsigned height = read_int(from); - unsigned max = read_int(from); - std::fgetc(from);//newline - - if (max != 255) - return false; - - into = image<color24>(width, height); - color24 *buffer = into.get_buffer(); - - for (unsigned y = 0; y < height; ++y) - for (unsigned x = 0; x < width; ++x) { - buffer[y * width + x].r = std::fgetc(from); - buffer[y * width + x].g = std::fgetc(from); - buffer[y * width + x].b = std::fgetc(from); - } - - return true; - - } - -} diff --git a/libraries/daguerre/source/daguerre.cpp b/libraries/daguerre/source/daguerre.cpp new file mode 100644 index 0000000..fb3ddc7 --- /dev/null +++ b/libraries/daguerre/source/daguerre.cpp @@ -0,0 +1,58 @@ +#include <daguerre.hpp> + +namespace daguerre { + + image<hilbert_color> get_hilbert_framebuffer() { + uint32_t width, height, pitch; + hilbert_color *ptr = __euler_get_framebuffer(width, height, pitch); + return image<hilbert_color>(ptr, width, height, pitch, false); + } + + unsigned read_text_int(std::FILE *input) { + unsigned n = 0; + char ch; + while (true) { + std::fread(&ch, 1, 1, input); + if (ch < '0' || ch > '9') + return n; + n = n * 10 + ch - '0'; + } + } + + bool try_load_ppm(std::FILE *input, image<rgb24> &into) { + + char header[3]; + if (std::fread(header, 1, 3, input) != 3) + return false; + + if (header[0] != 'P' || header[1] != '6' || header[2] != '\n') + return false; + + unsigned width = read_text_int(input); + unsigned height = read_text_int(input); + unsigned max = read_text_int(input); + + into = image<rgb24>(width, height); + + for (unsigned y = 0; y < height; ++y) + for (unsigned x = 0; x < width; ++x) { + if (std::fread(&into.buffer[y * width + x].r, 1, 1, input) != 1) + return false; + if (std::fread(&into.buffer[y * width + x].g, 1, 1, input) != 1) + return false; + if (std::fread(&into.buffer[y * width + x].b, 1, 1, input) != 1) + return false; + } + + if (max != 255) + for (unsigned v = 0; v < width * height; ++v) { + into.buffer[v].r = ((uint16_t)into.buffer[v].r * 255) / max; + into.buffer[v].g = ((uint16_t)into.buffer[v].g * 255) / max; + into.buffer[v].b = ((uint16_t)into.buffer[v].b * 255) / max; + } + + return true; + + } + +} diff --git a/libraries/euler/allocator.cpp b/libraries/euler/allocator.cpp deleted file mode 100644 index 5242df1..0000000 --- a/libraries/euler/allocator.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include <euler/syscall.hpp> - -//i guess we could figure out which parts of the pages the kernel allocated to -//load the application were not actually needed and add those to the memory -//that is available to be allocated, but whatever. - -namespace euler::allocator { - - struct block_info { - //vaddr, divisible by size - uint64_t start; - //power of 2, 0 for unused - uint64_t size; - }; - - struct block_info_page { - block_info infos[255]; - block_info_page *next; - }; - - static_assert(sizeof(block_info_page) == 4088); - - static block_info_page *first_bi_page = 0; - - static block_info *try_find_block(uint64_t start, uint64_t size) { - for (block_info_page *bip = first_bi_page; bip; bip = bip->next) - for (int i = 0; i < 255; ++i) - if (bip->infos[i].start == start && bip->infos[i].size == size) - return bip->infos + i; - return 0; - } - - static block_info *add_block(uint64_t start, uint64_t size) { - for (block_info_page *bip = first_bi_page; bip; bip = bip->next) - for (int i = 0; i < 255; ++i) - if (bip->infos[i].size == 0) { - bip->infos[i].start = start; - bip->infos[i].size = size; - return bip->infos + i; - } - block_info_page *new_bip = (block_info_page *)_syscall_get_new_pages(1); - new_bip->infos[0].start = start; - new_bip->infos[0].size = size; - for (int i = 1; i < 255; ++i) - new_bip->infos[i].size = 0; - new_bip->next = first_bi_page; - first_bi_page = new_bip; - return new_bip->infos; - } - - static block_info *find_minimal_block(uint64_t minimum_length) { - block_info *minimal_so_far = 0; - uint64_t length_so_far = UINT64_MAX; - for (block_info_page *bip = first_bi_page; bip; bip = bip->next) - for (int i = 0; i < 255; ++i) - if (bip->infos[i].size == minimum_length) - return bip->infos + i; - else if ( - bip->infos[i].size > minimum_length && - bip->infos[i].size < length_so_far - ) { - minimal_so_far = bip->infos + i; - length_so_far = bip->infos[i].size; - } - if (minimal_so_far != 0) - return minimal_so_far; - uint64_t pages_needed = (minimum_length - 1) / 4096 + 1; - void *block_start = _syscall_get_new_pages(pages_needed); - return add_block((uint64_t)block_start, pages_needed * 4096); - } - - static void deallocate_aligned(uint64_t start, uint64_t length) { - block_info *buddy = try_find_block(start ^ length, length); - if (buddy) { - buddy->size = 0; - deallocate_aligned(start & ~length, length * 2); - return; - } - add_block(start, length); - } - - static void deallocate(void *start, uint64_t length) { - uint64_t at = (uint64_t)start; - uint64_t left = length; - uint64_t i; - for (i = 1; i <= left; i *= 2) - if (at & i) { - deallocate_aligned(at, i); - at += i; - left -= i; - } - for (i /= 2; left > 0; i /= 2) - if (i <= left) { - deallocate_aligned(at, i); - at += i; - left -= i; - } - } - - [[nodiscard]] static void *allocate(uint64_t length) { - block_info *bi = find_minimal_block(length); - void *to_return = (void *)bi->start; - void *new_start = (void *)(bi->start + length); - uint64_t new_length = bi->size - length; - bi->size = 0; - deallocate(new_start, new_length); - return to_return; - } - - static void deallocate_with_length(void *start) { - uint64_t real_start = (uint64_t)start - 8; - deallocate((void *)real_start, *(uint64_t *)real_start); - } - - [[nodiscard]] static void *allocate_with_length(uint64_t length) { - uint64_t *real_start = (uint64_t *)allocate(length + 8); - *real_start = length + 8; - return real_start + 1; - } - -} - -using namespace euler::allocator; - -void operator delete[](void *start) { - deallocate_with_length(start); -} - -void operator delete(void *start) { - deallocate_with_length(start); -} - -void operator delete[](void *start, uint64_t) { - deallocate_with_length(start); -} - -void operator delete(void *start, uint64_t) { - deallocate_with_length(start); -} - -void *operator new[](uint64_t size) { - return allocate_with_length(size); -} - -void *operator new(uint64_t size) { - return allocate_with_length(size); -} diff --git a/libraries/euler/cassert.cpp b/libraries/euler/cassert.cpp deleted file mode 100644 index e811b38..0000000 --- a/libraries/euler/cassert.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include <cassert> - -namespace euler { - - [[noreturn]] void assert_failed( - const char *, const char *, int, const char * - ) { - //TODO: print error and abort - //we could just exit right now but i want to keep us in - //the application so we can get a stack trace in gdb. - while (1) - ; - } - - -} diff --git a/libraries/euler/cctype.cpp b/libraries/euler/cctype.cpp deleted file mode 100644 index 98234bb..0000000 --- a/libraries/euler/cctype.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include <cctype> - -namespace std { - - int isspace(int ch) { - return - ch == ' ' || ch == '\n' || ch == '\t' || - ch == '\r' || ch == '\v' || ch == '\f'; - } - -} diff --git a/libraries/euler/cstdio.cpp b/libraries/euler/cstdio.cpp deleted file mode 100644 index 367e7e0..0000000 --- a/libraries/euler/cstdio.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include <euler/syscall.hpp> -#include <cassert> -#include <cstring> -#include <cstdio> - -namespace euler { - - //read-only with no error bits for now - struct file_t { - - syscall::file_handle handle; - - //TODO: variable size buffer? maybe a multiple of block size for device? - char buffer[1024]; - //in bytes, aligned to buffer size; 1 for no buffer loaded - uint64_t buffer_start; - - bool is_offset_in_buffer() { - return - buffer_start != 1 && offset >= buffer_start && - offset < buffer_start + 1024; - } - - syscall::file_result ensure_offset_in_buffer() { - if (is_offset_in_buffer()) - return syscall::file_result::success; - uint64_t new_buffer_start = (offset / 1024) * 1024; - uint64_t new_buffer_end = new_buffer_start + 1024; - if (length < new_buffer_end) - new_buffer_end = length; - syscall::file_result result = _syscall_read_from_file( - handle, new_buffer_start, new_buffer_end - new_buffer_start, buffer); - if (result == syscall::file_result::success) - buffer_start = new_buffer_start; - return result; - } - - uint64_t offset; - uint64_t length; - - }; - -} - -namespace std { - - FILE *fopen(const char *path, const char *mode) { - - assert(mode[0] == 'r' && mode[1] == '\0'); - - euler::syscall::file_handle handle; - euler::syscall::file_result result = - _syscall_open_file(path, strlen(path), handle); - if (result != euler::syscall::file_result::success) - return 0; - - uint64_t length; - result = _syscall_get_file_length(handle, length); - if (result != euler::syscall::file_result::success) { - _syscall_close_file(handle); - return 0; - } - - return new FILE { - .handle = handle, - .buffer = {}, - .buffer_start = 1, - .offset = 0, - .length = length - }; - - } - - int fclose(FILE *file) { - _syscall_close_file(file->handle); - delete file; - return 0; - } - - int fgetc(FILE *from) { - if (from->offset >= from->length) - return EOF; - assert( - from->ensure_offset_in_buffer() == euler::syscall::file_result::success); - char ch = from->buffer[from->offset - from->buffer_start]; - ++from->offset; - return ch; - } - - int ungetc(int ch, FILE *from) { - if (ch == EOF || from->offset == 0) - return EOF; - --from->offset; - if (!from->is_offset_in_buffer()) { - ++from->offset; - return EOF; - } - from->buffer[from->offset - from->buffer_start] = ch; - return ch; - } - -} diff --git a/libraries/euler/entry.cpp b/libraries/euler/entry.cpp deleted file mode 100644 index 0bbffda..0000000 --- a/libraries/euler/entry.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include <euler/syscall.hpp> - -int main(int argc, char **argv); - -extern "C" [[noreturn]] void _entry() { - - //TODO: static constructors - - //TODO: get command line via system call and populate argc and argv. - int argc = 0; - char **argv = 0; - - int result = main(argc, argv); - - //TODO: static destructors - - _syscall_end_this_process(result); - -} diff --git a/libraries/euler/include/cassert b/libraries/euler/include/cassert deleted file mode 100644 index bc716a0..0000000 --- a/libraries/euler/include/cassert +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace euler { - [[noreturn]] void assert_failed( - const char *file, const char *function, int line, const char *condition); -} - -#ifdef NDEBUG -#define assert(condition) ((void)0) -#else -#define assert(condition) ((condition) ? ((void)0) : \ - euler::assert_failed(__FILE__, __func__, __LINE__, #condition)) -#endif diff --git a/libraries/euler/include/cctype b/libraries/euler/include/cctype deleted file mode 100644 index 087b191..0000000 --- a/libraries/euler/include/cctype +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace std { - - int isspace(int ch); - -} diff --git a/libraries/euler/include/cstddef b/libraries/euler/include/cstddef deleted file mode 120000 index 9eac9b6..0000000 --- a/libraries/euler/include/cstddef +++ /dev/null @@ -1 +0,0 @@ -../../../mintsuki-freestanding-headers/stddef.h
\ No newline at end of file diff --git a/libraries/euler/include/cstdint b/libraries/euler/include/cstdint deleted file mode 120000 index b087235..0000000 --- a/libraries/euler/include/cstdint +++ /dev/null @@ -1 +0,0 @@ -../../../mintsuki-freestanding-headers/stdint.h
\ No newline at end of file diff --git a/libraries/euler/include/cstdio b/libraries/euler/include/cstdio deleted file mode 100644 index 3bb293b..0000000 --- a/libraries/euler/include/cstdio +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#define EOF (-1) - -namespace euler { - struct file_t; -} - -namespace std { - - typedef euler::file_t FILE; - - FILE *fopen(const char *path, const char *mode); - int fclose(FILE *file); - - int fgetc(FILE *from); - int ungetc(int ch, FILE *from); - -} diff --git a/libraries/euler/include/cstring b/libraries/euler/include/cstring deleted file mode 100644 index 65d00dc..0000000 --- a/libraries/euler/include/cstring +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include <cstddef> - -namespace std { - - static inline size_t strlen(const char *str) { - size_t i = 0; - while (str[i]) - ++i; - return i; - } - -} diff --git a/libraries/euler/include/euler/syscall.hpp b/libraries/euler/include/euler/syscall.hpp deleted file mode 100644 index 2dc88b8..0000000 --- a/libraries/euler/include/euler/syscall.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <cstdint> - -namespace euler::syscall { - - typedef uint32_t encoded_color; - typedef int32_t exit_code; - typedef uint64_t file_handle; - typedef uint32_t key_packet; - - enum [[nodiscard]] file_result : uint64_t { - success, - bad_file_handle, - device_error, - file_system_corrupt, - out_of_bounds, - does_not_exist, - directory - }; - -} - -extern "C" { - - euler::syscall::encoded_color _syscall_encode_color( - uint8_t red, uint8_t green, uint8_t blue); - - void _syscall_get_framebuffer( - euler::syscall::encoded_color *&ptr_out, uint32_t &width_out, - uint32_t &height_out, uint32_t &pitch_out); - - euler::syscall::file_result _syscall_open_file( - const char *path, uint64_t path_length, euler::syscall::file_handle &out); - - euler::syscall::file_result _syscall_get_file_length( - euler::syscall::file_handle file, uint64_t &out); - - euler::syscall::file_result _syscall_read_from_file( - euler::syscall::file_handle file, - uint64_t start_offset, uint64_t length, void *into); - - [[noreturn]] void _syscall_end_this_process(euler::syscall::exit_code code); - - [[nodiscard]] void *_syscall_get_new_pages(uint64_t count); - - void _syscall_close_file(euler::syscall::file_handle file); - - euler::syscall::key_packet _syscall_read_key_packet(); - -} diff --git a/libraries/euler/syscall.asm b/libraries/euler/syscall.asm deleted file mode 100644 index c76a641..0000000 --- a/libraries/euler/syscall.asm +++ /dev/null @@ -1,88 +0,0 @@ -bits 64 - -section .text - -global _syscall_encode_color -_syscall_encode_color: - xor rax, rax - and edi, 0xff - and dx, 0xff - shl si, 8 - shl edx, 16 - or di, si - or edi, edx - syscall - ret - -global _syscall_get_framebuffer -_syscall_get_framebuffer: - push rcx - push rdx - push rsi - push rdi - mov rax, 1 - syscall - pop rcx - mov qword [rcx], rax - pop rcx - mov dword [rcx], edi - pop rcx - shr rdi, 32 - mov dword [rcx], edi - pop rcx - mov dword [rcx], esi - ret - -global _syscall_open_file -_syscall_open_file: - mov rax, 2 - push rdx - syscall - pop rdx - mov qword [rdx], rdi - ret - -global _syscall_get_file_length -_syscall_get_file_length: - mov rax, 3 - push rsi - syscall - pop rsi - mov qword [rsi], rdi - ret - -global _syscall_read_from_file -_syscall_read_from_file: - mov rax, 4 - push rcx - push rdx - push rsi - push rdi - mov rdi, rsp - syscall - add rsp, 32 - ret - -global _syscall_end_this_process -_syscall_end_this_process: - mov rax, 5 - syscall - ;does not return - -global _syscall_get_new_pages -_syscall_get_new_pages: - mov rax, 6 - syscall - ret - -global _syscall_close_file -_syscall_close_file: - mov rax, 7 - syscall - ret - -global _syscall_read_key_packet -_syscall_read_key_packet: - mov rax, 8 - syscall - ret diff --git a/limine.cfg b/limine.cfg deleted file mode 100644 index 4a411a1..0000000 --- a/limine.cfg +++ /dev/null @@ -1,5 +0,0 @@ -TIMEOUT=0 - -:Hilbert -PROTOCOL=limine -KERNEL_PATH=boot:///kernel.elf @@ -1,93 +1,124 @@ -GPP_ARGS = -std=c++17 -Wall -Wextra -O3 -ggdb -nostdinc \ - -fno-exceptions -ffreestanding -fno-rtti -mno-sse -KGPP_ARGS = ${GPP_ARGS} -I kernel/include -I mintsuki-freestanding-headers -AGPP_ARGS = ${GPP_ARGS} -I libraries/euler/include \ - -I libraries/daguerre/include +LIMINE_DIR = $(abspath dependencies/limine) +TOOLCHAIN_DIR = $(abspath toolchain) -LD_ARGS = -z noexecstack -KLD_ARGS = -T kernel/link.ld ${LD_ARGS} -ALD_ARGS = -T applications/link.ld ${LD_ARGS} -LLD_ARGS = ${LD_ARGS} +HILBERT_NASM = nasm -f elf64 +HILBERT_CC = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-c++ -std=c++17 -Wall \ + -Wextra -O3 -ggdb -static -fno-exceptions -fno-rtti -mno-sse -I include \ + -I $(abspath dependencies/mintsuki-headers) -I $(abspath euler/include) \ + -I $(abspath libraries/daguerre/include) +HILBERT_AR = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ar +HILBERT_LD = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ld -z noexecstack -all: out/disk.iso +.EXPORT_ALL_VARIABLES: -run: out/disk.iso +LIB_DIR = toolchain/usr/x86_64-elf/lib/ + +LIMINE_DEP = dependencies/.limine-done +MINTSUKI_HEADERS_DEP = dependencies/.mintsuki-headers-done +BINUTILS_DEP = toolchain/.binutils-done +GCC_DEP = toolchain/.gcc-done +LIBGCC_DEP = toolchain/.libgcc-done + +EULER_DEP = toolchain/.euler-done +DAGUERRE_DEP = toolchain/.daguerre-done + +APP_DEPS = ${GCC_DEP} ${LIBGCC_DEP} ${EULER_DEP} ${MINTSUKI_HEADERS_DEP} +LIBRARY_DEPS = ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} + +.PHONY: default run clean clean-dependencies + +default: build/disk.iso + +run: build/disk.iso gdb -x qemu.gdb clean: - rm -rf obj out - -dist-clean: - rm -rf limine mintsuki-freestanding-headers - -limine: - git clone --depth=1 -b v6.x-branch \ - https://github.com/limine-bootloader/limine.git limine - cd limine && ./bootstrap && ./configure -q --enable-bios --enable-bios-cd - +make -C limine - -mintsuki-freestanding-headers: - git clone --depth=1 \ - https://github.com/mintsuki/freestanding-headers.git \ - mintsuki-freestanding-headers - -obj/kernel/entry.cpp.o: kernel/entry.cpp limine mintsuki-freestanding-headers - @mkdir -p $(@D) - g++ -c ${KGPP_ARGS} $< -o $@ - -obj/kernel/%.cpp.o: kernel/%.cpp mintsuki-freestanding-headers - @mkdir -p $(@D) - g++ -c ${KGPP_ARGS} $< -o $@ - -obj/kernel/%.asm.o: kernel/%.asm - @mkdir -p $(@D) - nasm -f elf64 $< -o $@ - -KERNEL_OBJECTS = allocator.cpp application.cpp entry.cpp framebuffer.cpp \ - paging.asm paging.cpp storage.cpp storage/bd/memory.cpp terminal.cpp \ - storage/fs/tarfs.cpp utility.cpp vfile.cpp syscall.asm syscall.cpp \ - interrupts.asm interrupts.cpp input.cpp panic.cpp -obj/kernel.elf: ${KERNEL_OBJECTS:%=obj/kernel/%.o} - ld ${KLD_ARGS} $^ -o $@ - -obj/%.cpp.o: %.cpp mintsuki-freestanding-headers - @mkdir -p $(@D) - g++ -c ${AGPP_ARGS} $< -o $@ - -obj/%.asm.o: %.asm - @mkdir -p $(@D) - nasm -f elf64 $< -o $@ - -EULER_OBJECTS = entry.cpp syscall.asm cassert.cpp allocator.cpp cstdio.cpp \ - cctype.cpp -obj/euler.o: ${EULER_OBJECTS:%=obj/libraries/euler/%.o} - ld -r ${LLD_ARGS} $^ -o $@ - -DAGUERRE_OBJECTS = ppm.cpp -obj/daguerre.o: ${DAGUERRE_OBJECTS:%=obj/libraries/daguerre/%.o} - ld -r ${LLD_ARGS} $^ -o $@ - -INIT_OBJECTS = main.cpp -obj/initfs/bin/init.elf: ${INIT_OBJECTS:%=obj/applications/init/%.o} \ - obj/euler.o obj/daguerre.o - @mkdir -p $(@D) - ld ${ALD_ARGS} $^ -o $@ - -obj/initfs/.skeleton: - @mkdir -p obj/initfs - cp -r skeleton/* obj/initfs/ - @touch obj/initfs/.skeleton - -APPLICATIONS = init -obj/initfs.tgz: ${APPLICATIONS:%=obj/initfs/bin/%.elf} obj/initfs/.skeleton - tar czf obj/initfs.tgz -C obj/initfs . - -out/disk.iso: obj/kernel.elf obj/initfs.tgz limine - mkdir -p obj/iso out - cp obj/kernel.elf obj/initfs.tgz limine/bin/limine-bios.sys \ - limine/bin/limine-bios-cd.bin limine.cfg obj/iso/ - cp terminus/ter-u16b.psf obj/iso/termfont.psf - xorriso -as mkisofs -quiet -no-emul-boot -boot-info-table \ - -boot-load-size 4 -b limine-bios-cd.bin obj/iso -o $@ - limine/bin/limine bios-install $@ - rm -rf obj/iso + rm -rf build ${EULER_DEP} ${DAGUERRE_DEP} + make -C euler clean + make -C kernel clean + make -C applications/init clean + make -C libraries/daguerre clean + +clean-dependencies: clean + rm -rf toolchain dependencies + +${LIMINE_DEP}: + mkdir -p dependencies + test -e dependencies/limine || git clone --depth 1 -b v7.5.1 https://github.com/limine-bootloader/limine dependencies/limine + cd ${LIMINE_DIR} && ./bootstrap + cd ${LIMINE_DIR} && ./configure --enable-bios --enable-bios-cd + +make -C ${LIMINE_DIR} + touch $@ + +${MINTSUKI_HEADERS_DEP}: + mkdir -p dependencies + test -e dependencies/mintsuki-headers || git clone --depth 1 https://github.com/mintsuki/freestanding-headers dependencies/mintsuki-headers + touch $@ + +${BINUTILS_DEP}: + mkdir -p dependencies toolchain/usr + test -e dependencies/binutils || (git clone --depth 1 -b binutils-2_42 https://sourceware.org/git/binutils-gdb dependencies/binutils && cd dependencies/binutils && git apply ../../patches/binutils.txt) + mkdir -p dependencies/binutils/build + cd dependencies/binutils/build && ../configure --disable-gdb \ + --target=x86_64-elf --prefix=${TOOLCHAIN_DIR}/usr + +make -C dependencies/binutils/build + +make -C dependencies/binutils/build install + touch $@ + +${GCC_DEP}: ${BINUTILS_DEP} + mkdir -p toolchain/usr/include + test -e dependencies/gcc || git clone --depth 1 -b releases/gcc-14.1.0 https://gcc.gnu.org/git/gcc dependencies/gcc + mkdir -p dependencies/gcc/build + cd dependencies/gcc/build && ../configure --disable-fixed-point \ + --disable-gcov --disable-multilib --disable-shared --enable-languages=c++ \ + --target=x86_64-elf --prefix=${TOOLCHAIN_DIR}/usr --without-headers + +make -C dependencies/gcc/build all-gcc + +make -C dependencies/gcc/build install-gcc + touch $@ + +${LIBGCC_DEP}: ${GCC_DEP} + +make -C dependencies/gcc/build all-target-libgcc + +make -C dependencies/gcc/build install-target-libgcc + touch $@ + +${EULER_DEP}: ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} + +make -C euler build/crt0.o build/libc.a build/libg.a build/libm.a \ + build/libstdc++.a + mkdir -p toolchain/usr/x86_64-elf/lib + cp euler/build/crt0.o euler/build/libc.a euler/build/libg.a \ + euler/build/libm.a euler/build/libstdc++.a ${LIB_DIR} + touch $@ + +${DAGUERRE_DEP}: ${LIBRARY_DEPS} + +make -C libraries/daguerre build/libdaguerre.a + cp libraries/daguerre/build/libdaguerre.a ${LIB_DIR} + touch $@ + +kernel/build/kernel.elf: ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} ${LIMINE_DEP} + +make -C kernel build/kernel.elf + +applications/init/build/init.elf: ${APP_DEPS} ${DAGUERRE_DEP} + +make -C applications/init build/init.elf + +build/initfs.tgz: applications/init/build/init.elf + @mkdir -p build + rm -rf build/initfs + cp -r skeleton build/initfs + mkdir build/initfs/bin + cp applications/init/build/init.elf build/initfs/bin/init + cd build/initfs && tar czf ../initfs.tgz . + +build/disk.iso: kernel/build/kernel.elf build/initfs.tgz ${LIMINE_DEP} + @mkdir -p build + rm -rf build/iso + mkdir build/iso + cp kernel/build/kernel.elf ${LIMINE_DIR}/bin/limine-bios.sys \ + ${LIMINE_DIR}/bin/limine-bios-cd.bin build/initfs.tgz build/iso/ + echo 'TIMEOUT=0' > build/iso/limine.cfg + echo ':Hilbert OS' >> build/iso/limine.cfg + echo 'PROTOCOL=limine' >> build/iso/limine.cfg + echo 'KERNEL_PATH=boot:///kernel.elf' >> build/iso/limine.cfg + echo 'MODULE_PATH=$$boot:///initfs.tgz' >> build/iso/limine.cfg + echo 'MODULE_CMDLINE=initfs' >> build/iso/limine.cfg + xorriso -as mkisofs -b limine-bios-cd.bin -no-emul-boot -boot-load-size 4 \ + -boot-info-table --protective-msdos-label build/iso -o $@ diff --git a/patches/binutils.txt b/patches/binutils.txt new file mode 100644 index 0000000..b2dcff7 --- /dev/null +++ b/patches/binutils.txt @@ -0,0 +1,17 @@ +diff --git a/ld/emulparams/elf_x86_64.sh b/ld/emulparams/elf_x86_64.sh +index 466da2c4..806cd413 100644 +--- a/ld/emulparams/elf_x86_64.sh ++++ b/ld/emulparams/elf_x86_64.sh +@@ -14,9 +14,9 @@ SCRIPT_NAME=elf + ELFSIZE=64 + OUTPUT_FORMAT="elf64-x86-64" + NO_REL_RELOCS=yes +-TEXT_START_ADDR=0x400000 +-MAXPAGESIZE="CONSTANT (MAXPAGESIZE)" +-COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)" ++TEXT_START_ADDR=0x200000 ++MAXPAGESIZE="0x1000" ++COMMONPAGESIZE="0x1000" + ARCH="i386:x86-64" + MACHINE= + TEMPLATE_NAME=elf @@ -1,8 +1,6 @@ -target remote | qemu-system-x86_64 -gdb stdio -cdrom out/disk.iso -boot d -symbol-file obj/kernel.elf -add-symbol-file obj/initfs/bin/init.elf +target remote | qemu-system-x86_64 -gdb stdio -cdrom build/disk.iso -boot d +symbol-file kernel/build/kernel.elf +add-symbol-file build/initfs/bin/init set disassembly-flavor intel set print asm-demangle on -break entry -break main layout src @@ -1,48 +1,74 @@ -hilbert os is a 64-bit hobby operating system, which is not -very mature yet. to build and test it, you will need some -dependencies. these can be installed on debian with: - apt install g++ gcc gdb git make nasm qemu-system-x86 xorriso +hilbert os is a 64-bit hobby operating system, which is not very mature yet. to +build and test it, you will need some software installed. on debian, i believe +running command [1] below as root (e.g. with sudo) is sufficient. the default +makefile target builds a disk image at build/disk.iso that can be booted on a +64-bit bios system. you can use command [2] to build that. finally, use command +[3] to run the disk in qemu with gdb attached. -then, just run "make -jx", replacing x with the number of threads to use -while building. this will create a bios-bootable disk image in out/disk.iso. -you can then test it in qemu with gdb attached by running "make run". + [1] apt install g++ gdb git make nasm qemu-system-x86 xorriso + [2] make -j$(nproc) + [3] make run -acknowledgements (* = downloaded during make): +acknowledgements (any under "dependencies" are downloaded during build): - - limine bootloader* - homepage: https://limine-bootloader.org/ - license: limine/COPYING (bsd two-clause) + - dependencies/binutils (gnu binutils v2.42) + copyright 2024 free software foundation, inc. + license: dependencies/binutils/COPYING (gnu gpl v2) + homepage: https://www.gnu.org/software/binutils/ + note: the patch in patches/binutils.txt is applied before building - - terminus font - homepage: https://terminus-font.sourceforge.net/ - license: terminus/license.txt (sil open font license v1.1) + - dependencies/gcc (gnu compiler collection v14.1.0) + copyright 2024 free software foundation, inc. + license: + dependencies/gcc/COPYING3 (gnu gpl v3) + dependencies/gcc/COPYING.RUNTIME (gcc runtime library exception v3.1) + homepage: https://gcc.gnu.org/ - - photo at skeleton/assets/burdon.ppm - photographer: aaron burdon - source: https://unsplash.com/photos/selective-focus-photography-snowflakes-9yhy1FXlKwI - license: https://unsplash.com/license + - dependencies/limine (limine bootloader v7.5.1) + copyright 2019 - 2024 mintsuki and contributors + license: dependencies/limine/COPYING (bsd two-clause) + homepage: https://limine-bootloader.org/ - - mintsuki's freestanding c headers* - homepage: https://github.com/mintsuki/freestanding-headers - license: mintsuki-freestanding-headers/LICENSE (bsd zero-clause) + - dependencies/minstuki-headers + copyright 2022 - 2024 mintsuki and contributors + license: dependencies/mintsuki-headers/LICENSE (bsd zero-clause) + homepage: https://github.com/mintsuki/freestanding-headers/ + + - skeleton/assets/burden.ppm + ("selective focus photography snowflakes" by aaron burden) + license: https://unsplash.com/license + source: https://unsplash.com/photos/selective-focus-photography-snowflakes-9yhy1FXlKwI + +everything in the following directories is copyright 2024 benji dial, under the +license in license.txt (the isc license): + + - applications + - euler + - kernel + - libraries project structure: - - applications/init: the first application started by the kernel - - applications/link.ld: a common linker script used by every application - - documentation: documentation on the kernel (not very organized) - - kernel: the kernel of hilbert os - - libraries/daguerre: an image loading / rendering library - - libraries/euler: the c++ standard library and runtime for applications - - limine: the limine bootloader (see acknowledgements) - - mintsuki-freestanding-headers: - mintsuki's freestanding headers (see acknowledgements) - - obj: built object files - - out: completed builds - - skeleton: files that are directly copied to the initfs - - terminus: the terminus font (see acknowledgements) - - license.txt: the license that hilbert os is under - - limine.cfg: the limine configuration used by the built disk - - makefile: the makefile that is used to build the entire os - - qmeu.gdb: a file for gdb to include when doing make run - - readme.txt: this file + - applications/init: + the initial program loaded by the kernel. currently it displays the image + by aaron burden, and inverts the colors when the enter key is pressed. + + - documentation: + documentation. currently this directory is a bit disorganized, and has + some descriptions of things that have not been created yet. + + - euler: + (a minimal start to) a c/c++ standard library and runtime. the plan is to + follow the c++17 standard, and only add things as i need them. + + - kernel: + the kernel. + + - libraries/daguerre: + an image loading / rendering library. + + - patches/binutils.txt: + a patch that is applied to gnu binutils before it is built. + + - skeleton: + files that are copied directly to the initfs. diff --git a/skeleton/init/burdon.ppm b/skeleton/assets/burden.ppm index fe4a66a..fe4a66a 100644 --- a/skeleton/init/burdon.ppm +++ b/skeleton/assets/burden.ppm diff --git a/skeleton/assets/readme.txt b/skeleton/assets/readme.txt new file mode 100644 index 0000000..56b1a9b --- /dev/null +++ b/skeleton/assets/readme.txt @@ -0,0 +1,4 @@ +the photo in burden.ppm is "selective focus photography snowflakes" by aaron +burden. it can be found online at +https://unsplash.com/photos/selective-focus-photography-snowflakes-9yhy1FXlKwI. +its license can be found online at https://unsplash.com/license. diff --git a/skeleton/cfg/default.key b/skeleton/cfg/default.key new file mode 120000 index 0000000..9ad07aa --- /dev/null +++ b/skeleton/cfg/default.key @@ -0,0 +1 @@ +qwerty.key
\ No newline at end of file diff --git a/skeleton/cfg/init.sh b/skeleton/cfg/init.sh new file mode 100644 index 0000000..312dba0 --- /dev/null +++ b/skeleton/cfg/init.sh @@ -0,0 +1 @@ +/bin/wm & diff --git a/skeleton/init/readme.txt b/skeleton/init/readme.txt deleted file mode 100644 index 294909f..0000000 --- a/skeleton/init/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -the photo in burdon.ppm is by aaron burdon, and can be found online at -https://unsplash.com/photos/selective-focus-photography-snowflakes-9yhy1FXlKwI. -its license can be found at https://unsplash.com/license. diff --git a/terminus/license.txt b/terminus/license.txt deleted file mode 100644 index 5168372..0000000 --- a/terminus/license.txt +++ /dev/null @@ -1,94 +0,0 @@ -Copyright (C) 2020 Dimitar Toshkov Zhekov,
-with Reserved Font Name "Terminus Font".
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/terminus/readme.txt b/terminus/readme.txt deleted file mode 100644 index dcf0163..0000000 --- a/terminus/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -the file ter-u16b.psf contains the terminus font, version 4.49.1, at 8x16 -bold with the td1 patch applied. terminus is licensed under the sil open -font license, version 1.1, which is in the file license.txt. - -terminus home page: https://terminus-font.sourceforge.net/ diff --git a/terminus/ter-u16b.psf b/terminus/ter-u16b.psf Binary files differdeleted file mode 100644 index 3215b16..0000000 --- a/terminus/ter-u16b.psf +++ /dev/null |