From 03b3ddac73b95094181fd3211f74eb7446cdb073 Mon Sep 17 00:00:00 2001 From: Freywar Ulvnaudgari Date: Wed, 17 Jun 2026 21:20:14 +0300 Subject: [PATCH] Add subprocesses and convert terminal to an app --- .gdbinit | 4 +- build.sh | 3 +- meson.build | 91 ++++++++--- src/kernel/app/cat.c | 30 ---- src/kernel/app/cat.h | 5 - src/kernel/app/ls.c | 34 ----- src/kernel/app/ls.h | 5 - src/kernel/app/terminal.h | 5 - src/kernel/kernel.c | 43 ++++-- src/kernel/keyboard.c | 7 +- src/kernel/memory.c | 2 +- src/kernel/path.c | 43 +++++- src/kernel/path.h | 6 +- src/kernel/process.asm | 50 ++++++ src/kernel/process.c | 53 +++---- src/kernel/process.h | 20 +-- src/kernel/stream.h | 4 +- src/kernel/syscall.asm | 18 ++- src/kernel/syscall.c | 143 +++++++++++++++--- src/kernel/syscall.h | 6 +- src/kernel/vga.c | 8 +- src/lib/layout.h | 2 +- src/user/app/echo/echo.c | 13 ++ src/user/app/hello/hello.c | 6 - .../app => user/app/terminal}/terminal.c | 96 ++++++------ src/user/start.asm | 2 + src/user/syscall.asm | 24 +++ src/user/syscall.h | 10 +- 28 files changed, 488 insertions(+), 245 deletions(-) delete mode 100644 src/kernel/app/cat.c delete mode 100644 src/kernel/app/cat.h delete mode 100644 src/kernel/app/ls.c delete mode 100644 src/kernel/app/ls.h delete mode 100644 src/kernel/app/terminal.h create mode 100644 src/kernel/process.asm create mode 100644 src/user/app/echo/echo.c delete mode 100644 src/user/app/hello/hello.c rename src/{kernel/app => user/app/terminal}/terminal.c (67%) diff --git a/.gdbinit b/.gdbinit index 0097237..bd66374 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,4 +1,6 @@ target remote localhost:1234 set architecture i386:x86-64 set disassembly-flavor intel -display/i ($cs * 16 + $rip) +display/i $rip + +break *0x0000000000400000 diff --git a/build.sh b/build.sh index f855533..8103dce 100755 --- a/build.sh +++ b/build.sh @@ -15,4 +15,5 @@ dd if=build/bootloader.bin of=build/os.img bs="${sector_size}" seek=1 count="$(( mkfs.fat -F 16 --offset "${partition_offset}" build/os.img mcopy -i build/os.img@@"$((partition_offset * sector_size))" src ::src mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin -mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/hello.bin ::hello.bin +mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::terminal +mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::echo diff --git a/meson.build b/meson.build index a70c0de..ec59149 100644 --- a/meson.build +++ b/meson.build @@ -38,7 +38,15 @@ kernel_entry_o = custom_target( command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'], ) -syscall_o = custom_target('syscall', +process_o = custom_target( + 'process', + input: 'src/kernel/process.asm', + output: 'process.o', + command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'], +) + +syscall_o = custom_target( + 'syscall', input: 'src/kernel/syscall.asm', output: 'syscall.o', command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'], @@ -46,10 +54,12 @@ syscall_o = custom_target('syscall', cc = meson.get_compiler('c') -kernel_sources = files( - 'src/kernel/app/cat.c', - 'src/kernel/app/ls.c', - 'src/kernel/app/terminal.c', +lib_sources = files([ + 'src/lib/memory.c', + 'src/lib/string.c', +]) + +kernel_sources = files([ 'src/kernel/ata.c', 'src/kernel/fat16.c', 'src/kernel/fs.c', @@ -65,13 +75,11 @@ kernel_sources = files( 'src/kernel/syscall.c', 'src/kernel/tss.c', 'src/kernel/vga.c', - 'src/lib/memory.c', - 'src/lib/string.c', -) +]) kernel_elf = executable( 'kernel.elf', - sources: [kernel_entry_o, syscall_o, kernel_sources], + sources: [kernel_entry_o, process_o, syscall_o, lib_sources, kernel_sources], c_args: [ '-ffreestanding', '-nostdlib', @@ -99,28 +107,69 @@ custom_target( build_by_default: true, ) -hello_start = custom_target( - 'hello_start', +terminal_start = custom_target( + 'terminal_start', + input: 'src/user/start.asm', + output: 'terminal_start.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +terminal_syscall = custom_target( + 'terminal_syscall', + input: 'src/user/syscall.asm', + output: 'terminal_syscall.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +terminal_elf = executable( + 'terminal.elf', + sources: [terminal_start, terminal_syscall, lib_sources, 'src/user/app/terminal/terminal.c'], + c_args: [ + '-ffreestanding', + '-nostdlib', + '-fno-stack-protector', + '-mcmodel=large', + '-DVERSION="' + meson.project_version() + '"', + ], + link_args: [ + '-T', meson.project_source_root() / 'src/user/linker.ld', + '-nostdlib', + ], + link_depends: 'src/user/linker.ld', +) + +custom_target( + 'terminal', + input: terminal_elf, + output: 'terminal', + command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], + build_by_default: true, +) + + +echo_start = custom_target( + 'echo_start', input: 'src/user/start.asm', - output: 'hello_start.o', + output: 'echo_start.o', command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], ) -hello_syscall = custom_target( - 'hello_syscall', +echo_syscall = custom_target( + 'echo_syscall', input: 'src/user/syscall.asm', - output: 'hello_syscall.o', + output: 'echo_syscall.o', command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], ) -hello_elf = executable( - 'hello.elf', - sources: [hello_start, hello_syscall, 'src/user/app/hello/hello.c'], +echo_elf = executable( + 'echo.elf', + sources: [echo_start, echo_syscall, lib_sources, 'src/user/app/echo/echo.c'], c_args: [ '-ffreestanding', '-nostdlib', '-fno-stack-protector', '-mcmodel=large', + '-DVERSION="' + meson.project_version() + '"', ], link_args: [ '-T', meson.project_source_root() / 'src/user/linker.ld', @@ -130,9 +179,9 @@ hello_elf = executable( ) custom_target( - 'hello_bin', - input: hello_elf, - output: 'hello.bin', + 'echo', + input: echo_elf, + output: 'echo', command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], build_by_default: true, ) diff --git a/src/kernel/app/cat.c b/src/kernel/app/cat.c deleted file mode 100644 index ee7ffaa..0000000 --- a/src/kernel/app/cat.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "src/kernel/app/cat.h" -#include "src/kernel/fs.h" -#include "src/kernel/path.h" -#include "src/lib/memory.h" - -void cat(process_t *proc, uint8_t argc, char **argv) { - if (argc != 2) { - WRITE_S("cat: requires a single path\n"); - return; - } - - path_t *path = path_open(proc->root, proc->cwd, argv[1]); - if (!path) { - WRITE_S("cat: invalid path\n"); - return; - } - - fs_node_t *curr = path->depth ? path->stack[path->depth - 1] : proc->root; - if (curr->is_dir) { - WRITE_S("cat: can not print a directory\n"); - path_close(path); - return; - } - - char *content = memory_allocate(curr->size + 1); - fs_read(curr, 0, curr->size, content); - proc->stdout->write(proc->stdout, content, curr->size); - memory_free(content); - path_close(path); -} diff --git a/src/kernel/app/cat.h b/src/kernel/app/cat.h deleted file mode 100644 index aa044da..0000000 --- a/src/kernel/app/cat.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "src/kernel/process.h" - -void cat(process_t *proc, uint8_t argc, char **argv); diff --git a/src/kernel/app/ls.c b/src/kernel/app/ls.c deleted file mode 100644 index c6c0a3f..0000000 --- a/src/kernel/app/ls.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "src/kernel/app/ls.h" -#include "src/kernel/fs.h" -#include "src/kernel/path.h" -#include - -void ls(process_t *proc, uint8_t argc, char **argv) { - if (argc > 2) { - WRITE_S("ls: requires a single path\n"); - return; - } - - path_t *path = path_open(proc->root, proc->cwd, argc == 1 ? "." : argv[1]); - if (!path) { - WRITE_S("ls: invalid path\n"); - return; - } - - fs_node_t *curr = path->depth ? path->stack[path->depth - 1] : proc->root; - if (!curr->is_dir) { - WRITE_S("ls: can not list a file\n"); - path_close(path); - return; - } - - uint8_t j = 0; - fs_node_t *item = fs_open_at(curr, j++); - while (item) { - WRITE_D(item->name); - WRITE_S("\n"); - fs_close(item); - item = fs_open_at(curr, j++); - } - path_close(path); -} diff --git a/src/kernel/app/ls.h b/src/kernel/app/ls.h deleted file mode 100644 index 913b859..0000000 --- a/src/kernel/app/ls.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "src/kernel/process.h" - -void ls(process_t *proc, uint8_t argc, char **argv); diff --git a/src/kernel/app/terminal.h b/src/kernel/app/terminal.h deleted file mode 100644 index 20019a6..0000000 --- a/src/kernel/app/terminal.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "src/kernel/process.h" - -void terminal(process_t *proc, uint8_t argc, char **argv); diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 41b3b16..c0eb497 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -7,8 +7,10 @@ #include "src/kernel/pic.h" #include "src/kernel/process.h" #include "src/kernel/syscall.h" +#include "src/kernel/tss.h" #include "src/kernel/util.h" #include "src/kernel/vga.h" +#include "src/lib/layout.h" #include "src/lib/memory.h" __attribute__((interrupt)) void isr_divide_by_zero(__attribute__((unused)) struct interrupt_frame *frame) { @@ -47,30 +49,39 @@ void kernel_main() { gdt_init(); syscall_init(); - process_t *kp = memory_allocate(sizeof(process_t)); - kp->root = fs_mount(); - kp->cwd = memory_allocate(sizeof(path_t)); - kp->stdin = keyboard_init(); - kp->stdout = vga_init(); + process_t *kernel = memory_allocate(sizeof(process_t)); + kernel->pml4 = (void *)KERNEL_VIRTUAL_PML4; + kernel->root = fs_mount(); + kernel->cwd = memory_allocate(sizeof(path_t)); + kernel->stdin = keyboard_init(); + kernel->stdout = vga_init(); - fs_node_t *hello = fs_open_by(kp->root, "HELLO.BIN"); + fs_node_t *bin = fs_open_by(kernel->root, "terminal"); - ASSERT(hello, "kernel: hello.bin not found"); + ASSERT(bin, "kernel: terminal not found"); - uint8_t *hello_code = memory_allocate(hello->size); - fs_read(hello, 0, hello->size, hello_code); + uint8_t *code = memory_allocate(bin->size); + fs_read(bin, 0, bin->size, code); - process_t *proc = process_create(kp, hello_code, hello->size); + process_t *terminal = process_create(kernel, code, bin->size); - memory_free(hello_code); - fs_close(hello); + memory_free(code); + fs_close(bin); - process_run(proc); + __asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory"); - while (1) - ; + uint8_t *stack = (uint8_t *)terminal->user_stack; + + stack -= sizeof(uint64_t); + *(uint64_t *)stack = 0; + + terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)stack)); + + current_process = terminal; + tss.rsp0 = (uint64_t)current_process->kernel_stack; + process_switch_to(&kernel->kernel_rsp, terminal->kernel_rsp, VIRT_TO_PHYS(terminal->pml4)); - fs_unmount(kp->root); + fs_unmount((fs_node_t *)kernel->root); outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware __asm__ volatile("cli; hlt"); diff --git a/src/kernel/keyboard.c b/src/kernel/keyboard.c index 60e807d..e0aa7bc 100644 --- a/src/kernel/keyboard.c +++ b/src/kernel/keyboard.c @@ -50,11 +50,12 @@ static void append_sequence(const char *s) { } } -static void stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, - __attribute__((unused)) uint64_t size) { +static uint64_t stream_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, + __attribute__((unused)) uint64_t bytes) { + return 0; } -static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) { +static uint64_t stream_read(__attribute__((unused)) const stream_t *self, uint64_t max, char *to) { if (max == 0 || buffer_length == 0) { return 0; } diff --git a/src/kernel/memory.c b/src/kernel/memory.c index 28e2e87..ec67321 100644 --- a/src/kernel/memory.c +++ b/src/kernel/memory.c @@ -25,7 +25,7 @@ static uint64_t allocation[PAGE_COUNT / MAP_UNIT] = { }; static uint8_t get_allocated(uint16_t page) { - return !!allocation[page / MAP_UNIT] & ((uint64_t)1 << (page % MAP_UNIT)); + return !!(allocation[page / MAP_UNIT] & ((uint64_t)1 << (page % MAP_UNIT))); } static void set_allocated(uint16_t page, uint8_t allocated) { diff --git a/src/kernel/path.c b/src/kernel/path.c index 41ccfd5..93a29aa 100644 --- a/src/kernel/path.c +++ b/src/kernel/path.c @@ -4,11 +4,15 @@ #include "src/lib/string.h" #include "src/lib/util.h" -path_t *path_open(const fs_node_t *root, const path_t *source, char *path) { +path_t *path_open(const fs_node_t *root, const path_t *source, const char *path) { + uint64_t length = string_length(path); + char *path_own = memory_allocate(length + 1); + memory_copy(path, length + 1, path_own); + path_t *result = memory_allocate(sizeof(path_t)); char *path_components[PATH_DEPTH]; - uint8_t path_length = (uint8_t)string_split(path, '/', PATH_DEPTH, path_components); + uint8_t path_length = (uint8_t)string_split(path_own, '/', PATH_DEPTH, path_components); if (!string_empty(path_components[0])) { for (uint8_t i = 0; i < source->depth; i++) { result->stack[result->depth++] = fs_open_again(source->stack[i]); @@ -27,6 +31,7 @@ path_t *path_open(const fs_node_t *root, const path_t *source, char *path) { fs_node_t *next = prev->is_dir ? fs_open_by(prev, path_components[i]) : NUL; if (!next || result->depth >= PATH_DEPTH) { path_close(result); + memory_free(path_own); return NUL; } else { result->stack[result->depth++] = next; @@ -34,9 +39,43 @@ path_t *path_open(const fs_node_t *root, const path_t *source, char *path) { } } + memory_free(path_own); + return result; } +fs_node_t *path_open_file(const fs_node_t *root, const path_t *source, const char *path) { + path_t *p = path_open(root, source, path); + if (!p) { + return NUL; + } + if (!p->depth || p->stack[p->depth - 1]->is_dir) { + path_close(p); + return NUL; + } + fs_node_t *n = fs_open_again(p->stack[p->depth - 1]); + path_close(p); + return n; +} + +fs_node_t *path_open_directory(const fs_node_t *root, const path_t *source, const char *path) { + path_t *p = path_open(root, source, path); + if (!p) { + return NUL; + } + if (!p->depth) { + path_close(p); + return (fs_node_t *)root; + } + if (!p->stack[p->depth - 1]->is_dir) { + path_close(p); + return NUL; + } + fs_node_t *n = fs_open_again(p->stack[p->depth - 1]); + path_close(p); + return n; +} + void path_close(path_t *path) { for (uint64_t i = 0; i < path->depth; i++) { fs_close(path->stack[i]); diff --git a/src/kernel/path.h b/src/kernel/path.h index 9d09df6..35023fa 100644 --- a/src/kernel/path.h +++ b/src/kernel/path.h @@ -10,6 +10,10 @@ typedef struct { uint8_t depth; } path_t; -path_t *path_open(const fs_node_t *root, const path_t *source, char *path); +path_t *path_open(const fs_node_t *root, const path_t *source, const char *path); + +fs_node_t *path_open_file(const fs_node_t *root, const path_t *source, const char *path); + +fs_node_t *path_open_directory(const fs_node_t *root, const path_t *source, const char *path); void path_close(path_t *path); diff --git a/src/kernel/process.asm b/src/kernel/process.asm new file mode 100644 index 0000000..f0644da --- /dev/null +++ b/src/kernel/process.asm @@ -0,0 +1,50 @@ +bits 64 + +global process_trampoline +process_trampoline: + extern current_process + mov rax, [current_process] + mov rdx, [rax + 40] ; current_process->user_rsp + + ; Keep in sync with `gdt.h` and `layout.h` + mov rax, 0x0000000000400000 + mov rbx, 0x2B + mov rcx, 0x202 + mov rsi, 0x23 + + push rsi + push rdx + push rcx + push rbx + push rax + iretq + +global process_switch_to +; process_switch_to(uint64_t *save_rsp, uint64_t new_rsp, uint64_t new_pml4_phys) +process_switch_to: + ; save callee-saved registers of the OUTGOING process + push rbp + push rbx + push r12 + push r13 + push r14 + push r15 + + ; save current rsp into *save_rsp (rdi) + mov [rdi], rsp + + ; switch address space + mov cr3, rdx ; rdx = new_pml4_phys (3rd arg) + + ; switch to incoming process's stack + mov rsp, rsi ; rsi = new_rsp (2nd arg) + + ; restore callee-saved registers of the INCOMING process + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + pop rbp + + ret diff --git a/src/kernel/process.c b/src/kernel/process.c index bf9b923..9b05f68 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -1,9 +1,7 @@ #include "src/kernel/process.h" -#include "src/kernel/gdt.h" #include "src/kernel/memory.h" #include "src/kernel/panic.h" #include "src/kernel/path.h" -#include "src/kernel/tss.h" #include "src/lib/layout.h" #include "src/lib/memory.h" @@ -14,18 +12,33 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t process_t *proc = memory_allocate(sizeof(process_t)); + proc->parent = parent; + proc->kernel_stack = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; + uint64_t *kstackv = (uint64_t *)((uint8_t *)proc->kernel_stack); + *(--kstackv) = (uint64_t)process_trampoline; // "return address" for switch_to's ret + *(--kstackv) = 0; // r15 + *(--kstackv) = 0; // r14 + *(--kstackv) = 0; // r13 + *(--kstackv) = 0; // r12 + *(--kstackv) = 0; // rbx + *(--kstackv) = 0; // rbp + proc->kernel_rsp = kstackv; + proc->pml4 = PHYS_TO_VIRT(memory_page_allocate()); memory_set(0, PAGE_SIZE / 2, proc->pml4); memory_copy((uint64_t *)KERNEL_VIRTUAL_PML4 + 256, PAGE_SIZE / 2, proc->pml4 + 256); - memory_page_map(proc->pml4, (void *)USER_VIRTUAL_STACK, memory_page_allocate(), PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER); + void *ustackp = memory_page_allocate(); + proc->user_stack = PHYS_TO_VIRT((uint64_t)ustackp + PAGE_SIZE); + memory_page_map(proc->pml4, (void *)USER_VIRTUAL_STACK, ustackp, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER); for (uint64_t page = 0; size > 0; page++) { void *p = memory_page_allocate(); uint64_t s = size < PAGE_SIZE ? size : PAGE_SIZE; - memory_page_map(proc->pml4, (void *)(USER_VIRTUAL_CODE + page * PAGE_SIZE), p, PAGE_PRESENT | PAGE_USER); + memory_page_map(proc->pml4, (void *)(USER_VIRTUAL_CODE + page * PAGE_SIZE), p, + PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER); // TODO Restrict writeability of code. memory_copy(code, s, PHYS_TO_VIRT(p)); code += s; size -= s; @@ -36,12 +49,13 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER); } - proc->cwd = memory_allocate(sizeof(path_t)); - proc->cwd->depth = parent->cwd->depth; + path_t *cwd = memory_allocate(sizeof(path_t)); + cwd->depth = parent->cwd->depth; for (uint8_t i = 0; i < parent->cwd->depth; i++) { - proc->cwd->stack[i] = fs_open_again(parent->cwd->stack[i]); + cwd->stack[i] = fs_open_again(parent->cwd->stack[i]); } + proc->cwd = cwd; proc->root = parent->root; proc->stdin = parent->stdin; proc->stdout = parent->stdout; @@ -49,26 +63,8 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t return proc; } -void process_run(process_t *proc) { - current_process = proc; - tss.rsp0 = (uint64_t)proc->kernel_stack; - - __asm__ volatile("mov %0, %%cr3" : : "r"(VIRT_TO_PHYS(proc->pml4)) : "memory"); - - __asm__ volatile("push %0\n" // ss - "push %1\n" // rsp - "push %2\n" // rflags - "push %3\n" // cs - "push %4\n" // rip - "iretq" - : - : "r"((uint64_t)USER_DS), "r"(USER_VIRTUAL_STACK + PAGE_SIZE), "r"((uint64_t)USER_RFLAGS), "r"((uint64_t)USER_CS), - "r"(USER_VIRTUAL_CODE) - : "memory"); -} - void process_destroy(process_t *proc) { - path_close(proc->cwd); + path_close((path_t *)proc->cwd); for (uint16_t pml4i = 0; pml4i < 256; pml4i++) { if (!(proc->pml4[pml4i] & PAGE_PRESENT)) { @@ -97,6 +93,7 @@ void process_destroy(process_t *proc) { if (!(pt[pti] & PAGE_PRESENT)) { continue; } + memory_page_free((void *)(pt[pti] & ~(uint64_t)0xFFF)); } memory_page_free((void *)(pd[pdi] & ~(uint64_t)0xFFF)); @@ -106,9 +103,9 @@ void process_destroy(process_t *proc) { memory_page_free((void *)(proc->pml4[pml4i] & ~(uint64_t)0xFFF)); } - memory_page_free((void *)VIRT_TO_PHYS(proc->pml4)); + memory_page_free(VIRT_TO_PHYS(proc->pml4)); - memory_free(proc->kernel_stack); + memory_free(proc->kernel_stack - PAGE_SIZE); memory_free(proc); } diff --git a/src/kernel/process.h b/src/kernel/process.h index 53b230a..78468bd 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -3,28 +3,30 @@ #include "src/kernel/fs.h" #include "src/kernel/path.h" #include "src/kernel/stream.h" -#include "src/lib/string.h" #define USER_RFLAGS 0x202 typedef struct process { + const struct process *parent; uint64_t *pml4; void *kernel_stack; - fs_node_t *root; - path_t *cwd; - stream_t *stdin; - stream_t *stdout; + void *kernel_rsp; + void *user_stack; + void *user_rsp; + const fs_node_t *root; + const path_t *cwd; + const stream_t *stdin; + const stream_t *stdout; } process_t; typedef void (*app_t)(process_t *proc, uint8_t argc, char **argv); extern process_t *current_process; +extern void process_trampoline(); + process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size); -void process_run(process_t *proc); +extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys); void process_destroy(process_t *proc); - -#define WRITE_S(s) proc->stdout->write(proc->stdout, s, sizeof(s) - 1); -#define WRITE_D(s) proc->stdout->write(proc->stdout, s, string_length(s)); diff --git a/src/kernel/stream.h b/src/kernel/stream.h index a2967c7..7efd353 100644 --- a/src/kernel/stream.h +++ b/src/kernel/stream.h @@ -27,8 +27,8 @@ typedef struct stream stream_t; -typedef void (*stream_write_t)(stream_t *self, const char *from, uint64_t size); -typedef uint64_t (*stream_read_t)(stream_t *self, uint64_t max, char *to); +typedef uint64_t (*stream_write_t)(const stream_t *self, const char *from, uint64_t bytes); +typedef uint64_t (*stream_read_t)(const stream_t *self, uint64_t max, char *to); typedef struct stream { stream_write_t write; diff --git a/src/kernel/syscall.asm b/src/kernel/syscall.asm index 4a54ab9..df629b1 100644 --- a/src/kernel/syscall.asm +++ b/src/kernel/syscall.asm @@ -2,13 +2,23 @@ bits 64 extern tss extern syscall_dispatch +extern current_process global syscall_entry syscall_entry: - mov [user_rsp_tmp], rsp + mov [rel user_rsp_tmp], rsp + mov rsp, [tss + 4] + push rax + push rcx + mov rax, [rel current_process] + mov rcx, [rel user_rsp_tmp] + mov [rax + 40], rcx ; current_process->user_rsp + pop rcx + pop rax + push rcx push r11 push rbp @@ -33,6 +43,12 @@ syscall_entry: pop r11 pop rcx + push rax + mov rax, [rel current_process] + mov rax, [rax + 40] ; current_process->user_rsp + mov [rel user_rsp_tmp], rax + pop rax + mov rsp, [rel user_rsp_tmp] o64 sysret diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 8db6521..1057e03 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1,8 +1,12 @@ #include "src/kernel/syscall.h" +#include "src/kernel/fs.h" #include "src/kernel/gdt.h" +#include "src/kernel/path.h" #include "src/kernel/process.h" +#include "src/kernel/tss.h" #include "src/lib/layout.h" -#include "src/lib/util.h" +#include "src/lib/memory.h" +#include "src/lib/string.h" #define MSR_EFER 0xC0000080 #define MSR_STAR 0xC0000081 @@ -28,34 +32,139 @@ void syscall_init() { wrmsr(MSR_FMASK, 0x200); } -static void write(uint64_t fd, uint64_t data, uint64_t bytes) { +static uint64_t read(uint64_t fd, uint64_t max, char *to) { (void)fd; - current_process->stdout->write(current_process->stdout, (const char *)data, bytes); + return current_process->stdin->read(current_process->stdin, max, to); } -static void exit() { - __asm__ volatile("mov %0, %%cr3" : : "r"(KERNEL_VIRTUAL_PML4) : "memory"); +static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) { + (void)fd; + return current_process->stdout->write(current_process->stdout, from, bytes); +} + +static uint64_t getcwd(uint64_t max, char *to) { + if (max == 0) { + return 0; + } + + if (max == 1) { + to[0] = '\0'; + return 0; + } + + if (max == 2 || !current_process->cwd->depth) { + to[0] = '/'; + to[1] = '\0'; + return 1; + } + + uint64_t i = 0; + for (uint8_t j = 0; j < current_process->cwd->depth; j++) { + uint64_t l = string_length(current_process->cwd->stack[j]->name); + if (i + l + 1 > max) { + break; + } + to[i++] = '/'; + memory_copy(current_process->cwd->stack[j]->name, l, to + i); + i += l; + } + + to[i] = '\0'; + return i; +} + +static uint64_t chdir(const char *path) { + path_t *cwd = path_open(current_process->root, current_process->cwd, path); + if (!cwd) { + return (uint64_t)-1; + } + if (!cwd->depth || !cwd->stack[cwd->depth - 1]->is_dir) { + path_close(cwd); + return (uint64_t)-1; + } + + path_close((path_t *)current_process->cwd); + current_process->cwd = cwd; + return current_process->cwd->depth; +} - process_destroy(current_process); - current_process = NUL; +static uint64_t spawn(const char *path, uint64_t argc, const char **argv) { + fs_node_t *node = path_open_file(current_process->root, current_process->cwd, path); + if (!node) { + return (uint64_t)-1; + } + uint8_t *code = memory_allocate(node->size); + fs_read(node, 0, node->size, code); + + uint64_t size = 0; + uint64_t sizes[16]; + uint64_t offsets[16]; + for (uint64_t i = 0; i < argc; i++) { + offsets[i] = size; + size += (sizes[i] = string_length(argv[i]) + 1); + } + char *blob = memory_allocate(size); + for (uint64_t i = 0; i < argc; i++) { + memory_copy(argv[i], sizes[i], blob + offsets[i]); + } + + process_t *child = process_create(current_process, code, node->size); + + memory_free(code); + fs_close(node); + + __asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory"); + + uint8_t *stack = (uint8_t *)child->user_stack; + + stack -= size; + memory_copy(blob, size, stack); + memory_free(blob); - while (1) - ; + stack = (uint8_t *)((uint64_t)stack & ~7ULL); - // shutdown for now - // outw(0x604, 0x2000); - // __asm__ volatile("cli; hlt"); + stack -= argc * sizeof(char *); + for (uint64_t i = 0; i < argc; i++) { + ((char **)stack)[i] = (char *)(USER_VIRTUAL_STACK_TOP - size + offsets[i]); + } + stack -= sizeof(uint64_t); + *(uint64_t *)stack = argc; + + child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack)); + + process_t *parent = current_process; + current_process = child; + tss.rsp0 = (uint64_t)child->kernel_stack; + + process_switch_to(&parent->kernel_rsp, child->kernel_rsp, VIRT_TO_PHYS(child->pml4)); + + tss.rsp0 = (uint64_t)parent->kernel_stack; + current_process = parent; + process_destroy(child); + + return 0; +} + +static void exit() { + process_switch_to(¤t_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4)); } -void syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3) { +uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3) { switch (func) { + case SYSCALL_READ: + return read(arg1, arg2, (char *)arg3); case SYSCALL_WRITE: - write(arg1, arg2, arg3); - break; + return write(arg1, (const char *)arg2, arg3); + case SYSCALL_GETCWD: + return getcwd(arg1, (char *)arg2); + case SYSCALL_CHDIR: + return chdir((const char *)arg1); + case SYSCALL_SPAWN: + return spawn((const char *)arg1, arg2, (const char **)arg3); case SYSCALL_EXIT: exit(); - break; + return 0; default: - break; + return (uint64_t)-1; } } diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index b4d26e6..a98214d 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -2,9 +2,13 @@ #include +#define SYSCALL_READ 0 #define SYSCALL_WRITE 1 +#define SYSCALL_GETCWD 2 +#define SYSCALL_CHDIR 3 +#define SYSCALL_SPAWN 4 #define SYSCALL_EXIT 60 void syscall_init(); -void syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3); +uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3); diff --git a/src/kernel/vga.c b/src/kernel/vga.c index 011c095..fbc97cd 100644 --- a/src/kernel/vga.c +++ b/src/kernel/vga.c @@ -142,13 +142,15 @@ static void on_char_received(char c) { } } -static void stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t size) { - while (size--) { +static uint64_t stream_write(__attribute__((unused)) const stream_t *self, const char *from, uint64_t bytes) { + uint64_t left = bytes; + while (left--) { on_char_received(*from++); } + return bytes; } -static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, +static uint64_t stream_read(__attribute__((unused)) const stream_t *self, __attribute__((unused)) uint64_t max, __attribute__((unused)) char *to) { return 0; } diff --git a/src/lib/layout.h b/src/lib/layout.h index d545bba..0f8b2c5 100644 --- a/src/lib/layout.h +++ b/src/lib/layout.h @@ -9,7 +9,7 @@ #define KERNEL_VIRTUAL_BASE 0xFFFFFFFF80000000ULL #define PHYS_TO_VIRT(phys) ((void *)((uint64_t)(phys) + KERNEL_VIRTUAL_BASE)) -#define VIRT_TO_PHYS(virt) ((uint64_t)(virt) - KERNEL_VIRTUAL_BASE) +#define VIRT_TO_PHYS(virt) ((void *)((uint64_t)(virt) - KERNEL_VIRTUAL_BASE)) #define KERNEL_VIRTUAL_PML4 (KERNEL_VIRTUAL_BASE + 0x10000) #define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000) diff --git a/src/user/app/echo/echo.c b/src/user/app/echo/echo.c new file mode 100644 index 0000000..4be8332 --- /dev/null +++ b/src/user/app/echo/echo.c @@ -0,0 +1,13 @@ +#include "src/lib/string.h" +#include "src/user/syscall.h" + +int main(uint64_t argc, const char **argv) { + for (uint64_t i = 1; i < argc; i++) { + if (i > 1) { + write(1, " ", 1); + } + write(1, argv[i], string_length(argv[i])); + } + write(1, "\n", 1); + return 0; +} diff --git a/src/user/app/hello/hello.c b/src/user/app/hello/hello.c deleted file mode 100644 index eb0e00d..0000000 --- a/src/user/app/hello/hello.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "src/user/syscall.h" - -int main() { - write(1, "Hello from C!\n", 14); - return 0; -} diff --git a/src/kernel/app/terminal.c b/src/user/app/terminal/terminal.c similarity index 67% rename from src/kernel/app/terminal.c rename to src/user/app/terminal/terminal.c index a11c606..13c9e58 100644 --- a/src/kernel/app/terminal.c +++ b/src/user/app/terminal/terminal.c @@ -1,15 +1,13 @@ -#include "src/kernel/app/terminal.h" -#include "src/kernel/app/cat.h" -#include "src/kernel/app/ls.h" -#include "src/kernel/fs.h" -#include "src/kernel/path.h" -#include "src/kernel/process.h" #include "src/lib/memory.h" #include "src/lib/string.h" -#include "src/lib/util.h" +#include "src/user/syscall.h" +#include -static void help(process_t *proc, uint8_t argc, char **argv); -static void cd(process_t *proc, uint8_t argc, char **argv); +static void help(uint8_t argc, char **argv); +static void cd(uint8_t argc, char **argv); + +#define WRITE_S(s) write(1, s, sizeof(s) - 1); +#define WRITE_D(s) write(1, s, string_length(s)); #define PROMPT_LENGTH 255 @@ -20,54 +18,50 @@ static char prompt[PROMPT_LENGTH + 1]; static uint8_t prompt_length = 0; static uint8_t prompt_offset = 0; -process_t *proc; - -static uint8_t active = 1; +typedef void (*app_t)(uint8_t argc, char **argv); typedef struct { const char *name; app_t app; } app_entry_t; -static app_entry_t apps[] = { +static app_entry_t builtins[] = { {"help", help}, {"cd", cd}, - {"ls", ls}, - {"cat", cat}, }; -static void help(process_t *proc, uint8_t argc, __attribute__((unused)) char **argv) { - if (argc > 1) { - WRITE_S("help: accepts no arguments\n"); +static void help(uint8_t argc, __attribute__((unused)) char **argv) { + if (argc) { + WRITE_S("help: expects no arguments"); + return; } WRITE_S("Available commands:\n"); - for (uint64_t i = 0; i < sizeof(apps) / sizeof(app_entry_t); i++) { - WRITE_D(apps[i].name); + for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { + WRITE_D(builtins[i].name); WRITE_S("\n"); } } -static void cd(process_t *proc, uint8_t argc, char **argv) { +static void cd(uint8_t argc, char **argv) { if (argc != 2) { WRITE_S("cd: requires a single path\n"); return; } - path_t *new = path_open(proc->root, proc->cwd, argv[1]); - if (!new) { + if (chdir(argv[1]) == (uint64_t)-1) { WRITE_S("cd: path does not exist\n"); - return; } - - path_close(proc->cwd); - proc->cwd = new; } static void print_prompt() { - WRITE_S("["); - fs_node_t *curr = proc->cwd->depth ? proc->cwd->stack[proc->cwd->depth - 1] : proc->root; - WRITE_D(curr->name); + char path[PROMPT_LENGTH]; + uint8_t pl = getcwd(PROMPT_LENGTH, path); + char *components[16]; + uint8_t cl = string_split(path, '/', 16, components); + + WRITE_S("[") + WRITE_D(pl == 1 ? "/" : components[cl - 1]); WRITE_S("]$ "); } @@ -76,7 +70,7 @@ static void on_home_pressed() { return; } - proc->stdout->write(proc->stdout, bs, prompt_offset); + write(1, bs, prompt_offset); prompt_offset = 0; } @@ -85,7 +79,7 @@ static void on_left_pressed() { return; } - proc->stdout->write(proc->stdout, bs, 1); + write(1, bs, 1); prompt_offset--; } @@ -94,7 +88,7 @@ static void on_right_pressed() { return; } - proc->stdout->write(proc->stdout, prompt + prompt_offset, 1); + write(1, prompt + prompt_offset, 1); prompt_offset++; } @@ -103,7 +97,7 @@ static void on_end_pressed() { return; } - proc->stdout->write(proc->stdout, prompt + prompt_offset, prompt_length - prompt_offset); + write(1, prompt + prompt_offset, prompt_length - prompt_offset); prompt_offset = prompt_length; } @@ -116,14 +110,16 @@ static void on_enter_pressed() { WRITE_S("\n"); uint8_t i; - for (i = 0; i < sizeof(apps) / sizeof(app_entry_t); i++) { - if (string_equal(argv[0], apps[i].name)) { - apps[i].app(proc, argc, argv); + for (i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { + if (string_equal(argv[0], builtins[i].name)) { + builtins[i].app(argc, argv); break; } } - if (i == sizeof(apps) / sizeof(app_entry_t)) { - WRITE_S("Unknown command\n"); + if (i == sizeof(builtins) / sizeof(app_entry_t)) { + if (spawn(argv[0], argc, (const char **)argv) == (uint64_t)-1){ + WRITE_S("terminal: program not found\n"); + } } prompt_length = prompt_offset = 0; print_prompt(); @@ -137,7 +133,7 @@ static void on_cancel_pressed() { static void on_disconnect_pressed() { WRITE_S("^D\n"); - active = 0; + exit(); } static void on_ctrl_character_pressed(char c) { @@ -150,9 +146,9 @@ static void on_ctrl_character_pressed(char c) { static void redraw_from_cursor() { uint64_t tail = prompt_length - prompt_offset; - proc->stdout->write(proc->stdout, prompt + prompt_offset, tail); - proc->stdout->write(proc->stdout, ws, 1); // erase the character past the end - proc->stdout->write(proc->stdout, bs, tail + 1); // move back to cursor position + write(1, prompt + prompt_offset, tail); + write(1, ws, 1); // erase the character past the end + write(1, bs, tail + 1); // move back to cursor position } static void on_character_pressed(char c) { @@ -166,7 +162,7 @@ static void on_character_pressed(char c) { prompt_length++; prompt_offset++; - proc->stdout->write(proc->stdout, &c, 1); // emit the character itself + write(1, &c, 1); // emit the character itself redraw_from_cursor(); } @@ -180,7 +176,7 @@ static void on_backspace_pressed() { prompt_offset--; prompt_length--; - proc->stdout->write(proc->stdout, bs, 1); + write(1, bs, 1); redraw_from_cursor(); } @@ -262,22 +258,20 @@ static void on_char_received(char c) { } } -void terminal(process_t *p, __attribute__((unused)) uint8_t argc, __attribute__((unused)) char **argv) { +int main() { memory_set('\b', PROMPT_LENGTH, (char *)bs); memory_set(' ', PROMPT_LENGTH, (char *)ws); - proc = p; - WRITE_D("Welcome to FreywarOS v" VERSION "!\n\n"); print_prompt(); - while (active) { + while (1) { char c; - if (proc->stdin->read(proc->stdin, 1, &c)) { + if (read(0, 1, &c)) { on_char_received(c); } } - proc = NUL; + return 0; } diff --git a/src/user/start.asm b/src/user/start.asm index 08b6c02..654c742 100644 --- a/src/user/start.asm +++ b/src/user/start.asm @@ -4,6 +4,8 @@ extern main global _start _start: + pop rdi + mov rsi, rsp call main mov rdi, rax ; return value mov rax, 60 ; exit syscall diff --git a/src/user/syscall.asm b/src/user/syscall.asm index 872e773..43a63c3 100644 --- a/src/user/syscall.asm +++ b/src/user/syscall.asm @@ -1,11 +1,35 @@ bits 64 +global read +read: + mov rax, 0 + syscall + ret + global write write: mov rax, 1 syscall ret +global getcwd +getcwd: + mov rax, 2 + syscall + ret + +global chdir +chdir: + mov rax, 3 + syscall + ret + +global spawn +spawn: + mov rax, 4 + syscall + ret + global exit exit: mov rax, 60 diff --git a/src/user/syscall.h b/src/user/syscall.h index 081054f..4702d89 100644 --- a/src/user/syscall.h +++ b/src/user/syscall.h @@ -2,6 +2,14 @@ #include -void write(int64_t fd, const void *data, uint64_t bytes); +uint64_t read(int64_t fd, uint64_t max, void *to); + +uint64_t write(int64_t fd, const void *from, uint64_t bytes); + +uint64_t getcwd(uint64_t max, void *to); + +uint64_t chdir(const char *path); + +uint64_t spawn(const char *path, uint64_t argc, const char **argv); void exit();