From 2aa39271da11163103c6b714f2fba7adfa32c597 Mon Sep 17 00:00:00 2001 From: Freywar Ulvnaudgari Date: Fri, 19 Jun 2026 16:49:29 +0300 Subject: [PATCH] Add process scheduler, pipes, and separate terminal from shell --- build.sh | 1 + meson.build | 50 +++++++++++- src/kernel/kernel.c | 60 +++++++++------ src/kernel/pipe.c | 126 +++++++++++++++++++++++++++++++ src/kernel/pipe.h | 5 ++ src/kernel/process.c | 76 ++++++++++++++++++- src/kernel/process.h | 13 ++++ src/kernel/syscall.c | 37 +++++++-- src/kernel/syscall.h | 1 + src/kernel/timer.asm | 28 +++++++ src/kernel/timer.c | 20 +++++ src/kernel/timer.h | 3 + src/lib/memory.c | 2 +- src/user/app/shell/shell.c | 126 +++++++++++++++++++++++++++++++ src/user/app/terminal/terminal.c | 86 +-------------------- src/user/syscall.asm | 6 ++ src/user/syscall.h | 4 +- 17 files changed, 524 insertions(+), 120 deletions(-) create mode 100644 src/kernel/pipe.c create mode 100644 src/kernel/pipe.h create mode 100644 src/kernel/timer.asm create mode 100644 src/kernel/timer.c create mode 100644 src/kernel/timer.h create mode 100644 src/user/app/shell/shell.c diff --git a/build.sh b/build.sh index eebcb38..2548d40 100755 --- a/build.sh +++ b/build.sh @@ -17,6 +17,7 @@ mcopy -i build/os.img@@"$((partition_offset * sector_size))" -s src ::src mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal +mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/shell ::bin/shell mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat diff --git a/meson.build b/meson.build index 21c1c60..104ea0b 100644 --- a/meson.build +++ b/meson.build @@ -38,6 +38,13 @@ kernel_entry_o = custom_target( command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'], ) +timer_o = custom_target( + 'timer', + input: 'src/kernel/timer.asm', + output: 'timer.o', + command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'], +) + process_o = custom_target( 'process', input: 'src/kernel/process.asm', @@ -71,15 +78,17 @@ kernel_sources = files([ 'src/kernel/panic.c', 'src/kernel/path.c', 'src/kernel/pic.c', + 'src/kernel/pipe.c', 'src/kernel/process.c', 'src/kernel/syscall.c', + 'src/kernel/timer.c', 'src/kernel/tss.c', 'src/kernel/vga.c', ]) kernel_elf = executable( 'kernel.elf', - sources: [kernel_entry_o, process_o, syscall_o, lib_sources, kernel_sources], + sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources], c_args: [ '-ffreestanding', '-nostdlib', @@ -146,6 +155,45 @@ custom_target( build_by_default: true, ) +shell_start = custom_target( + 'shell_start', + input: 'src/user/start.asm', + output: 'shell_start.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +shell_syscall = custom_target( + 'shell_syscall', + input: 'src/user/syscall.asm', + output: 'shell_syscall.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +shell_elf = executable( + 'shell.elf', + sources: [shell_start, shell_syscall, lib_sources, 'src/user/app/shell/shell.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( + 'shell', + input: shell_elf, + output: 'shell', + command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], + build_by_default: true, +) + echo_start = custom_target( 'echo_start', input: 'src/user/start.asm', diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 64c2318..e68b5b6 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -5,8 +5,10 @@ #include "src/kernel/panic.h" #include "src/kernel/path.h" #include "src/kernel/pic.h" +#include "src/kernel/pipe.h" #include "src/kernel/process.h" #include "src/kernel/syscall.h" +#include "src/kernel/timer.h" #include "src/kernel/tss.h" #include "src/kernel/util.h" #include "src/kernel/vga.h" @@ -44,46 +46,56 @@ void kernel_main() { idt_set_entry(0x0E, isr_page_fault, 0x8E); idt_set_entry(0x0D, isr_general_violation, 0x8E); idt_set_entry(46, isr_ata_primary, 0x8E); - __asm__ volatile("sti"); gdt_init(); syscall_init(); process_t *kernel = memory_allocate(sizeof(process_t)); - kernel->pml4 = (void *)KERNEL_VIRTUAL_PML4; - kernel->root = fs_mount(); + kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4; + kernel->pid = 0; + kernel->state = PROCESS_RUNNING; + kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; + kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; kernel->cwd = memory_allocate(sizeof(path_t)); - kernel->fds[0] = keyboard_init(); - kernel->fds[1] = vga_init(); - kernel->free_fd = 2; + kernel->root = fs_mount(); + kernel->fds[kernel->free_fd++] = keyboard_init(); + kernel->fds[kernel->free_fd++] = vga_init(); + kernel->code = EXIT_CODE_OK; - fs_node_t *bin = path_open_file(kernel->root, kernel->cwd, "bin/terminal"); + current_process = kernel; + tss.rsp0 = (uint64_t)kernel->kernel_stack; - ASSERT(bin, "kernel: terminal not found"); + fs_node_t *terminal_node = path_open_file(kernel->root, kernel->cwd, "bin/terminal"); + fs_node_t *shell_node = path_open_file(kernel->root, kernel->cwd, "bin/shell"); - uint8_t *code = memory_allocate(bin->size); - fs_read(bin, 0, bin->size, code); + ASSERT(terminal_node, "kernel: terminal not found"); + ASSERT(shell_node, "kernel: shell not found"); - process_t *terminal = process_create(kernel, code, bin->size); + uint8_t *terminal_code = memory_allocate(terminal_node->size); + fs_read(terminal_node, 0, terminal_node->size, terminal_code); + fs_close(terminal_node); - memory_free(code); - fs_close(bin); + uint8_t *shell_code = memory_allocate(shell_node->size); + fs_read(shell_node, 0, shell_node->size, shell_code); + fs_close(shell_node); - __asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory"); + process_t *terminal = process_create(kernel, terminal_code, terminal_node->size); + memory_free(terminal_code); - uint8_t *stack = (uint8_t *)terminal->user_stack; + process_t *shell = process_create(kernel, shell_code, shell_node->size); + memory_free(shell_code); - stack -= sizeof(uint64_t); - *(uint64_t *)stack = 0; + uint64_t *terminal_stack = (uint64_t *)terminal->user_stack; + *(--terminal_stack) = 0; + terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)terminal_stack)); - terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)stack)); + uint64_t *shell_stack = (uint64_t *)shell->user_stack; + *(--shell_stack) = 0; + shell->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)shell->user_stack - (uint64_t)shell_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)); + pipe_init(&(terminal->fds[terminal->free_fd++]), &(shell->fds[0])); - fs_unmount((fs_node_t *)kernel->root); + timer_init(); - outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware - __asm__ volatile("cli; hlt"); + __asm__ volatile("sti; hlt"); } diff --git a/src/kernel/pipe.c b/src/kernel/pipe.c new file mode 100644 index 0000000..11f1294 --- /dev/null +++ b/src/kernel/pipe.c @@ -0,0 +1,126 @@ +#include "src/kernel/pipe.h" +#include "src/kernel/stream.h" +#include "src/lib/memory.h" + +#define PIPE_BUFFER_SIZE 65536 + +typedef struct { + char buffer[PIPE_BUFFER_SIZE]; + uint64_t begin; + uint64_t end; + uint8_t write_closed; + uint8_t read_closed; +} pipe_t; + +typedef struct { + stream_t stream; + pipe_t *pipe; +} pipe_read_stream_t; + +typedef struct { + stream_t stream; + pipe_t *pipe; +} pipe_write_stream_t; + +static uint64_t null_read(__attribute__((unused)) const stream_t *self, __attribute__((unused)) uint64_t max, + __attribute__((unused)) char *to) { + return 0; +} + +static uint64_t null_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, + __attribute__((unused)) uint64_t bytes) { + return 0; +} + +static uint64_t pipe_read(const stream_t *self, uint64_t max, char *to) { + pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; + + uint64_t available = (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE; + uint64_t to_read = available < max ? available : max; + + if (pipe->write_closed && !to_read) { + return (uint64_t)-1; + } + + if (!to_read) { + return 0; + } + + if (pipe->begin + to_read <= PIPE_BUFFER_SIZE) { + memory_copy(pipe->buffer + pipe->begin, to_read, to); + } else { + uint64_t chunk = PIPE_BUFFER_SIZE - pipe->begin; + memory_copy(pipe->buffer + pipe->begin, chunk, to); + memory_copy(pipe->buffer, to_read - chunk, to + chunk); + } + + pipe->begin = (pipe->begin + to_read) % PIPE_BUFFER_SIZE; + return to_read; +} + +static uint64_t pipe_write(const stream_t *self, const char *from, uint64_t bytes) { + pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; + + if (pipe->read_closed) { + return (uint64_t)-1; + } + + uint64_t available = PIPE_BUFFER_SIZE - (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE; + uint64_t to_write = available < bytes ? available : bytes; + + if (to_write == 0) { + return 0; + } + + if (pipe->end + to_write <= PIPE_BUFFER_SIZE) { + memory_copy(from, to_write, pipe->buffer + pipe->end); + } else { + uint64_t chunk = PIPE_BUFFER_SIZE - pipe->end; + memory_copy(from, chunk, pipe->buffer + pipe->end); + memory_copy(from + chunk, to_write - chunk, pipe->buffer); + } + + pipe->end = (pipe->end + to_write) % PIPE_BUFFER_SIZE; + return to_write; +} + +static void read_close(stream_t *self) { + pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; + + pipe->read_closed = 1; + + if (pipe->read_closed && pipe->write_closed) { + memory_free(pipe); + } + memory_free(self); +} + +static void write_close(stream_t *self) { + pipe_t *pipe = ((pipe_write_stream_t *)self)->pipe; + + pipe->write_closed = 1; + + if (pipe->read_closed && pipe->write_closed) { + memory_free(pipe); + } + memory_free(self); +} + +void pipe_init(stream_t **write, stream_t **read) { + pipe_t *pipe = memory_allocate(sizeof(pipe_t)); + + pipe_write_stream_t *ws = memory_allocate(sizeof(pipe_write_stream_t)); + ws->stream.read = null_read; + ws->stream.write = pipe_write; + ws->stream.close = write_close; + ws->pipe = pipe; + + pipe_read_stream_t *rs = memory_allocate(sizeof(pipe_read_stream_t)); + rs->stream.read = pipe_read; + rs->stream.write = null_write; + rs->stream.close = read_close; + rs->pipe = pipe; + + *write = (stream_t *)ws; + *read = (stream_t *)rs; +} diff --git a/src/kernel/pipe.h b/src/kernel/pipe.h new file mode 100644 index 0000000..c4edcb9 --- /dev/null +++ b/src/kernel/pipe.h @@ -0,0 +1,5 @@ +#pragma once + +#include "src/kernel/stream.h" + +void pipe_init(stream_t **write, stream_t **read); diff --git a/src/kernel/process.c b/src/kernel/process.c index 736a663..c2dbf28 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -1,17 +1,35 @@ #include "src/kernel/process.h" #include "src/kernel/memory.h" -#include "src/kernel/panic.h" #include "src/kernel/path.h" +#include "src/kernel/tss.h" +#include "src/kernel/util.h" #include "src/lib/layout.h" #include "src/lib/memory.h" #include "src/lib/util.h" +#define MAX_PROCESSES 256 + +static process_t *processes[MAX_PROCESSES]; +static uint64_t free_pid = 1; + process_t *current_process; process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size) { - ASSERT(size <= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE, "process_create: binary too big"); + if (size >= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE) { + return NUL; + } + + uint64_t free_pi; + for (free_pi = 0; free_pi <= MAX_PROCESSES; free_pi++) { + if (!processes[free_pi]) { + break; + } + } + if (free_pi == MAX_PROCESSES) { + return NUL; + } - process_t *proc = memory_allocate(sizeof(process_t)); + process_t *proc = processes[free_pi] = memory_allocate(sizeof(process_t)); proc->parent = parent; @@ -56,6 +74,8 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t cwd->stack[i] = fs_open_again(parent->cwd->stack[i]); } + proc->pid = free_pid++; + proc->state = PROCESS_RUNNING; proc->cwd = cwd; proc->root = parent->root; proc->fds[0] = parent->fds[0]; @@ -66,6 +86,49 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t return proc; } +process_t *process_get(uint64_t pid) { + for (uint64_t i = 0; i < MAX_PROCESSES; i++) { + if (processes[i] && processes[i]->pid == pid) { + return processes[i]; + } + } + return NUL; +} + +void process_next() { + process_t *next = NUL; + uint64_t cpi = 0; + + for (uint64_t i = 0; i < MAX_PROCESSES; i++) { + if (processes[i] == current_process) { + cpi = i; + break; + } + } + + for (uint64_t i = 1; i <= MAX_PROCESSES; i++) { + process_t *candidate = processes[(cpi + i) % MAX_PROCESSES]; + if (candidate && candidate->state == PROCESS_RUNNING) { + next = candidate; + break; + } + } + + if (!next) { + outw(0x604, 0x2000); + __asm__ volatile("cli; hlt"); + } + + if (next == current_process) { + return; + } + + process_t *prev = current_process; + tss.rsp0 = (uint64_t)next->kernel_stack; + current_process = next; + process_switch_to(&prev->kernel_rsp, next->kernel_rsp, VIRT_TO_PHYS(next->pml4)); +} + void process_destroy(process_t *proc) { for (uint64_t i = 2; i < MAX_FDS; i++) { if (proc->fds[i]) { @@ -117,5 +180,12 @@ void process_destroy(process_t *proc) { memory_free(proc->kernel_stack - PAGE_SIZE); + for (uint64_t i = 0; i < MAX_PROCESSES; i++) { + if (processes[i] == proc) { + processes[i] = NUL; + break; + } + } + memory_free(proc); } diff --git a/src/kernel/process.h b/src/kernel/process.h index 1060f7b..60e6a3d 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -9,6 +9,12 @@ #define MAX_FDS 16 +typedef enum { + PROCESS_RUNNING, + PROCESS_WAITING, + PROCESS_ZOMBIE, +} process_state_t; + typedef struct process { const struct process *parent; uint64_t *pml4; @@ -16,6 +22,9 @@ typedef struct process { void *kernel_rsp; void *user_stack; void *user_rsp; + uint64_t pid; + process_state_t state; + uint64_t waiting_for; const fs_node_t *root; const path_t *cwd; stream_t *fds[MAX_FDS]; @@ -31,6 +40,10 @@ extern void process_trampoline(); process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size); +process_t *process_get(uint64_t pid); + extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys); +void process_next(); + void process_destroy(process_t *proc); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 0fcf3b7..1ec065c 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -94,7 +94,7 @@ static uint64_t chdir(const char *path) { return current_process->cwd->depth; } -static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) { +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 EXIT_CODE_NOT_FOUND; @@ -138,15 +138,25 @@ static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) { 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; + __asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory"); - process_switch_to(&parent->kernel_rsp, child->kernel_rsp, VIRT_TO_PHYS(child->pml4)); + return child->pid; +} + +static exit_code_t wait(uint64_t pid) { + process_t *child = process_get(pid); + if (!child) { + return EXIT_CODE_GENERAL_FAILURE; + } + + current_process->state = PROCESS_WAITING; + current_process->waiting_for = pid; + while (child->state != PROCESS_ZOMBIE) { + process_next(); + } + current_process->state = PROCESS_RUNNING; exit_code_t code = child->code; - tss.rsp0 = (uint64_t)parent->kernel_stack; - current_process = parent; process_destroy(child); return code; @@ -174,8 +184,17 @@ static uint64_t close(uint64_t fd) { } static void exit(exit_code_t code) { + current_process->state = PROCESS_ZOMBIE; current_process->code = code; - process_switch_to(¤t_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4)); + + if (current_process->parent) { + process_t *parent = (process_t *)current_process->parent; + if (parent->state == PROCESS_WAITING && parent->waiting_for == current_process->pid) { + parent->state = PROCESS_RUNNING; + } + } + + process_next(); } uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, __attribute__((unused)) uint64_t arg4) { @@ -190,6 +209,8 @@ uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t return chdir((const char *)arg1); case SYSCALL_SPAWN: return spawn((const char *)arg1, arg2, (const char **)arg3); + case SYSCALL_WAIT: + return wait(arg1); case SYSCALL_OPEN: return open((const char *)arg1); case SYSCALL_CLOSE: diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index 9d95467..c7553ec 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -7,6 +7,7 @@ #define SYSCALL_GETCWD 2 #define SYSCALL_CHDIR 3 #define SYSCALL_SPAWN 4 +#define SYSCALL_WAIT 5 #define SYSCALL_OPEN 6 #define SYSCALL_CLOSE 7 #define SYSCALL_EXIT 60 diff --git a/src/kernel/timer.asm b/src/kernel/timer.asm new file mode 100644 index 0000000..8a3e130 --- /dev/null +++ b/src/kernel/timer.asm @@ -0,0 +1,28 @@ +bits 64 +extern timer_handler + +global timer_entry +timer_entry: + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + call timer_handler + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + + iretq diff --git a/src/kernel/timer.c b/src/kernel/timer.c new file mode 100644 index 0000000..9cc7895 --- /dev/null +++ b/src/kernel/timer.c @@ -0,0 +1,20 @@ +#include "src/kernel/timer.h" +#include "src/kernel/idt.h" +#include "src/kernel/process.h" +#include "src/kernel/util.h" + +extern void timer_entry(); + +void timer_init() { + // PIT channel 0, rate generator, ~100Hz + outb(0x43, 0x36); + outb(0x40, 0xA9); // divisor low byte (11932 for ~100Hz) + outb(0x40, 0x2E); // divisor high byte + outb(0x21, inb(0x21) & ~0x01); // unmask IRQ0 + idt_set_entry(32, timer_entry, 0x8E); +} + +void timer_handler() { + outb(0x20, 0x20); + process_next(); +} diff --git a/src/kernel/timer.h b/src/kernel/timer.h new file mode 100644 index 0000000..9ea8113 --- /dev/null +++ b/src/kernel/timer.h @@ -0,0 +1,3 @@ +#pragma once + +void timer_init(); diff --git a/src/lib/memory.c b/src/lib/memory.c index 972a6d2..0ad4bdd 100644 --- a/src/lib/memory.c +++ b/src/lib/memory.c @@ -22,7 +22,7 @@ void *memory_allocate(uint64_t size) { free_list->next = NUL; } - size = sizeof(free_chunk_t) + (size + 7) & (uint64_t)~7; + size = sizeof(free_chunk_t) + ((size + 7) & (uint64_t)~7); free_chunk_t *prev = NUL; free_chunk_t *curr = free_list; diff --git a/src/user/app/shell/shell.c b/src/user/app/shell/shell.c new file mode 100644 index 0000000..f16ebd8 --- /dev/null +++ b/src/user/app/shell/shell.c @@ -0,0 +1,126 @@ +#include "src/lib/memory.h" +#include "src/lib/string.h" +#include "src/lib/util.h" +#include "src/user/syscall.h" + +static void help(uint8_t argc, char **argv); +static void cd(uint8_t argc, char **argv); + +#define PATH "/bin/" + +#define PRINT_S(s) write(1, s, sizeof(s) - 1); +#define PRINT_D(s) write(1, s, string_length(s)); + +typedef void (*app_t)(uint8_t argc, char **argv); + +typedef struct { + const char *name; + app_t app; +} app_entry_t; + +static app_entry_t builtins[] = { + {"help", help}, + {"cd", cd}, +}; + +static void help(uint8_t argc, __attribute__((unused)) char **argv) { + if (argc > 1) { + PRINT_S("help: expects no arguments"); + return; + } + + PRINT_S("Available commands:\n"); + for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { + PRINT_D(builtins[i].name); + PRINT_S("\n"); + } +} + +static void cd(uint8_t argc, char **argv) { + if (argc != 2) { + PRINT_S("cd: requires a single path\n"); + return; + } + + if (chdir(argv[1]) == (uint64_t)-1) { + PRINT_S("cd: path does not exist\n"); + } +} + +static void print_prompt() { + char path[256]; + uint8_t pl = getcwd(256, path); + char *components[16]; + uint8_t cl = string_split(path, '/', 16, components); + + PRINT_S("[") + PRINT_D(pl == 1 ? "/" : components[cl - 1]); + PRINT_S("]$ "); +} + +static void execute(char *command) { + char *argv[16]; + uint8_t argc = (uint8_t)string_split(command, ' ', 16, argv); + + PRINT_S("\n"); + + if (string_equal(command, "^C")) { + print_prompt(); + return; + } + + if (string_equal(command, "^D")) { + exit(EXIT_CODE_OK); + } + + uint8_t i; + 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(builtins) / sizeof(app_entry_t)) { + uint64_t size = sizeof(PATH) + string_length(argv[0]) + 1; + char *path = memory_allocate(size); + memory_copy(PATH, sizeof(PATH), path); + memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1); + uint64_t pid = spawn(path, argc, (const char **)argv); + memory_free(path); + + if (pid == EXIT_CODE_NOT_FOUND) { + PRINT_S("shell: program not found\n"); + } else { + waitpid(pid); + } + } + print_prompt(); +} + +uint64_t main() { + PRINT_D("Welcome to FreywarOS v" VERSION "!\n\n"); + + print_prompt(); + + char line[256]; + uint64_t offset; + + char chunk[64]; + uint64_t received; + + while (1) { + if ((received = read(0, sizeof(chunk), chunk))) { + for (uint64_t i = 0; i < received; i++) { + if (chunk[i] == '\n') { + line[offset] = '\0'; + execute(line); + offset = 0; + } else if (offset < 255) { + line[offset++] = chunk[i]; + } + } + } + } + + return 0; +} diff --git a/src/user/app/terminal/terminal.c b/src/user/app/terminal/terminal.c index f62e54c..4054e29 100644 --- a/src/user/app/terminal/terminal.c +++ b/src/user/app/terminal/terminal.c @@ -1,13 +1,6 @@ #include "src/lib/memory.h" -#include "src/lib/string.h" #include "src/lib/util.h" #include "src/user/syscall.h" -#include - -static void help(uint8_t argc, char **argv); -static void cd(uint8_t argc, char **argv); - -#define PATH "/bin/" #define PRINT_S(s) write(1, s, sizeof(s) - 1); #define PRINT_D(s) write(1, s, string_length(s)); @@ -21,53 +14,6 @@ static char prompt[PROMPT_LENGTH + 1]; static uint8_t prompt_length = 0; static uint8_t prompt_offset = 0; -typedef void (*app_t)(uint8_t argc, char **argv); - -typedef struct { - const char *name; - app_t app; -} app_entry_t; - -static app_entry_t builtins[] = { - {"help", help}, - {"cd", cd}, -}; - -static void help(uint8_t argc, __attribute__((unused)) char **argv) { - if (argc) { - PRINT_S("help: expects no arguments"); - return; - } - - PRINT_S("Available commands:\n"); - for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { - PRINT_D(builtins[i].name); - PRINT_S("\n"); - } -} - -static void cd(uint8_t argc, char **argv) { - if (argc != 2) { - PRINT_S("cd: requires a single path\n"); - return; - } - - if (chdir(argv[1]) == (uint64_t)-1) { - PRINT_S("cd: path does not exist\n"); - } -} - -static void print_prompt() { - char path[PROMPT_LENGTH]; - uint8_t pl = getcwd(PROMPT_LENGTH, path); - char *components[16]; - uint8_t cl = string_split(path, '/', 16, components); - - PRINT_S("[") - PRINT_D(pl == 1 ? "/" : components[cl - 1]); - PRINT_S("]$ "); -} - static void on_home_pressed() { if (!prompt_offset) { return; @@ -105,42 +51,22 @@ static void on_end_pressed() { } static void on_enter_pressed() { - prompt[prompt_length] = '\0'; - - char *argv[16]; - uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv); + prompt[prompt_length] = '\n'; - PRINT_S("\n"); + write(2, prompt, prompt_length + 1); - uint8_t i; - 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(builtins) / sizeof(app_entry_t)) { - uint64_t size = sizeof(PATH) + string_length(argv[0]) + 1; - char *path = memory_allocate(size); - memory_copy(PATH, sizeof(PATH), path); - memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1); - if (spawn(path, argc, (const char **)argv) == EXIT_CODE_NOT_FOUND) { - PRINT_S("terminal: program not found\n"); - } - memory_free(path); - } prompt_length = prompt_offset = 0; - print_prompt(); } static void on_cancel_pressed() { PRINT_S("^C\n"); + write(2, "^C\n", 3); prompt_length = prompt_offset = 0; - print_prompt(); } static void on_disconnect_pressed() { PRINT_S("^D\n"); + write(2, "^D\n", 3); exit(EXIT_CODE_OK); } @@ -270,10 +196,6 @@ uint64_t main() { memory_set('\b', PROMPT_LENGTH, (char *)bs); memory_set(' ', PROMPT_LENGTH, (char *)ws); - PRINT_D("Welcome to FreywarOS v" VERSION "!\n\n"); - - print_prompt(); - while (1) { char c; if (read(0, 1, &c)) { diff --git a/src/user/syscall.asm b/src/user/syscall.asm index d68b02e..f4a195f 100644 --- a/src/user/syscall.asm +++ b/src/user/syscall.asm @@ -30,6 +30,12 @@ spawn: syscall ret +global waitpid +waitpid: + mov rax, 5 + syscall + ret + global open open: mov rax, 6 diff --git a/src/user/syscall.h b/src/user/syscall.h index 5124d32..11af13a 100644 --- a/src/user/syscall.h +++ b/src/user/syscall.h @@ -11,7 +11,9 @@ uint64_t getcwd(uint64_t max, void *to); uint64_t chdir(const char *path); -exit_code_t spawn(const char *path, uint64_t argc, const char **argv); +uint64_t spawn(const char *path, uint64_t argc, const char **argv); + +exit_code_t waitpid(uint64_t pid); uint64_t open(const char *path);