summaryrefslogtreecommitdiff
path: root/kernel/source
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/source')
-rw-r--r--kernel/source/allocator.cpp160
-rw-r--r--kernel/source/application.asm171
-rw-r--r--kernel/source/application.cpp550
-rw-r--r--kernel/source/entry.cpp222
-rw-r--r--kernel/source/framebuffer.cpp40
-rw-r--r--kernel/source/input.cpp22
-rw-r--r--kernel/source/interrupts.asm340
-rw-r--r--kernel/source/interrupts.cpp152
-rw-r--r--kernel/source/paging.asm16
-rw-r--r--kernel/source/paging.cpp145
-rw-r--r--kernel/source/panic.cpp11
-rw-r--r--kernel/source/storage.cpp67
-rw-r--r--kernel/source/storage/bd/memory.cpp21
-rw-r--r--kernel/source/storage/fs/tarfs.cpp238
-rw-r--r--kernel/source/syscall.cpp538
-rw-r--r--kernel/source/utility.cpp51
-rw-r--r--kernel/source/vfile.cpp206
17 files changed, 2950 insertions, 0 deletions
diff --git a/kernel/source/allocator.cpp b/kernel/source/allocator.cpp
new file mode 100644
index 0000000..324f992
--- /dev/null
+++ b/kernel/source/allocator.cpp
@@ -0,0 +1,160 @@
+#include <hilbert/kernel/paging.hpp>
+#include <stddef.h>
+
+namespace hilbert::kernel::allocator {
+
+ struct free_entry {
+ uint64_t start;
+ uint64_t len;//0 for unused
+ };
+
+ struct free_page {
+ free_page *next;
+ free_entry entries[255];
+ };
+
+ free_page *first_page;
+
+ static_assert(sizeof(free_page) == 4088);
+
+ free_entry *get_entry(uint64_t start, uint64_t len) {
+ for (free_page *fp = first_page; fp; fp = fp->next)
+ for (int i = 0; i < 255; ++i)
+ if (fp->entries[i].start == start && fp->entries[i].len == len)
+ return fp->entries + i;
+ return 0;
+ }
+
+ void add_entry(uint64_t start, uint64_t len) {
+ for (free_page *fp = first_page; fp; fp = fp->next)
+ for (int i = 0; i < 255; ++i)
+ if (fp->entries[i].len == 0) {
+ fp->entries[i].start = start;
+ fp->entries[i].len = len;
+ return;
+ }
+ free_page *new_page = (free_page *)paging::map_new_kernel_pages(1);
+ new_page->next = first_page;
+ first_page = new_page;
+ for (int i = 2; i < 255; ++i)
+ new_page->entries[i].len = 0;
+ new_page->entries[0].start = (uint64_t)new_page + 4088;
+ new_page->entries[0].len = 8;
+ new_page->entries[1].start = start;
+ new_page->entries[1].len = len;
+ }
+
+ //len is power of 2, start is len-aligned
+ void free_block(uint64_t start, uint64_t len) {
+ free_entry *buddy = get_entry(start ^ len, len);
+ if (buddy) {
+ buddy->start = start & ~len;
+ buddy->len = len * 2;
+ }
+ else
+ add_entry(start, len);
+ }
+
+ void free_region(uint64_t start, uint64_t len) {
+ uint64_t block_size = 1;
+ while (block_size <= len) {
+ if (start & block_size) {
+ free_block(start, block_size);
+ start += block_size;
+ len -= block_size;
+ }
+ block_size *= 2;
+ }
+ while (len) {
+ block_size /= 2;
+ if (block_size <= len) {
+ free_block(start, block_size);
+ start += block_size;
+ len -= block_size;
+ }
+ }
+ //testing
+ if (len != 0)
+ while (1)
+ ;
+ }
+
+ uint64_t take_region(uint64_t len) {
+
+ uint64_t min_size = 1;
+ while (min_size < len)
+ min_size *= 2;
+
+ free_entry *entry = 0;
+
+ for (free_page *fp = first_page; fp; fp = fp->next)
+ for (int i = 0; i < 255; ++i)
+ if (fp->entries[i].len >= min_size) {
+ if (fp->entries[i].len == min_size) {
+ entry = fp->entries + i;
+ goto loop_done;
+ }
+ if (entry == 0 || fp->entries[i].len < entry->len)
+ entry = fp->entries + i;
+ }
+
+ loop_done:
+ if (entry != 0) {
+ uint64_t start = entry->start;
+ uint64_t block_len = entry->len;
+ entry->len = 0;
+ if (block_len != len)
+ free_region(start + len, block_len - len);
+ return start;
+ }
+
+ uint64_t pages = (len - 1) / 4096 + 1;
+ uint64_t start = (uint64_t)paging::map_new_kernel_pages(pages);
+ if (pages * 4096 != len)
+ free_region(start + len, pages * 4096 - len);
+ return start;
+
+ }
+
+}
+
+using namespace hilbert::kernel::allocator;
+
+void *_new(size_t len) {
+ if (len == 0)
+ return 0;
+ uint64_t vaddr = take_region(len + sizeof(size_t));
+ *(size_t *)vaddr = len;
+ return (void *)(vaddr + sizeof(size_t));
+}
+
+void _delete(void *ptr) {
+ if ((uint64_t)ptr == 0)
+ return;
+ uint64_t vaddr = (uint64_t)ptr - sizeof(size_t);
+ free_region(vaddr, *(size_t *)vaddr + sizeof(size_t));
+}
+
+void *operator new(size_t len) {
+ return _new(len);
+}
+
+void operator delete(void *ptr, size_t) {
+ return _delete(ptr);
+}
+
+void operator delete(void *ptr) {
+ return _delete(ptr);
+}
+
+void *operator new[](size_t len) {
+ return _new(len);
+}
+
+void operator delete[](void *ptr, size_t) {
+ return _delete(ptr);
+}
+
+void operator delete[](void *ptr) {
+ return _delete(ptr);
+}
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/source/entry.cpp b/kernel/source/entry.cpp
new file mode 100644
index 0000000..820b107
--- /dev/null
+++ b/kernel/source/entry.cpp
@@ -0,0 +1,222 @@
+#include <hilbert/kernel/storage/bd/memory.hpp>
+#include <hilbert/kernel/storage/fs/tarfs.hpp>
+#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>
+#include <limine.h>
+
+using namespace hilbert::kernel;
+
+LIMINE_BASE_REVISION(1)
+
+static volatile limine_memmap_request memmap_request {
+ .id = LIMINE_MEMMAP_REQUEST,
+ .revision = 0,
+ .response = 0
+};
+
+static volatile limine_kernel_address_request kernel_address_request {
+ .id = LIMINE_KERNEL_ADDRESS_REQUEST,
+ .revision = 0,
+ .response = 0
+};
+
+static volatile limine_framebuffer_request framebuffer_request {
+ .id = LIMINE_FRAMEBUFFER_REQUEST,
+ .revision = 0,
+ .response = 0
+};
+
+static volatile limine_hhdm_request hhdm_request {
+ .id = LIMINE_HHDM_REQUEST,
+ .revision = 0,
+ .response = 0
+};
+
+static volatile limine_module_request module_request = {
+ .id = LIMINE_MODULE_REQUEST,
+ .revision = 2,
+ .response = 0,
+ .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)
+ if (!cmdline[j]) {
+
+ //module start is guaranteed to be page-aligned, end is not.
+ uint64_t start_paddr =
+ (uint64_t)file->address - hhdm_request.response->offset;
+ uint64_t end_paddr =
+ ((start_paddr + file->size - 1) / 4096 + 1) * 4096;
+
+ uint64_t start_vaddr =
+ paging::find_unmapped_vram_region((end_paddr - start_paddr) / 4096);
+ for (uint64_t i = 0; i < end_paddr - start_paddr; i += 4096)
+ paging::map_kernel_page(
+ start_paddr + i, start_vaddr + i, true, false);
+
+ vaddr_out = (void *)start_vaddr;
+ len_out = file->size;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+//defined in linker script, page-aligned:
+extern uint8_t __kernel_start;
+extern uint8_t __kernel_end;
+extern uint8_t __kernel_rx_start;
+extern uint8_t __kernel_rx_end;
+extern uint8_t __kernel_ro_start;
+extern uint8_t __kernel_ro_end;
+extern uint8_t __kernel_rw_start;
+extern uint8_t __kernel_rw_end;
+
+uint8_t *initfs;
+uint64_t initfs_len;
+
+[[noreturn]] static void with_kernel_p4();
+
+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
+ // fulfilled and display some error message if not
+
+ //set up the physical memory usage bitmap:
+
+ paging::mark_all_pram_used();
+ auto memmap = memmap_request.response;
+ for (uint64_t i = 0; i < memmap->entry_count; ++i) {
+ //we don't allocate any physical pages until after we are done using limine
+ //structures (specifically at the call to paging::map_kernel_stacks), so
+ //we consider bootloader reclaimable to be free. usable and bootloader
+ //reclaimable are guaranteed by limine spec to be page-aligned.
+ auto entry = memmap->entries[i];
+ if (entry->type == LIMINE_MEMMAP_USABLE ||
+ entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE)
+ paging::mark_pram_region_free(entry->base, entry->base + entry->length);
+ }
+
+ //set up page mappings:
+
+ auto kernel_address = kernel_address_request.response;
+ uint64_t kernel_offset = kernel_address->virtual_base
+ - kernel_address->physical_base;
+
+ uint64_t hhdm = hhdm_request.response->offset;
+
+ //framebuffer might not be page-aligned
+ auto framebuffer = framebuffer_request.response->framebuffers[0];
+ uint64_t fb_start = ((uint64_t)framebuffer->address / 4096) * 4096 - hhdm;
+ uint64_t fb_end = (uint64_t)framebuffer->address
+ + framebuffer->pitch * framebuffer->height - hhdm;
+ fb_end = ((fb_end - 1) / 4096 + 1) * 4096;
+
+ paging::init_kernel_page_tables(kernel_offset);
+
+ //kernel image rx
+ for (uint64_t vaddr = (uint64_t)&__kernel_rx_start;
+ vaddr < (uint64_t)&__kernel_rx_end; vaddr += 4096)
+ paging::map_kernel_page(vaddr - kernel_offset, vaddr, false, true);
+
+ //kernel image ro
+ for (uint64_t vaddr = (uint64_t)&__kernel_ro_start;
+ vaddr < (uint64_t)&__kernel_ro_end; vaddr += 4096)
+ paging::map_kernel_page(vaddr - kernel_offset, vaddr, false, false);
+
+ //kernel image rw
+ for (uint64_t vaddr = (uint64_t)&__kernel_rw_start;
+ vaddr < (uint64_t)&__kernel_rw_end; vaddr += 4096)
+ paging::map_kernel_page(vaddr - kernel_offset, vaddr, true, false);
+
+ //framebuffer
+ uint64_t fb_vaddr =
+ paging::find_unmapped_vram_region((fb_end - fb_start) / 4096);
+ for (uint64_t i = 0; i < fb_end - fb_start; i += 4096)
+ paging::map_kernel_page(fb_start + i, fb_vaddr + i, true, false);
+
+ have_initfs =
+ try_map_module_by_cmdline("initfs", (void *&)initfs, initfs_len);
+
+ //set up framebuffer and terminal:
+ //TODO: assumes framebuffer is 32-bpp rgb
+
+ framebuffer::init_framebuffer(fb_start, fb_vaddr,
+ framebuffer->width, framebuffer->height, framebuffer->pitch);
+
+ //switch to kernel p4
+
+ paging::map_kernel_stacks();
+ load_gdt_and_idt();
+ switch_to_kernel_p4(&with_kernel_p4);
+
+}
+
+[[noreturn]] static void with_kernel_p4() {
+
+ 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);
+ initfs_bd->mounted_as = initfs_fs;
+
+ 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(initfs_root.dir_entry.node) !=
+ storage::fs_result::success)
+ panic(0x48a6ed);
+
+ vfile::set_root(initfs_root);
+
+ 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(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/source/interrupts.asm b/kernel/source/interrupts.asm
new file mode 100644
index 0000000..babc020
--- /dev/null
+++ b/kernel/source/interrupts.asm
@@ -0,0 +1,340 @@
+bits 64
+
+global load_gdt_and_idt
+
+section .rodata
+
+;0x28 picked to align with limine choice
+
+;0x18 - tss
+;0x28 - kernel code
+;0x30 - kernel data
+;0x38 - user data
+;0x40 - user code
+
+tss:
+ times 9 dd 0
+ dq 0xffffffffffeff000
+ times 15 dd 0
+
+gdtr:
+ dw 0x47
+ dq gdt
+
+idtr:
+ dw 4095
+ dq idt
+
+section .bss
+
+idt:
+ resq 512
+
+global exception_info
+exception_info:
+.rax:
+ resq 1
+.rbx:
+ resq 1
+.rcx:
+ resq 1
+.rdx:
+ resq 1
+.rdi:
+ resq 1
+.rsi:
+ resq 1
+.rbp:
+ resq 1
+.rsp:
+ resq 1
+.r8:
+ resq 1
+.r9:
+ resq 1
+.r10:
+ resq 1
+.r11:
+ resq 1
+.r12:
+ resq 1
+.r13:
+ resq 1
+.r14:
+ resq 1
+.r15:
+ resq 1
+.cr2:
+ resq 1
+.cr3:
+ resq 1
+.rip:
+ resq 1
+.rflags:
+ resq 1
+.error:
+ resq 1
+.has_error:
+ resb 1
+.exception_number:
+ resb 1
+
+section .rodata
+
+has_error_code:
+ db 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0
+
+exception_isrs:
+ dq exception_00, exception_01, exception_02, exception_03
+ dq exception_04, exception_05, exception_06, exception_07
+ dq exception_08, exception_09, exception_0a, exception_0b
+ dq exception_0c, exception_0d, exception_0e, exception_0f
+
+section .text
+
+extern print_exception
+
+exception_00:
+ mov byte [exception_info.exception_number], 0x00
+ jmp exception_common
+exception_01:
+ mov byte [exception_info.exception_number], 0x01
+ jmp exception_common
+exception_02:
+ mov byte [exception_info.exception_number], 0x02
+ jmp exception_common
+exception_03:
+ mov byte [exception_info.exception_number], 0x03
+ jmp exception_common
+exception_04:
+ mov byte [exception_info.exception_number], 0x04
+ jmp exception_common
+exception_05:
+ mov byte [exception_info.exception_number], 0x05
+ jmp exception_common
+exception_06:
+ mov byte [exception_info.exception_number], 0x06
+ jmp exception_common
+exception_07:
+ mov byte [exception_info.exception_number], 0x07
+ jmp exception_common
+exception_08:
+ mov byte [exception_info.exception_number], 0x08
+ jmp exception_common
+exception_09:
+ mov byte [exception_info.exception_number], 0x09
+ jmp exception_common
+exception_0a:
+ mov byte [exception_info.exception_number], 0x0a
+ jmp exception_common
+exception_0b:
+ mov byte [exception_info.exception_number], 0x0b
+ jmp exception_common
+exception_0c:
+ mov byte [exception_info.exception_number], 0x0c
+ jmp exception_common
+exception_0d:
+ mov byte [exception_info.exception_number], 0x0d
+ jmp exception_common
+exception_0e:
+ mov byte [exception_info.exception_number], 0x0e
+ jmp exception_common
+exception_0f:
+ mov byte [exception_info.exception_number], 0x0f
+ jmp exception_common
+
+exception_common:
+ mov qword [exception_info.rax], rax
+
+ movzx rax, byte [exception_info.exception_number]
+ mov al, byte [has_error_code + rax]
+ test al, al
+ jz .no_error_code
+
+ mov byte [exception_info.has_error], 1
+ pop rax
+ mov qword [exception_info.error], rax
+ jmp .post_error_code
+
+.no_error_code:
+ mov byte [exception_info.has_error], 0
+
+.post_error_code:
+ mov qword [exception_info.rbx], rbx
+ mov qword [exception_info.rcx], rcx
+ mov qword [exception_info.rdx], rdx
+ mov qword [exception_info.rdi], rdi
+ mov qword [exception_info.rsi], rsi
+ mov qword [exception_info.rbp], rbp
+ mov qword [exception_info.r8], r8
+ mov qword [exception_info.r9], r9
+ mov qword [exception_info.r10], r10
+ mov qword [exception_info.r11], r11
+ mov qword [exception_info.r12], r12
+ mov qword [exception_info.r13], r13
+ mov qword [exception_info.r14], r14
+ mov qword [exception_info.r15], r15
+
+ pop rax
+ mov qword [exception_info.rip], rax
+ pop rax
+ pop rax
+ mov qword [exception_info.rflags], rax
+ pop rax
+ mov qword [exception_info.rsp], rax
+
+ mov rax, cr2
+ mov qword [exception_info.cr2], rax
+ mov rax, cr3
+ mov qword [exception_info.cr3], rax
+
+ jmp print_exception
+
+set_isr:
+;rdi - index
+;rsi - isr pointer
+
+ shl rdi, 4
+ add rdi, idt
+
+ mov word [rdi], si
+ shr rsi, 16
+ mov word [rdi + 6], si
+ shr rsi, 16
+ mov dword [rdi + 8], esi
+
+ mov byte [rdi + 5], 0x8e
+ mov word [rdi + 2], 0x28
+ mov byte [rdi + 4], 1
+
+ ret
+
+section .data
+
+gdt:
+ dq 0
+ dq 0
+ dq 0
+.tss:
+ dq 0x0000e90000000067
+ dq 0;tss is 2 qwords wide
+ dq 0x002f98000000ffff
+ dq 0x002f92000000ffff
+ dq 0x002ff2000000ffff
+ dq 0x002ff8000000ffff
+
+section .bss
+
+section .text
+
+write_keyboard_byte:
+ in al, 0x64
+ test al, 0x02
+ jnz write_keyboard_byte
+ mov al, dil
+ out 0x60, al
+ ret
+
+extern on_keyboard_interrupt
+
+keyboard_isr:
+
+ push r11
+ push r10
+ push r9
+ push r8
+ push rsi
+ push rdi
+ push rdx
+ push rcx
+ push rax
+
+ in al, 0x60
+ mov dil, al
+
+ call on_keyboard_interrupt
+
+ mov al, 0x20
+ out 0x20, al
+
+ pop rax
+ pop rcx
+ pop rdx
+ pop rdi
+ pop rsi
+ pop r8
+ pop r9
+ pop r10
+ pop r11
+
+ iretq
+
+load_gdt_and_idt:
+
+ ;fill exception entries in idt
+
+ mov rcx, 16
+
+.loop:
+
+ mov rdi, rcx
+ dec rdi
+ mov rsi, qword [exception_isrs + rdi * 8]
+ call set_isr
+
+ loop .loop
+
+ ;reset pic and map irqs to 0x20 - 0x2f
+
+ mov al, 0x11
+ out 0x20, al
+ mov al, 0x20
+ out 0x21, al
+ mov al, 0x04
+ out 0x21, al
+ mov al, 0x01
+ out 0x21, al
+ mov al, 0xfd ;mask all but irq 1
+ out 0x21, al
+
+ mov al, 0x11
+ out 0xa0, al
+ mov al, 0x28
+ out 0xa1, al
+ mov al, 0x02
+ out 0xa1, al
+ mov al, 0x01
+ out 0xa1, al
+ mov al, 0xff ;mask all
+ out 0xa1, al
+
+ mov rdi, 0x21
+ mov rsi, keyboard_isr
+ call set_isr
+
+ ;set keyboard config
+
+ mov al, 0x60
+ out 0x64, al
+ mov dil, 0x01
+ call write_keyboard_byte
+
+ ;make tss entry in gdt
+
+ mov rax, tss
+
+ mov word [gdt.tss + 2], ax
+ shr rax, 16
+ mov byte [gdt.tss + 4], al
+ mov byte [gdt.tss + 7], ah
+ shr rax, 16
+ mov dword [gdt.tss + 8], eax
+
+ ;load gdt, idt, tss
+
+ lgdt [gdtr]
+ lidt [idtr]
+ mov ax, 0x18
+ ltr ax
+
+ ret
diff --git a/kernel/source/interrupts.cpp b/kernel/source/interrupts.cpp
new file mode 100644
index 0000000..6e22121
--- /dev/null
+++ b/kernel/source/interrupts.cpp
@@ -0,0 +1,152 @@
+#include <hilbert/kernel/input.hpp>
+#include <hilbert/kernel/panic.hpp>
+
+using namespace hilbert::kernel;
+
+struct [[gnu::packed]] exception_info_t {
+
+ 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 cr2;
+ uint64_t cr3;
+ uint64_t rip;
+ uint64_t rflags;
+
+ uint64_t error;
+ uint8_t has_error;//0 or 1
+ uint8_t exception_number;
+
+};
+
+extern exception_info_t exception_info;
+
+extern "C" [[noreturn]] void print_exception() {
+
+ //so exception_info's type is known by gdb
+ exception_info_t the_exception_info = exception_info;
+ (void)the_exception_info;
+
+ //TODO: log exception, and recover if possible.
+
+ panic(0xba40bb);
+
+}
+
+static uint32_t current_flags = 0;
+
+#define SETBIT(field, bit, cond) \
+ field = (cond) ? (field | (bit)) : (field & ~(bit));
+
+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;
+
+ else if (key == (input::BREAK | 0x58))
+ current_flags ^= input::CAPS_LOCK;
+
+ else if ((key & 0xff) == 0xa7)
+ SETBIT(current_flags, input::RIGHT_WIN, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x9f)
+ SETBIT(current_flags, input::LEFT_WIN, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x91)
+ SETBIT(current_flags, input::RIGHT_ALT, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x11)
+ SETBIT(current_flags, input::LEFT_ALT, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x94)
+ SETBIT(current_flags, input::RIGHT_CTRL, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x14)
+ SETBIT(current_flags, input::LEFT_CTRL, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x59)
+ SETBIT(current_flags, input::RIGHT_SHIFT, !(key & input::BREAK))
+
+ else if ((key & 0xff) == 0x12)
+ SETBIT(current_flags, input::LEFT_SHIFT, !(key & input::BREAK))
+
+}
+
+static uint8_t key_so_far[8];
+uint8_t key_so_far_len = 0;
+
+extern "C" void on_keyboard_interrupt(uint8_t byte) {
+
+ key_so_far[key_so_far_len++] = byte;
+
+ if (key_so_far_len == 1) {
+ if (byte != 0xe0 && byte != 0xe1 && byte != 0xf0) {
+ got_key(byte);
+ key_so_far_len = 0;
+ }
+ }
+
+ else if (key_so_far_len == 2) {
+ if (key_so_far[0] == 0xe0 && byte != 0xf0 && byte != 0x12) {
+ got_key(byte | 0x80);
+ key_so_far_len = 0;
+ }
+ else if (key_so_far[0] == 0xf0) {
+ got_key(input::BREAK | byte);
+ key_so_far_len = 0;
+ }
+ }
+
+ else if (key_so_far_len == 3) {
+ if (key_so_far[0] == 0xe0 && key_so_far[1] == 0xf0 && byte != 0x7c) {
+ got_key(input::BREAK | byte | 0x80);
+ key_so_far_len = 0;
+ }
+ }
+
+ else if (key_so_far_len == 4) {
+ if (key_so_far[0] == 0xe0 && key_so_far[1] == 0x12 &&
+ key_so_far[2] == 0xe0 && byte == 0x7c) {
+ got_key(0xe0);
+ key_so_far_len = 0;
+ }
+ }
+
+ else if (key_so_far_len == 6) {
+ if (key_so_far[0] == 0xe0 && key_so_far[1] == 0xf0 &&
+ key_so_far[2] == 0x7c && key_so_far[3] == 0xe0 &&
+ key_so_far[4] == 0xf0 && byte == 0x12) {
+ got_key(input::BREAK | 0xe0);
+ key_so_far_len = 0;
+ }
+ }
+
+ else if (key_so_far_len == 8) {
+ if (key_so_far[0] == 0xe1 && key_so_far[1] == 0x14 &&
+ key_so_far[2] == 0x77 && key_so_far[3] == 0xe1 &&
+ key_so_far[2] == 0xf0 && key_so_far[3] == 0x14 &&
+ key_so_far[4] == 0xf0 && byte == 0x77) {
+ got_key(0xe1);
+ got_key(input::BREAK | 0xe1);
+ }
+ key_so_far_len = 0;
+ }
+
+}
diff --git a/kernel/source/paging.asm b/kernel/source/paging.asm
new file mode 100644
index 0000000..f1047a9
--- /dev/null
+++ b/kernel/source/paging.asm
@@ -0,0 +1,16 @@
+bits 64
+
+;see also ../documentation/memory.txt
+
+global switch_to_kernel_p4
+
+;from paging.cpp:
+extern __kernel_p4_paddr
+
+section .text
+
+switch_to_kernel_p4:
+ mov rax, qword [__kernel_p4_paddr]
+ mov cr3, rax
+ mov rsp, 0xfffffffffffff000
+ jmp rdi
diff --git a/kernel/source/paging.cpp b/kernel/source/paging.cpp
new file mode 100644
index 0000000..d8869fc
--- /dev/null
+++ b/kernel/source/paging.cpp
@@ -0,0 +1,145 @@
+#include <hilbert/kernel/utility.hpp>
+#include <hilbert/kernel/paging.hpp>
+
+//see also ../documentation/memory.txt
+
+extern "C" {
+ uint64_t __kernel_p4_paddr;
+}
+
+namespace hilbert::kernel::paging {
+
+ static constexpr uint64_t kernel_vram_start = 0xffffffffc0000000;
+ static constexpr uint64_t kernel_vram_end = 0xffffffffffe00000;
+ static constexpr uint64_t kernel_vram_pages =
+ (kernel_vram_end - kernel_vram_start) / 4096;
+ static constexpr uint64_t syscall_stack_bottom = 0xfffffffffff01000;
+ static constexpr uint64_t syscall_stack_top = 0xfffffffffffff000;
+ static constexpr uint64_t interrupt_stack_bottom = 0xffffffffffe01000;
+ static constexpr uint64_t interrupt_stack_top = 0xffffffffffeff000;
+
+ static constexpr uint64_t pram_pages = 1 << 23;
+ static uint64_t pram_usage_bitmap[pram_pages / 64];
+
+ void mark_all_pram_used() {
+ utility::mark_bitmap_region_one(pram_usage_bitmap, 0, pram_pages);
+ }
+
+ void mark_pram_region_free(uint64_t start_addr, uint64_t end_addr) {
+ utility::mark_bitmap_region_zero(
+ pram_usage_bitmap, start_addr / 4096,
+ utility::min(end_addr / 4096, pram_pages));
+ }
+
+ [[gnu::aligned(4096)]] uint64_t kernel_p4[512];
+ [[gnu::aligned(4096)]] uint64_t kernel_p3[512];
+ [[gnu::aligned(4096)]] uint64_t kernel_p2[512];
+ [[gnu::aligned(4096)]] uint64_t kernel_p1s[512 * 512];
+
+ uint64_t kernel_p4e;
+
+ uint64_t encode_pte(uint64_t addr, bool user, bool write, bool execute) {
+ return (addr & 0x0000ffffffffffff) | (execute ? 0 : (1ULL << 63))
+ | (user << 2) | (write << 1) | 1;
+ }
+
+ void init_kernel_page_tables(uint64_t kernel_offset) {
+ __kernel_p4_paddr = (uint64_t)kernel_p4 - kernel_offset;
+ for (int i = 0; i < 511; ++i)
+ kernel_p4[i] = 0;
+ kernel_p4e = encode_pte(
+ (uint64_t)kernel_p3 - kernel_offset, false, true, true);
+ kernel_p4[511] = kernel_p4e;
+ for (int i = 0; i < 511; ++i)
+ kernel_p3[i] = 0;
+ kernel_p3[511] = encode_pte(
+ (uint64_t)kernel_p2 - kernel_offset, false, true, true);
+ for (int i = 0; i < 512; ++i)
+ kernel_p2[i] = encode_pte(
+ (uint64_t)kernel_p1s + 4096 * i - kernel_offset, false, true, true);
+ for (int i = 0; i < 512 * 512; ++i)
+ kernel_p1s[i] = 0;
+ }
+
+ void map_kernel_page(
+ uint64_t paddr, uint64_t vaddr, bool write, bool execute) {
+ uint64_t i = (vaddr - kernel_vram_start) / 4096;
+ kernel_p1s[i] = encode_pte(paddr, false, write, execute);
+ }
+
+ void unmap_kernel_page(uint64_t vaddr) {
+ uint64_t i = (vaddr - kernel_vram_start) / 4096;
+ kernel_p1s[i] = 0;
+ asm volatile (
+ "mov %%cr3, %%rax\nmov %%rax, %%cr3" ::: "%rax"
+ );
+ }
+
+ uint64_t take_pram_page() {
+ for (uint64_t i = 0; i < pram_pages / 64; ++i)
+ if (~pram_usage_bitmap[i] != 0)
+ for (int j = 0; j < 64; ++j)
+ if (~pram_usage_bitmap[i] & (1ULL << j)) {
+ pram_usage_bitmap[i] |= (1ULL << j);
+ return 4096 * (i * 64 + j);
+ }
+ //TODO: handle error
+ return 0;
+ }
+
+ void map_kernel_stacks() {
+ for (uint64_t vaddr = syscall_stack_bottom;
+ vaddr < syscall_stack_top; vaddr += 4096)
+ map_kernel_page(take_pram_page(), vaddr, true, false);
+ for (uint64_t vaddr = interrupt_stack_bottom;
+ vaddr < interrupt_stack_top; vaddr += 4096)
+ map_kernel_page(take_pram_page(), vaddr, true, false);
+ }
+
+ uint64_t find_unmapped_vram_region(uint64_t page_count) {
+ uint64_t start = 0;
+ uint64_t len = 0;
+ for (uint64_t i = 0; i < kernel_vram_pages; ++i)
+ if (kernel_p1s[i] == 0) {
+ ++len;
+ if (len == page_count)
+ return start * 4096 + kernel_vram_start;
+ }
+ else {
+ start = i + 1;
+ len = 0;
+ }
+ //TODO: handle error
+ return 0;
+ }
+
+ void *map_new_kernel_pages(uint64_t count) {
+ uint64_t vaddr = find_unmapped_vram_region(count);
+ for (uint64_t i = 0; i < count; ++i)
+ map_kernel_page(take_pram_page(), vaddr + i * 4096, true, false);
+ return (void *)vaddr;
+ }
+
+ void map_new_kernel_page(uint64_t &vaddr_out, uint64_t &paddr_out) {
+ vaddr_out = find_unmapped_vram_region(1);
+ paddr_out = take_pram_page();
+ map_kernel_page(paddr_out, vaddr_out, true, false);
+ }
+
+ uint64_t get_used_vram_page_count() {
+ uint64_t count = 0;
+ for (uint64_t i = 0; i < kernel_vram_pages; ++i)
+ if (kernel_p1s[i] != 0)
+ ++count;
+ return count;
+ }
+
+ uint64_t get_free_pram_page_count() {
+ uint64_t used_count = 0;
+ for (uint64_t i = 0; i < pram_pages / 64; ++i)
+ for (uint64_t j = 0; j < 64; ++j)
+ used_count += (pram_usage_bitmap[i] >> j) & 1;
+ return pram_pages - used_count;
+ }
+
+}
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/source/storage.cpp b/kernel/source/storage.cpp
new file mode 100644
index 0000000..b6b1a04
--- /dev/null
+++ b/kernel/source/storage.cpp
@@ -0,0 +1,67 @@
+#include <hilbert/kernel/storage.hpp>
+
+namespace hilbert::kernel::storage {
+
+ bd_result block_device::load_cache_block(uint64_t i) {
+
+ if (block_cache_i == i)
+ return bd_result::success;
+
+ bd_result result = read_blocks_no_cache(i, 1, block_cache);
+
+ if (result != bd_result::success) {
+ block_cache_i = block_count;
+ return result;
+ }
+
+ block_cache_i = i;
+ return bd_result::success;
+
+ }
+
+ bd_result block_device::read_bytes(
+ uint64_t start, uint64_t count, void *into
+ ) {
+
+ if (start + count > block_size * block_count)
+ return bd_result::out_of_bounds;
+
+ uint8_t *into_u8 = (uint8_t *)into;
+
+ if (start % block_size != 0) {
+ uint64_t prefix_len = block_size - start % block_size;
+ bd_result result = load_cache_block(start / block_size);
+ if (result != bd_result::success)
+ return result;
+ for (uint64_t i = 0; i < prefix_len; ++i)
+ into_u8[i] = block_cache[start % block_size + i];
+ into_u8 += prefix_len;
+ start += prefix_len;
+ count -= prefix_len;
+ }
+
+ uint64_t postfix_start = ((start + count) / block_size) * block_size;
+
+ if (postfix_start != start) {
+ bd_result result = read_blocks_no_cache(
+ start / block_size, (postfix_start - start) / block_size, into_u8);
+ if (result != bd_result::success)
+ return result;
+ count -= postfix_start - start;
+ into_u8 += postfix_start - start;
+ start = postfix_start;
+ }
+
+ if (count != 0) {
+ bd_result result = load_cache_block(start / block_size);
+ if (result != bd_result::success)
+ return result;
+ for (uint64_t i = 0; i < count; ++i)
+ into_u8[i] = block_cache[i];
+ }
+
+ return bd_result::success;
+
+ }
+
+}
diff --git a/kernel/source/storage/bd/memory.cpp b/kernel/source/storage/bd/memory.cpp
new file mode 100644
index 0000000..d6a6719
--- /dev/null
+++ b/kernel/source/storage/bd/memory.cpp
@@ -0,0 +1,21 @@
+#include <hilbert/kernel/storage/bd/memory.hpp>
+
+namespace hilbert::kernel::storage::bd {
+
+ memory::memory(void *buffer, uint64_t buffer_len)
+ : buffer((uint8_t *)buffer)
+ {
+ block_size = 1;
+ block_count = buffer_len;
+ //block cache will never be used, since the block size is 1.
+ }
+
+ bd_result memory::read_blocks_no_cache(
+ uint64_t start, uint64_t count, void *into
+ ) {
+ for (uint64_t i = 0; i < count; ++i)
+ ((uint8_t *)into)[i] = buffer[start + i];
+ return bd_result::success;
+ }
+
+}
diff --git a/kernel/source/storage/fs/tarfs.cpp b/kernel/source/storage/fs/tarfs.cpp
new file mode 100644
index 0000000..5986f62
--- /dev/null
+++ b/kernel/source/storage/fs/tarfs.cpp
@@ -0,0 +1,238 @@
+#include <hilbert/kernel/storage/fs/tarfs.hpp>
+
+//in tarfs_instance, node_id_t and directory_iter_t refer to the number
+//of bytes into the block device that the info sector is located.
+
+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 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, node_id_t &out) {
+
+ uint64_t bytes;
+ FS_TO_FS(read_num(node + 124, 12, bytes))
+ out = node + ((bytes - 1) / 512 + 2) * 512;
+
+ uint8_t sector[512];
+ BD_TO_FS(bd->read_bytes(node, 512, sector))
+ for (unsigned i = 0; i < 512; ++i)
+ if (sector[i] != 0)
+ return fs_result::success;
+
+ return fs_result::does_not_exist;
+
+ }
+
+ fs_result tarfs_instance::read_full_name(
+ node_id_t node, utility::string &out
+ ) {
+
+ out.count = 0;
+ out.verify_buffer_len(155);
+ BD_TO_FS(bd->read_bytes(node + 345, 155, out.buffer))
+
+ while (out.count < 155 && out.buffer[out.count] != '\0')
+ ++out.count;
+
+ unsigned new_max = out.count + 100;
+ out.verify_buffer_len(new_max);
+ BD_TO_FS(bd->read_bytes(node, 100, out.buffer + out.count))
+
+ while (out.count < 255 && out.buffer[out.count] != '\0')
+ ++out.count;
+
+ return fs_result::success;
+
+ }
+
+ //len <= 12.
+ fs_result tarfs_instance::read_num(
+ uint64_t offset, unsigned len, uint64_t &out
+ ) {
+
+ char buf[12];
+ BD_TO_FS(bd->read_bytes(offset, len, buf))
+
+ out = 0;
+ for (unsigned i = 0; i < len && buf[i] != '\0'; ++i) {
+ if (buf[i] < '0' || buf[i] > '7')
+ return fs_result::fs_corrupt;
+ out = out * 8 + buf[i] - '0';
+ }
+
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::first_child_starting_at(
+ node_id_t parent, node_id_t start, node_id_t &out
+ ) {
+
+ utility::string parent_full_name;
+ FS_TO_FS(read_full_name(parent, parent_full_name))
+
+ utility::string child_full_name;
+ out = start;
+
+ while (true) {
+
+ 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)
+ ) {
+ if (child_full_name.buffer[child_full_name.count - 1] == '/')
+ --child_full_name.count;
+ for (unsigned i = parent_full_name.count;
+ i < child_full_name.count; ++i)
+ if (child_full_name.buffer[i] == '/')
+ goto next;
+ return fs_result::success;
+ }
+
+ next:
+ FS_TO_FS(next_node(out, out))
+
+ }
+
+ }
+
+ fs_result tarfs_instance::get_dir_entry(node_id_t node, dir_entry &entry) {
+
+ utility::string full_name;
+ read_full_name(node, full_name);
+
+ if (full_name.count == 2)
+ entry.name.count = 0;
+
+ else {
+
+ if (full_name.buffer[full_name.count - 1] == '/')
+ --full_name.count;
+
+ unsigned last_slash =
+ utility::find_last(full_name.buffer, full_name.count, '/');
+ entry.name.count = full_name.count - last_slash - 1;
+ entry.name.verify_buffer_len(entry.name.count);
+
+ for (unsigned i = 0; i < entry.name.count; ++i)
+ entry.name.buffer[i] = full_name.buffer[last_slash + 1 + i];
+
+ }
+
+ entry.node = node;
+
+ char ch;
+ BD_TO_FS(bd->read_bytes(node + 156, 1, &ch));
+ switch (ch) {
+ case '0':
+ entry.type = file_type::regular_file;
+ break;
+ case '2':
+ entry.type = file_type::symlink;
+ break;
+ case '5':
+ entry.type = file_type::directory;
+ break;
+ default:
+ return fs_result::fs_corrupt;
+ }
+
+ if (entry.type == file_type::regular_file) {
+ uint64_t length;
+ FS_TO_FS(read_num(node + 124, 12, length))
+ entry.length = length;
+ }
+
+ else if (entry.type == file_type::symlink) {
+ utility::string target;
+ target.verify_buffer_len(100);
+ BD_TO_FS(bd->read_bytes(node + 157, 100, target.buffer))
+ while (target.count < 100 && target.buffer[target.count] != '\0')
+ ++target.count;
+ entry.target = utility::move(target);
+ }
+
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::get_root_node(node_id_t &out) {
+
+ utility::string full_name;
+ node_id_t on = 0;
+
+ while (true) {
+
+ FS_TO_FS(read_full_name(on, full_name))
+ if (full_name.count == 2) {
+ out = on;
+ return fs_result::success;
+ }
+ fs_result result = next_node(on, on);
+ if (result == fs_result::does_not_exist)
+ return fs_result::fs_corrupt;
+ FS_TO_FS(result)
+
+ }
+
+ }
+
+ fs_result tarfs_instance::get_first_child(
+ node_id_t node, dir_entry &out, directory_iter_t &iter_out
+ ) {
+
+ node_id_t child;
+ FS_TO_FS(first_child_starting_at(node, 0, child))
+
+ dir_entry entry;
+ 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, dir_entry &out, directory_iter_t &iter
+ ) {
+
+ node_id_t start;
+ FS_TO_FS(next_node((node_id_t)iter, start))
+
+ 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 = utility::move(entry);
+ iter = (directory_iter_t)child;
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::read_bytes_from_file(
+ node_id_t node, uint64_t start, uint64_t count, void *into
+ ) {
+ BD_TO_FS(bd->read_bytes(node + 512 + start, count, into))
+ return fs_result::success;
+ }
+
+
+}
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/source/utility.cpp b/kernel/source/utility.cpp
new file mode 100644
index 0000000..12e88fd
--- /dev/null
+++ b/kernel/source/utility.cpp
@@ -0,0 +1,51 @@
+#include <hilbert/kernel/utility.hpp>
+
+void *operator new(size_t, void *ptr) {
+ return ptr;
+}
+
+void operator delete(void *, void *) {}
+
+namespace hilbert::kernel::utility {
+
+ void mark_bitmap_region_zero(
+ uint64_t *bitmap, uint64_t start_i, uint64_t end_i) {
+
+ if (start_i % 64 != 0) {
+ uint64_t keep = (1 << (start_i % 64)) - 1;
+ bitmap[start_i / 64] &= keep;
+ start_i = (start_i / 64 + 1) * 64;
+ }
+
+ if (end_i % 64 != 0) {
+ uint64_t replace = (1 << (end_i % 64)) - 1;
+ bitmap[end_i / 64] &= ~replace;
+ end_i = (end_i / 64) * 64;
+ }
+
+ for (uint64_t i = start_i / 64; i < end_i / 64; ++i)
+ bitmap[i] = 0;
+
+ }
+
+ void mark_bitmap_region_one(
+ uint64_t *bitmap, uint64_t start_i, uint64_t end_i) {
+
+ if (start_i % 64 != 0) {
+ uint64_t keep = (1 << (start_i % 64)) - 1;
+ bitmap[start_i / 64] |= ~keep;
+ start_i = (start_i / 64 + 1) * 64;
+ }
+
+ if (end_i % 64 != 0) {
+ uint64_t replace = (1 << (end_i % 64)) - 1;
+ bitmap[end_i / 64] |= replace;
+ end_i = (end_i / 64) * 64;
+ }
+
+ for (uint64_t i = start_i / 64; i < end_i / 64; ++i)
+ bitmap[i] = 0xffffffffffffffff;
+
+ }
+
+}
diff --git a/kernel/source/vfile.cpp b/kernel/source/vfile.cpp
new file mode 100644
index 0000000..89c95e6
--- /dev/null
+++ b/kernel/source/vfile.cpp
@@ -0,0 +1,206 @@
+#include <hilbert/kernel/vfile.hpp>
+
+//TODO: handle symlink loops nicely in vfile::get_child,
+// vfile::get_children, and lookup_path.
+
+namespace hilbert::kernel::vfile {
+
+ void canon_path::parent() {
+ if (segments.count != 0)
+ --segments.count;
+ else if (!absolute)
+ ++parent_count;
+ }
+
+ void canon_path::rel(const canon_path &r) {
+ if (r.absolute) {
+ segments.count = 0;
+ absolute = true;
+ parent_count = 0;
+ }
+ for (unsigned i = 0; i < r.parent_count; ++i)
+ parent();
+ for (unsigned i = 0; i < r.segments.count; ++i)
+ segments.add_end(r.segments.buffer[i]);
+ }
+
+ void canonize_path(const utility::string &name, canon_path &out) {
+
+ out.absolute = false;
+ out.parent_count = 0;
+ out.segments.count = 0;
+
+ const char *str = name.buffer;
+ unsigned len = name.count;
+
+ if (len == 0)
+ return;
+
+ if (len == 1 && str[0] == '/') {
+ out.absolute = true;
+ return;
+ }
+
+ if (str[0] == '/') {
+ out.absolute = true;
+ ++str;
+ --len;
+ }
+
+ while (len != 0) {
+
+ unsigned segment_len = utility::find(str, len, '/');
+ unsigned to_skip = segment_len == len ? segment_len : segment_len + 1;
+
+ if (segment_len == 0)
+ ;
+
+ else if (segment_len == 1 && str[0] == '.')
+ ;
+
+ else if (segment_len == 2 && str[0] == '.' && str[1] == '.')
+ out.parent();
+
+ else {
+ utility::string segment(str, segment_len);
+ out.segments.add_end(utility::move(segment));
+ }
+
+ str += to_skip;
+ len -= to_skip;
+
+ }
+
+ }
+
+#define RET_NOT_SUC(expr) \
+ { \
+ storage::fs_result _result = expr; \
+ if (_result != storage::fs_result::success) \
+ return _result; \
+ }
+
+ storage::fs_result vfile::follow_symlinks(vfile &out) const {
+
+ if (dir_entry.type != storage::file_type::symlink) {
+ out = *this;
+ return storage::fs_result::success;
+ }
+
+ canon_path target_path;
+ canonize_path(dir_entry.target, target_path);
+ canon_path full_path = path;
+ full_path.parent();
+ full_path.rel(target_path);
+
+ vfile next;
+ RET_NOT_SUC(lookup_path(full_path, next, false))
+
+ next.path = path;
+ return next.follow_symlinks(out);
+
+ }
+
+ storage::fs_result vfile::get_child(
+ vfile &out, const utility::string &name
+ ) const {
+
+ storage::dir_entry entry;
+ storage::directory_iter_t iter;
+
+ RET_NOT_SUC(bd->mounted_as->get_first_child(dir_entry.node, entry, iter))
+
+ while (true) {
+
+ if (entry.name == name) {
+
+ vfile vf;
+ vf.bd = bd;
+ vf.dir_entry = utility::move(entry);
+ vf.path = path;
+ vf.path.segments.add_end(name);
+ out = utility::move(vf);
+ return storage::fs_result::success;
+
+ }
+
+ RET_NOT_SUC(bd->mounted_as->get_next_child(dir_entry.node, entry, iter))
+
+ }
+
+ }
+
+ storage::fs_result vfile::get_children(utility::vector<vfile> &out) const {
+
+ storage::dir_entry entry;
+ storage::directory_iter_t 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 (true) {
+
+ vfile vf;
+ vf.bd = bd;
+ vf.path = path;
+ vf.path.segments.add_end(entry.name);
+ vf.dir_entry = utility::move(entry);
+ out.add_end(utility::move(vf));
+
+ 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;
+
+ }
+
+ }
+
+ storage::fs_result vfile::read_file(
+ uint64_t start, uint64_t length, void *into
+ ) const {
+ return bd->mounted_as->read_bytes_from_file(
+ dir_entry.node, start, length, into);
+ }
+
+ //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, bool follow_final_symlink
+ ) {
+
+ //assume path is absolute.
+
+ out = *root;
+ for (unsigned i = 0; i < path.segments.count; ++i) {
+
+ 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 = utility::move(result);
+
+ }
+
+ if (follow_final_symlink) {
+ vfile result;
+ RET_NOT_SUC(out.follow_symlinks(result))
+ out = utility::move(result);
+ }
+
+ return storage::fs_result::success;
+
+ }
+
+}