diff options
author | Benji Dial <benji@benjidial.net> | 2024-05-18 21:53:38 -0400 |
---|---|---|
committer | Benji Dial <benji@benjidial.net> | 2024-05-18 21:53:38 -0400 |
commit | b1a912a8a6ff472a49b2e0a09cfd433adfc2cb24 (patch) | |
tree | 5009d4415ba13e4baa37f3d0271852528130fd3b /kernel/source | |
parent | a8a80d326de9550b2a25b1255a2093ab43219ede (diff) | |
download | hilbert-os-b1a912a8a6ff472a49b2e0a09cfd433adfc2cb24.tar.gz |
reorganization, cross compiler
Diffstat (limited to 'kernel/source')
-rw-r--r-- | kernel/source/allocator.cpp | 160 | ||||
-rw-r--r-- | kernel/source/application.asm | 171 | ||||
-rw-r--r-- | kernel/source/application.cpp | 550 | ||||
-rw-r--r-- | kernel/source/entry.cpp | 222 | ||||
-rw-r--r-- | kernel/source/framebuffer.cpp | 40 | ||||
-rw-r--r-- | kernel/source/input.cpp | 22 | ||||
-rw-r--r-- | kernel/source/interrupts.asm | 340 | ||||
-rw-r--r-- | kernel/source/interrupts.cpp | 152 | ||||
-rw-r--r-- | kernel/source/paging.asm | 16 | ||||
-rw-r--r-- | kernel/source/paging.cpp | 145 | ||||
-rw-r--r-- | kernel/source/panic.cpp | 11 | ||||
-rw-r--r-- | kernel/source/storage.cpp | 67 | ||||
-rw-r--r-- | kernel/source/storage/bd/memory.cpp | 21 | ||||
-rw-r--r-- | kernel/source/storage/fs/tarfs.cpp | 238 | ||||
-rw-r--r-- | kernel/source/syscall.cpp | 538 | ||||
-rw-r--r-- | kernel/source/utility.cpp | 51 | ||||
-rw-r--r-- | kernel/source/vfile.cpp | 206 |
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; + + } + +} |