From 7199e74aa22e592a3b77bdd81f735edca5470596 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Sat, 20 Jan 2024 17:59:40 -0500 Subject: update --- kernel/allocator.cpp | 2 +- kernel/application.cpp | 16 +- kernel/entry.cpp | 29 +- kernel/framebuffer.cpp | 38 +-- kernel/include/hilbert/kernel/application.hpp | 77 ++++++ kernel/include/hilbert/kernel/framebuffer.hpp | 31 +++ kernel/include/hilbert/kernel/paging.hpp | 42 +++ kernel/include/hilbert/kernel/storage.hpp | 106 ++++++++ .../include/hilbert/kernel/storage/bd/memory.hpp | 20 ++ kernel/include/hilbert/kernel/storage/fs/tarfs.hpp | 33 +++ kernel/include/hilbert/kernel/terminal.hpp | 32 +++ kernel/include/hilbert/kernel/utility.hpp | 297 +++++++++++++++++++++ kernel/include/hilbert/kernel/vfile.hpp | 63 +++++ kernel/storage/fs/tarfs.cpp | 102 +++---- kernel/syscall.cpp | 250 ++++++++++++++++- kernel/utility.cpp | 6 + kernel/vfile.cpp | 85 +++--- 17 files changed, 1060 insertions(+), 169 deletions(-) create mode 100644 kernel/include/hilbert/kernel/application.hpp create mode 100644 kernel/include/hilbert/kernel/framebuffer.hpp create mode 100644 kernel/include/hilbert/kernel/paging.hpp create mode 100644 kernel/include/hilbert/kernel/storage.hpp create mode 100644 kernel/include/hilbert/kernel/storage/bd/memory.hpp create mode 100644 kernel/include/hilbert/kernel/storage/fs/tarfs.hpp create mode 100644 kernel/include/hilbert/kernel/terminal.hpp create mode 100644 kernel/include/hilbert/kernel/utility.hpp create mode 100644 kernel/include/hilbert/kernel/vfile.hpp (limited to 'kernel') diff --git a/kernel/allocator.cpp b/kernel/allocator.cpp index 807bc94..324f992 100644 --- a/kernel/allocator.cpp +++ b/kernel/allocator.cpp @@ -1,5 +1,5 @@ #include -#include +#include namespace hilbert::kernel::allocator { diff --git a/kernel/application.cpp b/kernel/application.cpp index ed8795b..af31a09 100644 --- a/kernel/application.cpp +++ b/kernel/application.cpp @@ -72,6 +72,15 @@ namespace hilbert::kernel::application { } + 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; @@ -129,7 +138,10 @@ namespace hilbert::kernel::application { bool executable; }; - create_app_result create_app(const vfile::vfile &file, app_instance *&out) { + 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) @@ -259,6 +271,8 @@ namespace hilbert::kernel::application { 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/entry.cpp b/kernel/entry.cpp index 8ba585f..3a42df7 100644 --- a/kernel/entry.cpp +++ b/kernel/entry.cpp @@ -3,9 +3,9 @@ #include #include #include -#include #include #include +#include "../limine/limine.h" using namespace hilbert::kernel; @@ -197,32 +197,31 @@ extern "C" [[noreturn]] void start_user_mode( terminal::init_terminal(); - storage::bd::memory initfs_bd(initfs, initfs_len); - storage::fs::tarfs_instance initfs_fs(&initfs_bd); - initfs_bd.mounted_as = &initfs_fs; + auto *initfs_bd = new storage::bd::memory(initfs, initfs_len); + auto *initfs_fs = new storage::fs::tarfs_instance(initfs_bd); + initfs_bd->mounted_as = initfs_fs; - vfile::vfile vfs_root; - vfs_root.bd = &initfs_bd; - vfs_root.dir_entry.type = storage::file_type::directory; - vfs_root.path.absolute = true; + vfile::vfile initfs_root; + initfs_root.bd = initfs_bd; + initfs_root.dir_entry.type = storage::file_type::directory; + initfs_root.path.absolute = true; - if (initfs_fs.get_root_node(vfs_root.dir_entry.node) != + if (initfs_fs->get_root_node(initfs_root.dir_entry.node) != storage::fs_result::success) print_and_halt("failed to get root node of initfs."); + vfile::set_root(initfs_root); + utility::string init_path_string("/bin/init.elf", 13); vfile::canon_path init_path; vfile::canonize_path(init_path_string, init_path); - std::optional init_file; - if (vfile::lookup_path(vfs_root, init_path, init_file) != - storage::fs_result::success) + vfile::vfile init_file; + if (vfile::lookup_path(init_path, init_file) != storage::fs_result::success) print_and_halt("failed to look up /bin/init.elf."); - if (!init_file) - print_and_halt("/bin/init.elf does not exist."); application::app_instance *init; - if (application::create_app(*init_file, init) != + if (application::create_app(init_file, init, initfs_root) != application::create_app_result::success) print_and_halt("failed to parse /bin/init.elf."); diff --git a/kernel/framebuffer.cpp b/kernel/framebuffer.cpp index dbd735f..08d0e16 100644 --- a/kernel/framebuffer.cpp +++ b/kernel/framebuffer.cpp @@ -1,44 +1,13 @@ #include #include -#include namespace hilbert::kernel::framebuffer { - static uint64_t paddr; + uint64_t paddr; static uint32_t *vaddr; int width; int height; - static int dword_pitch; - - void encode_color_syscall( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx - ) { - rax = (uint64_t)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 = (dword_pitch * 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, paddr + i * 4096, true, false, false); - app->framebuffer_vaddr = vaddr; - } - - rax = app->framebuffer_vaddr; - rdi = (uint64_t)(uint32_t)width | ((uint64_t)(uint32_t)height << 32); - rsi = (uint32_t)dword_pitch; - rdx = 0; - - } + int dword_pitch; void init_framebuffer(uint64_t paddr, uint64_t vaddr, uint64_t width, uint64_t height, uint64_t pitch @@ -52,9 +21,6 @@ namespace hilbert::kernel::framebuffer { framebuffer::height = height; dword_pitch = pitch / 4; - syscall::add_syscall(0, &encode_color_syscall); - syscall::add_syscall(1, &get_framebuffer_syscall); - } color encode_color(uint8_t r, uint8_t g, uint8_t b) { diff --git a/kernel/include/hilbert/kernel/application.hpp b/kernel/include/hilbert/kernel/application.hpp new file mode 100644 index 0000000..51f304d --- /dev/null +++ b/kernel/include/hilbert/kernel/application.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +//TODO: end application, threading. + +namespace hilbert::kernel::application { + + void init_syscalls(); + + enum class app_state { + running, + paused, + zombie + }; + + struct app_instance { + + utility::id_allocator open_files; + + vfile::vfile working_dir; + + app_state state; + + uint64_t *p4; + uint64_t *p3; + uint64_t *p2s[512]; + uint64_t **p1s[512]; + + bool **p1es_to_free_on_exit[512]; + + uint64_t p4_paddr; + + //set to 0 if none + uint64_t framebuffer_vaddr; + + //only valid if state is zombie + int32_t exit_code; + + //only valid if state is paused + struct { + uint64_t rip; + uint64_t rsp; + //TODO: etc. + } saved_regs; + + app_instance(); + + //vaddr and paddr must be aligned, and vaddr must be < 0x0080.0000.0000 + void map_page(uint64_t vaddr, uint64_t paddr, + bool write, bool execute, bool free_pram_on_exit); + + //where "owned" means in lower half and marked free on exit + bool is_page_owned(uint64_t vaddr); + + //returns start of first page + uint64_t get_free_vaddr_pages(uint64_t count); + + //in lower half + uint64_t count_mapped_vram_pages(); + + }; + + extern app_instance *running_app; + + enum class create_app_result { + success, + device_error, + app_corrupt, + fs_corrupt + }; + + create_app_result create_app(const vfile::vfile &file, + app_instance *&out, const vfile::vfile &working_dir); + +} diff --git a/kernel/include/hilbert/kernel/framebuffer.hpp b/kernel/include/hilbert/kernel/framebuffer.hpp new file mode 100644 index 0000000..fb28462 --- /dev/null +++ b/kernel/include/hilbert/kernel/framebuffer.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace hilbert::kernel::framebuffer { + + extern uint64_t paddr; + extern int width; + extern int height; + extern int dword_pitch; + + void init_framebuffer( + uint64_t paddr, uint64_t vaddr, uint64_t width, + uint64_t height, uint64_t pitch); + + typedef uint32_t color; + color encode_color(uint8_t r, uint8_t g, uint8_t b); + + 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); + +} diff --git a/kernel/include/hilbert/kernel/paging.hpp b/kernel/include/hilbert/kernel/paging.hpp new file mode 100644 index 0000000..91ab5f7 --- /dev/null +++ b/kernel/include/hilbert/kernel/paging.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +//in paging.asm +extern "C" [[noreturn]] void switch_to_kernel_p4(void (*and_then_jump_to)()); + +namespace hilbert::kernel::paging { + + void mark_all_pram_used(); + void mark_all_vram_free(); + + void mark_pram_region_free(uint64_t start_addr, uint64_t end_addr); + void mark_vram_region_used(uint64_t start_addr, uint64_t end_addr); + + uint64_t find_unmapped_vram_region(uint64_t page_count); + + uint64_t encode_pte(uint64_t addr, bool user, bool write, bool execute); + + void init_kernel_page_tables(uint64_t kernel_offset); + + uint64_t take_pram_page(); + + void map_kernel_stacks(); + + void map_kernel_page( + uint64_t paddr, uint64_t vaddr, bool write, bool execute); + + void unmap_kernel_page(uint64_t vaddr); + + //maps writable and not executable + void *map_new_kernel_pages(uint64_t count); + + //maps writable and not executable + void map_new_kernel_page(uint64_t &vaddr_out, uint64_t &paddr_out); + + uint64_t get_used_vram_page_count(); + uint64_t get_free_pram_page_count(); + + extern uint64_t kernel_p4e; + +} diff --git a/kernel/include/hilbert/kernel/storage.hpp b/kernel/include/hilbert/kernel/storage.hpp new file mode 100644 index 0000000..2e2169e --- /dev/null +++ b/kernel/include/hilbert/kernel/storage.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include + +namespace hilbert::kernel::storage { + + typedef uint64_t node_id_t; + typedef uint64_t directory_iter_t; + + enum class file_type { + regular_file, + directory, + symlink + }; + + enum class bd_result { + success, + out_of_bounds, + device_error + }; + + enum class fs_result { + success, + device_error, + fs_corrupt, + does_not_exist + }; + + struct dir_entry { + utility::string name; + node_id_t node; + file_type type; + //only used if type is regular_file. + uint64_t length; + //only used if type is symlink. + utility::string target; + }; + + class file_system_instance { + + public: + virtual ~file_system_instance() {} + + virtual fs_result get_root_node(node_id_t &out) = 0; + + //it is assumed that this is a directory. sets iter_out + //to a value that can be passed to subsequent calls + //to get_next_child with the same argument for node. + virtual fs_result get_first_child( + node_id_t node, dir_entry &out, directory_iter_t &iter_out) = 0; + + //it is assumed that this is a directory. sets iter_out + //to a value that can be passed to subsequent calls + //to get_next_child with the same argument for node. + virtual fs_result get_next_child( + node_id_t node, dir_entry &out, directory_iter_t &iter) = 0; + + //it is assumed that this is a regular file and that + //the region requested is within the bounds of the file. + virtual fs_result read_bytes_from_file( + node_id_t node, uint64_t start, uint64_t count, void *into) = 0; + + }; + + class block_device { + + private: + uint8_t *block_cache; + uint64_t block_cache_i; + + //it is assumed that this block is within the bounds of the device. + bd_result load_cache_block(uint64_t i); + + protected: + //implemented in driver. it is assumed that the + //blocks are within the bounds of the device. + virtual bd_result read_blocks_no_cache( + uint64_t start, uint64_t count, void *into) = 0; + + //it is assumed that this is only called once, by the driver, after + //block_size and block_count have been set, and before read_bytes or + //write_bytes can be called (e.g. in the derived class's constructor). + void allocate_block_cache(); + + public: + //set by storage component + file_system_instance *mounted_as; + //set by storage component + utility::uuid id; + + //set by driver + uint64_t block_size; + //set by driver + uint64_t block_count; + + virtual ~block_device() { + if (block_cache) + delete block_cache; + } + + bd_result read_bytes(uint64_t start, uint64_t count, void *into); + + }; + +} diff --git a/kernel/include/hilbert/kernel/storage/bd/memory.hpp b/kernel/include/hilbert/kernel/storage/bd/memory.hpp new file mode 100644 index 0000000..e057241 --- /dev/null +++ b/kernel/include/hilbert/kernel/storage/bd/memory.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace hilbert::kernel::storage::bd { + + class memory : public block_device { + + private: + uint8_t *buffer; + + public: + memory(void *buffer, uint64_t buffer_len); + + bd_result read_blocks_no_cache( + uint64_t start, uint64_t count, void *into) override; + + }; + +} diff --git a/kernel/include/hilbert/kernel/storage/fs/tarfs.hpp b/kernel/include/hilbert/kernel/storage/fs/tarfs.hpp new file mode 100644 index 0000000..cef35f2 --- /dev/null +++ b/kernel/include/hilbert/kernel/storage/fs/tarfs.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace hilbert::kernel::storage::fs { + + class tarfs_instance : public file_system_instance { + + private: + block_device *bd; + + fs_result next_node(node_id_t node, node_id_t &out); + fs_result read_full_name(node_id_t node, utility::string &out); + //len <= 12 + fs_result read_num(uint64_t offset, unsigned len, uint64_t &out); + fs_result first_child_starting_at( + node_id_t parent, node_id_t start, node_id_t &out); + fs_result get_dir_entry(node_id_t node, dir_entry &entry); + + public: + tarfs_instance(block_device *bd); + + fs_result get_root_node(node_id_t &out); + fs_result get_first_child(node_id_t node, + dir_entry &out, directory_iter_t &iter_out); + fs_result get_next_child( + node_id_t node, dir_entry &out, directory_iter_t &iter); + fs_result read_bytes_from_file( + node_id_t node, uint64_t start, uint64_t count, void *into); + + }; + +} diff --git a/kernel/include/hilbert/kernel/terminal.hpp b/kernel/include/hilbert/kernel/terminal.hpp new file mode 100644 index 0000000..350d79b --- /dev/null +++ b/kernel/include/hilbert/kernel/terminal.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +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/include/hilbert/kernel/utility.hpp b/kernel/include/hilbert/kernel/utility.hpp new file mode 100644 index 0000000..2df1d65 --- /dev/null +++ b/kernel/include/hilbert/kernel/utility.hpp @@ -0,0 +1,297 @@ +#pragma once + +#include +#include + +void *operator new(size_t, void *ptr); +void operator delete(void *, void *); + +namespace hilbert::kernel::utility { + + template + struct no_ref; + template + struct no_ref { + typedef t _t; + }; + template + struct no_ref { + typedef t _t; + }; + template + struct no_ref { + typedef t _t; + }; + + template + constexpr typename no_ref::_t &&move(t &&v) { + return static_cast::_t &&>(v); + } + + template + static inline t min(t a, t b) { + return a < b ? a : b; + } + + //includes start_i, does not include end_i + void mark_bitmap_region_zero( + uint64_t *bitmap, uint64_t start_i, uint64_t end_i); + //includes start_i, does not include end_i + void mark_bitmap_region_one( + uint64_t *bitmap, uint64_t start_i, uint64_t end_i); + + struct uuid { + uint8_t bytes[16]; + }; + + //if c appears in str, this returns the index of the first time it appears. + //otherwise, this returns len. + static inline unsigned find(const char *str, unsigned len, char c) { + for (unsigned i = 0; i < len; ++i) + if (str[i] == c) + return i; + return len; + } + + //if c appears in str, this returns the index of the last time it appears. + //otherwise, this returns len. + static inline unsigned find_last(const char *str, unsigned len, char c) { + for (unsigned i = len; i > 0; --i) + if (str[i - 1] == c) + return i - 1; + return len; + } + + //str1 starts with str2 + static inline bool starts_with( + const char *str1, unsigned str1_len, const char *str2, unsigned str2_len + ) { + if (str1_len < str2_len) + return false; + for (unsigned i = 0; i < str2_len; ++i) + if (str1[i] != str2[i]) + return false; + return true; + } + + template + struct list { + + struct node { + value_t value; + node *next; + node *prev; + }; + + node *first; + node *last; + + list() : first(0), last(0) {} + + list(const list &other) = delete; + list(list &&other) = delete; + + ~list() { + if (first) { + for (node *n = first->next; n; n = n->next) + delete n->prev; + delete last; + } + } + + list &operator =(const list &other) = delete; + list &operator =(list &&other) = delete; + + void insert_end(const value_t &value) { + node *n = new node {}; + n->value = value; + n->next = 0; + n->prev = last; + last = n; + } + + void insert_end(value_t &&value) { + node *n = new node {}; + n->value = value; + n->next = 0; + n->prev = last; + last = n; + } + + void clear() { + for (node *n = first; n; n = n->next) + delete n; + first = 0; + last = 0; + } + + //assumes there is a last. + void remove_last() { + node *new_last = last->prev; + if (new_last) + new_last->next = 0; + else + first = 0; + delete last; + last = new_last; + } + + void remove(node *n) { + if (n->prev) + n->prev->next = n->next; + else + first = n->next; + if (n->next) + n->next->prev = n->prev; + else + last = n->prev; + delete n; + } + + }; + + template + struct vector { + + value_t *buffer; + unsigned buffer_len; + unsigned count; + + vector(unsigned buffer_len = 16) + : buffer(new value_t[buffer_len]), buffer_len(buffer_len), count(0) {} + + vector(const value_t *copy_from, unsigned len) + : buffer(new value_t[len]), buffer_len(len), count(len) { + for (unsigned i = 0; i < len; ++i) + buffer[i] = copy_from[i]; + } + + vector(const vector &other) + : buffer(new value_t[other.buffer_len]), + buffer_len(other.buffer_len), count(other.count) + { + for (unsigned i = 0; i < count; ++i) + buffer[i] = other.buffer[i]; + } + + vector(vector &&other) = delete; + + ~vector() { + if (buffer) + delete[] buffer; + } + + vector &operator =(const vector &other) { + if (buffer) + delete[] buffer; + buffer = new value_t[other.buffer_len]; + buffer_len = other.buffer_len; + count = other.count; + for (unsigned i = 0; i < other.count; ++i) + buffer[i] = other.buffer[i]; + return *this; + } + + vector &operator =(vector &&other) { + if (buffer) + delete[] buffer; + buffer = other.buffer; + buffer_len = other.buffer_len; + count = other.count; + other.buffer = 0; + return *this; + } + + bool operator ==(const vector &other) { + if (other.count != count) + return false; + for (unsigned i = 0; i < count; ++i) + if (other.buffer[i] != buffer[i]) + return false; + return true; + } + + void verify_buffer_len(unsigned target_len) { + if (buffer_len >= target_len) + return; + unsigned new_buffer_len = buffer_len; + do + new_buffer_len *= 2; + while (new_buffer_len < target_len); + value_t *new_buffer = new value_t[new_buffer_len]; + for (unsigned i = 0; i < count; ++i) + new_buffer[i] = move(buffer[i]); + delete[] buffer; + buffer = new_buffer; + buffer_len = new_buffer_len; + } + + void add_end(value_t &&v) { + verify_buffer_len(count + 1); + buffer[count] = move(v); + ++count; + } + + void add_end(const value_t &v) { + verify_buffer_len(count + 1); + buffer[count] = v; + ++count; + } + + bool starts_with(const vector &other) { + if (count < other.count) + return false; + for (unsigned i = 0; i < other.count; ++i) + if (buffer[i] != other.buffer[i]) + return false; + return true; + } + + }; + + typedef vector string; + + template + struct id_allocator { + + vector the_vector; + + public: + id_allocator() : the_vector() {} + id_allocator(const id_allocator &other) = delete; + id_allocator(id_allocator &&other) = delete; + id_allocator &operator =(const id_allocator &other) = delete; + id_allocator &operator =(id_allocator &&other) = delete; + + //returns id; pointer becomes owned by id_allocator + unsigned add(value_t *v) { + for (unsigned i = 0; i < the_vector.count; ++i) + if (the_vector.buffer[i] == 0) { + the_vector.buffer[i] = v; + return i; + } + the_vector.add_end(v); + return the_vector.count - 1; + } + + //returns id + unsigned add_new(value_t &&v) { + return add(new value_t(move(v))); + } + + bool has_id(unsigned i) { + return i < the_vector.count && the_vector.buffer[i] != 0; + } + + value_t &get(unsigned i) { + return *the_vector.buffer[i]; + } + + void remove_id(unsigned i) { + delete the_vector.buffer[i]; + the_vector.buffer[i] = 0; + } + + }; + +} diff --git a/kernel/include/hilbert/kernel/vfile.hpp b/kernel/include/hilbert/kernel/vfile.hpp new file mode 100644 index 0000000..f8a387d --- /dev/null +++ b/kernel/include/hilbert/kernel/vfile.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +//TODO: mounts points. +//maybe a two-way map between mount points and targets? one mount point per +//target and vice versa. only directories may be mount points, and only file +//system roots (which must be directories) may be targets. + +namespace hilbert::kernel::vfile { + + //a canon path contains no . or empty directory names, and + //contains no .. except for at the start of a relative path. + struct canon_path { + + bool absolute; + unsigned parent_count; + utility::vector segments; + + canon_path() : absolute(false), parent_count(0), segments(8) {} + + void parent(); + void rel(const canon_path &r); + + utility::string to_string(bool trailing_slash) const; + + }; + + void canonize_path(const utility::string &name, canon_path &out); + + struct vfile { + + //on followed symlinks, path is the source of the symlink and the + //rest of these are the target. in any case, path should be absolute. + canon_path path; + storage::block_device *bd; + storage::dir_entry dir_entry; + + storage::fs_result follow_symlinks(vfile &out) const; + + //dir_entry.type is assumed to be directory. + storage::fs_result get_child( + vfile &out, const utility::string &name) const; + //dir_entry.type is assumed to be directory. out must be empty on entry. + storage::fs_result get_children(utility::vector &out) const; + + //assumes file is a regular file and [start, start + length) + //is in bounds of file. start and length are in bytes. + storage::fs_result read_file( + uint64_t start, uint64_t length, void *into) const; + + }; + + //TODO: see comment at top of file. + void set_root(const vfile &root); + + //path must be absolute. follows symlinks on all but the last node. + //relative_to should be a directory to do the lookup inside; e.g., + //if relative_to is /a/b and path is c, then out becomes /a/b/c. + storage::fs_result lookup_path(const canon_path &path, vfile &out); + +} diff --git a/kernel/storage/fs/tarfs.cpp b/kernel/storage/fs/tarfs.cpp index fb1eff8..5986f62 100644 --- a/kernel/storage/fs/tarfs.cpp +++ b/kernel/storage/fs/tarfs.cpp @@ -5,27 +5,25 @@ namespace hilbert::kernel::storage::fs { -#define BD_TO_FS(expr) \ - { \ - bd_result _result = expr; \ - if (_result == bd_result::out_of_bounds) \ - return fs_result::fs_corrupt; \ - if (_result == bd_result::device_error) \ - return fs_result::device_error; \ - } + #define BD_TO_FS(expr) \ + { \ + bd_result _result = expr; \ + if (_result == bd_result::out_of_bounds) \ + return fs_result::fs_corrupt; \ + if (_result == bd_result::device_error) \ + return fs_result::device_error; \ + } -#define FS_TO_FS(expr) \ - { \ - fs_result _result = expr; \ - if (_result != fs_result::success) \ - return _result; \ - } + #define FS_TO_FS(expr) \ + { \ + fs_result _result = expr; \ + if (_result != fs_result::success) \ + return _result; \ + } tarfs_instance::tarfs_instance(block_device *bd) : bd(bd) {} - fs_result tarfs_instance::next_node( - node_id_t node, std::optional &out - ) { + fs_result tarfs_instance::next_node(node_id_t node, node_id_t &out) { uint64_t bytes; FS_TO_FS(read_num(node + 124, 12, bytes)) @@ -37,8 +35,7 @@ namespace hilbert::kernel::storage::fs { if (sector[i] != 0) return fs_result::success; - out = {}; - return fs_result::success; + return fs_result::does_not_exist; } @@ -84,7 +81,7 @@ namespace hilbert::kernel::storage::fs { } fs_result tarfs_instance::first_child_starting_at( - node_id_t parent, node_id_t start, std::optional &out + node_id_t parent, node_id_t start, node_id_t &out ) { utility::string parent_full_name; @@ -93,9 +90,9 @@ namespace hilbert::kernel::storage::fs { utility::string child_full_name; out = start; - do { + while (true) { - FS_TO_FS(read_full_name(*out, child_full_name)) + FS_TO_FS(read_full_name(out, child_full_name)) if (child_full_name.count > parent_full_name.count && child_full_name.starts_with(parent_full_name) @@ -110,11 +107,9 @@ namespace hilbert::kernel::storage::fs { } next: - next_node(*out, out); + FS_TO_FS(next_node(out, out)) - } while (out); - - return fs_result::success; + } } @@ -171,7 +166,7 @@ namespace hilbert::kernel::storage::fs { BD_TO_FS(bd->read_bytes(node + 157, 100, target.buffer)) while (target.count < 100 && target.buffer[target.count] != '\0') ++target.count; - entry.target = std::move(target); + entry.target = utility::move(target); } return fs_result::success; @@ -181,64 +176,53 @@ namespace hilbert::kernel::storage::fs { fs_result tarfs_instance::get_root_node(node_id_t &out) { utility::string full_name; - std::optional on = 0; + node_id_t on = 0; - do { + while (true) { - FS_TO_FS(read_full_name(*on, full_name)) + FS_TO_FS(read_full_name(on, full_name)) if (full_name.count == 2) { - out = *on; + out = on; return fs_result::success; } - next_node(*on, on); - - } while (on); + fs_result result = next_node(on, on); + if (result == fs_result::does_not_exist) + return fs_result::fs_corrupt; + FS_TO_FS(result) - return fs_result::fs_corrupt; + } } fs_result tarfs_instance::get_first_child( - node_id_t node, std::optional &out, directory_iter_t &iter_out + node_id_t node, dir_entry &out, directory_iter_t &iter_out ) { - std::optional child; + node_id_t child; FS_TO_FS(first_child_starting_at(node, 0, child)) - if (!child) { - out = {}; - return fs_result::success; - } dir_entry entry; - FS_TO_FS(get_dir_entry(*child, entry)) - out = std::move(entry); - iter_out = (directory_iter_t)*child; + FS_TO_FS(get_dir_entry(child, entry)) + out = utility::move(entry); + iter_out = (directory_iter_t)child; return fs_result::success; } fs_result tarfs_instance::get_next_child( - node_id_t node, std::optional &out, directory_iter_t &iter + node_id_t node, dir_entry &out, directory_iter_t &iter ) { - std::optional start; + node_id_t start; FS_TO_FS(next_node((node_id_t)iter, start)) - if (!start) { - out = {}; - return fs_result::success; - } - std::optional child; - FS_TO_FS(first_child_starting_at(node, *start, child)) - if (!child) { - out = {}; - return fs_result::success; - } + node_id_t child; + FS_TO_FS(first_child_starting_at(node, start, child)) dir_entry entry; - FS_TO_FS(get_dir_entry(*child, entry)) - out = std::move(entry); - iter = (directory_iter_t)*child; + FS_TO_FS(get_dir_entry(child, entry)) + out = utility::move(entry); + iter = (directory_iter_t)child; return fs_result::success; } diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 3aa4105..3c72d23 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -1,21 +1,247 @@ #include #include -#include #include +#include namespace hilbert::kernel::syscall { - syscall_handler handlers[256]; + 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 + }; - void init_syscalls() { - for (int i = 0; i < 256; ++i) - handlers[i] = 0; + 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 add_syscall(uint64_t rax, syscall_handler handler) { - handlers[rax] = handler; + 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)) { + 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; + } + + vfile::vfile real_file; + switch (file.follow_symlinks(real_file)) { + 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 (real_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(real_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); + + } + + 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 + }; + + static constexpr int max_syscall_number = 7; + } using namespace hilbert::kernel::syscall; @@ -24,13 +250,9 @@ extern "C" void do_syscall( uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx ) { - if (rax < 256 && handlers[rax] != 0) + if (rax <= max_syscall_number && handlers[rax] != 0) handlers[rax](rax, rdi, rsi, rdx); - else { - rax = 0; - rdi = 0; - rsi = 0; - rdx = 0; - } + else + set_zero(rax, rdi, rsi, rdx); } diff --git a/kernel/utility.cpp b/kernel/utility.cpp index 249fb2c..12e88fd 100644 --- a/kernel/utility.cpp +++ b/kernel/utility.cpp @@ -1,5 +1,11 @@ #include +void *operator new(size_t, void *ptr) { + return ptr; +} + +void operator delete(void *, void *) {} + namespace hilbert::kernel::utility { void mark_bitmap_region_zero( diff --git a/kernel/vfile.cpp b/kernel/vfile.cpp index 74cfbf9..028db6d 100644 --- a/kernel/vfile.cpp +++ b/kernel/vfile.cpp @@ -63,7 +63,7 @@ namespace hilbert::kernel::vfile { else { utility::string segment(str, segment_len); - out.segments.add_end(std::move(segment)); + out.segments.add_end(utility::move(segment)); } str += to_skip; @@ -80,9 +80,7 @@ namespace hilbert::kernel::vfile { return _result; \ } - storage::fs_result vfile::follow_symlinks( - const vfile &root, std::optional &out - ) const { + storage::fs_result vfile::follow_symlinks(vfile &out) const { if (dir_entry.type != storage::file_type::symlink) { out = *this; @@ -95,37 +93,33 @@ namespace hilbert::kernel::vfile { full_path.parent(); full_path.rel(target_path); - std::optional next; - RET_NOT_SUC(lookup_path(root, full_path, next)) - if (!next) { - out = {}; - return storage::fs_result::success; - } + vfile next; + RET_NOT_SUC(lookup_path(full_path, next)) - next->path = path; - return next->follow_symlinks(root, out); + next.path = path; + return next.follow_symlinks(out); } storage::fs_result vfile::get_child( - std::optional &out, const utility::string &name + vfile &out, const utility::string &name ) const { - std::optional entry; + storage::dir_entry entry; storage::directory_iter_t iter; RET_NOT_SUC(bd->mounted_as->get_first_child(dir_entry.node, entry, iter)) - while (entry) { + while (true) { - if (entry->name == name) { + if (entry.name == name) { vfile vf; vf.bd = bd; - vf.dir_entry = std::move(*entry); + vf.dir_entry = utility::move(entry); vf.path = path; vf.path.segments.add_end(name); - out = std::move(vf); + out = utility::move(vf); return storage::fs_result::success; } @@ -134,33 +128,37 @@ namespace hilbert::kernel::vfile { } - out = {}; - return storage::fs_result::success; - } storage::fs_result vfile::get_children(utility::vector &out) const { - std::optional entry; + storage::dir_entry entry; storage::directory_iter_t iter; - RET_NOT_SUC(bd->mounted_as->get_first_child(dir_entry.node, entry, iter)) + storage::fs_result result = + bd->mounted_as->get_first_child(dir_entry.node, entry, iter); + if (result == storage::fs_result::does_not_exist) + return storage::fs_result::success; + else if (result != storage::fs_result::success) + return result; - while (entry) { + while (true) { vfile vf; vf.bd = bd; vf.path = path; - vf.path.segments.add_end(entry->name); - vf.dir_entry = std::move(*entry); - out.add_end(std::move(vf)); + vf.path.segments.add_end(entry.name); + vf.dir_entry = utility::move(entry); + out.add_end(utility::move(vf)); - RET_NOT_SUC(bd->mounted_as->get_next_child(dir_entry.node, entry, iter)) + result = bd->mounted_as->get_next_child(dir_entry.node, entry, iter); + if (result == storage::fs_result::does_not_exist) + return storage::fs_result::success; + else if (result != storage::fs_result::success) + return result; } - return storage::fs_result::success; - } storage::fs_result vfile::read_file( @@ -170,25 +168,26 @@ namespace hilbert::kernel::vfile { dir_entry.node, start, length, into); } - storage::fs_result lookup_path( - const vfile &root, const canon_path &path, std::optional &out - ) { + //TODO: see comment at top of vfile.hpp. + static const vfile *root; + + void set_root(const vfile &root) { + kernel::vfile::root = new vfile(root); + } + + storage::fs_result lookup_path(const canon_path &path, vfile &out) { //assume path is absolute. - out = root; + out = *root; for (unsigned i = 0; i < path.segments.count; ++i) { - std::optional result; - RET_NOT_SUC(out->follow_symlinks(root, result)) - out = std::move(result); - if (!out) - return storage::fs_result::success; + vfile result; + RET_NOT_SUC(out.follow_symlinks(result)) + out = utility::move(result); - RET_NOT_SUC(out->get_child(result, path.segments.buffer[i])) - out = std::move(result); - if (!out) - return storage::fs_result::success; + RET_NOT_SUC(out.get_child(result, path.segments.buffer[i])) + out = utility::move(result); } -- cgit v1.2.3