From 9b1a70e3a544adfaec125f8245bdd9dfa3f99cc2 Mon Sep 17 00:00:00 2001 From: JimmyBinoculars Date: Fri, 27 Feb 2026 20:14:24 +0000 Subject: [PATCH] Enhance command execution with improved concurrency and documentation updates. Refined the CommandTaskContext structure for better task management and clarified usage instructions for 'memtest' and 'tasktest' commands in README.md. --- commands.h | 10 ++- kernel.c | 220 +++++++++++++++++++++++++++++++++++++++-------------- task.c | 27 ++++++- task.h | 3 + 4 files changed, 199 insertions(+), 61 deletions(-) diff --git a/commands.h b/commands.h index 77135ac..f96348b 100644 --- a/commands.h +++ b/commands.h @@ -9,6 +9,7 @@ #define COMMANDS_H #include "boot_info.h" +#include "task.h" /* Handler function signature: receives BootInfo and any argument text. */ typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args); @@ -26,8 +27,13 @@ typedef struct { CommandHandlerFn handler; /* function that executes the cmd */ } Command; -/* Parse and dispatch a line of user input. */ -void execute_command(BootInfo *Boot, CHAR16 *Input); +/* Parse and dispatch a line of user input. + * + * Returns: + * - Pointer to a Task representing the spawned command process, + * or NULL if the command was not found or ran synchronously. + */ +Task *execute_command(BootInfo *Boot, CHAR16 *Input); /* Print a formatted list of all registered commands. */ void show_help(BootInfo *Boot); diff --git a/kernel.c b/kernel.c index 589c160..d3db011 100644 --- a/kernel.c +++ b/kernel.c @@ -1,12 +1,13 @@ /* - * kernel.c – Kernel entry point and interactive shell loop. + * kernel.c – Kernel entry point and Starling Terminal task. * * kmain() is called by the loader (main.c) after the ELF kernel has been * mapped into memory. It initialises subsystems (IDT, memory, tasks), - * prints a welcome banner, and enters an interactive read-eval-print + * prints a welcome banner, and then spawns the Starling Terminal as a + * dedicated task. The terminal task runs the interactive read-eval-print * loop that dispatches typed commands via commands.c. * - * While waiting for keyboard input, the shell yields to the cooperative + * While the terminal waits for keyboard input it yields to the cooperative * scheduler so that background tasks can make progress. */ @@ -16,6 +17,7 @@ #include "idt.h" #include "memory.h" #include "task.h" +#include "string_utils.h" /* Null-safe print helper used throughout the kernel. */ #define SAFE_PRINT(Boot, ...) \ @@ -32,15 +34,143 @@ #define COLOR_BLACK 0x0 #define COLOR_LIGHTGREEN 0xA +/* Simple context passed to each Starling Terminal instance. */ +typedef struct { + BootInfo *Boot; + UINTN depth; /* 0 = top-level, 1+ = nested shells */ +} StarlingContext; + +/* ================================================================ + * Starling Terminal task – interactive command loop + * ================================================================ */ + +static void starling_terminal_task(void *arg) +{ + StarlingContext *ctx = (StarlingContext *)arg; + BootInfo *Boot = NULL; + KeyEvent Key; + KSTATUS Status; + UINTN read_errors = 0; + CHAR16 line[128]; + UINTN len = 0; + UINTN depth = 0; + + if (ctx == NULL || ctx->Boot == NULL) { + return; + } + + Boot = ctx->Boot; + depth = ctx->depth; + + SAFE_PRINT(Boot, L"\n\r[Starling Terminal depth %d] ready.\n\r\n\r", depth); + SAFE_PRINT(Boot, L"starling> "); + + while (TRUE) { + /* Try non-blocking read first; yield to other tasks while idle */ + if (Boot->try_read_key != NULL) { + Status = Boot->try_read_key(&Key); + if (Status != 0) { + task_yield(); + continue; + } + } else if (Boot->read_key != NULL) { + Status = Boot->read_key(&Key); + } else { + SAFE_PRINT(Boot, L"Console input unavailable.\n\r"); + break; + } + + if (Status != 0) { + read_errors++; + if (read_errors == 1 || (read_errors % 64) == 0) { + SAFE_PRINT(Boot, L"read_key failed (status=%ld)\n\r", + (UINT64)Status); + } + continue; + } + read_errors = 0; + + if (Key.unicode_char == L'\r' || Key.unicode_char == L'\n') { + /* Enter pressed: execute the buffered command */ + line[len] = L'\0'; + SAFE_PRINT(Boot, L"\n\r"); + trim_spaces_inplace(line); + + /* Built-in terminal controls: exit / spawn nested Starling. */ + if (ascii_streq_ci(line, L"exit")) { + if (depth == 0) { + SAFE_PRINT(Boot, L"Starling: cannot exit top-level terminal.\n\r"); + SAFE_PRINT(Boot, L"starling> "); + } else { + SAFE_PRINT(Boot, L"Exiting Starling Terminal depth %d...\n\r", depth); + break; + } + } else if (ascii_streq_ci(line, L"starling")) { + StarlingContext *child_ctx; + Task *child_task; + + child_ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext)); + if (child_ctx == NULL) { + SAFE_PRINT(Boot, L"Starling: failed to allocate nested terminal context.\n\r"); + SAFE_PRINT(Boot, L"starling> "); + } else { + child_ctx->Boot = Boot; + child_ctx->depth = depth + 1; + + child_task = task_create(L"starling-term", starling_terminal_task, child_ctx); + if (child_task == NULL) { + SAFE_PRINT(Boot, L"Starling: failed to spawn nested terminal.\n\r"); + kfree(child_ctx); + SAFE_PRINT(Boot, L"starling> "); + } else { + SAFE_PRINT(Boot, L"[starling] spawned nested terminal (PID %d, depth %d)\n\r", + child_task->pid, child_ctx->depth); + /* Block this shell until the child terminal exits. */ + task_wait(child_task); + SAFE_PRINT(Boot, L"[starling] returned from nested terminal (depth %d)\n\r", depth); + SAFE_PRINT(Boot, L"starling> "); + } + } + } else { + Task *cmd_task = execute_command(Boot, line); + + /* If a command task was spawned, wait for it to finish. */ + if (cmd_task != NULL) { + task_wait(cmd_task); + } + + /* Reset for next command */ + len = 0; + SAFE_PRINT(Boot, L"starling> "); + } + } else if (Key.scan_code == 0x08 || Key.unicode_char == L'\b' || Key.unicode_char == 0x7F) { + /* Backspace */ + if (len > 0) { + len--; + SAFE_PRINT(Boot, L"\b \b"); + } + } else if (Key.unicode_char >= 32 && Key.unicode_char < 127) { + /* Printable ASCII */ + if (len < (sizeof(line) / sizeof(line[0]) - 1)) { + line[len++] = Key.unicode_char; + SAFE_PRINT(Boot, L"%c", Key.unicode_char); + } + } + } + + /* Free our context on exit (allocated by the spawner). */ + kfree(ctx); +} + /* ================================================================ * Kernel entry point * ================================================================ */ void kmain(BootInfo *Boot) { - KeyEvent Key; KSTATUS Status; - UINTN read_errors = 0; + Task *terminal_task = NULL; + StarlingContext *ctx = NULL; if (Boot == NULL) { return; @@ -84,61 +214,41 @@ void kmain(BootInfo *Boot) SAFE_PRINT(Boot, L"Available Services:\n\r"); SAFE_PRINT(Boot, L" - Console Input/Output: %s\n\r", (Boot->read_key != NULL && Boot->print != NULL) ? L"Active" : L"Unavailable"); + SAFE_PRINT(Boot, L" - Terminal: Starling Terminal\n\r"); SAFE_PRINT(Boot, L"\n\r"); SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r"); - /* ---- Interactive shell loop ---- */ - CHAR16 line[128]; - UINTN len = 0; + /* ---- Spawn Starling Terminal as its own task ---- */ + ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext)); + if (ctx == NULL) { + SAFE_PRINT(Boot, L"Failed to allocate Starling Terminal context; starting inline.\n\r"); + StarlingContext inline_ctx; + inline_ctx.Boot = Boot; + inline_ctx.depth = 0; + starling_terminal_task(&inline_ctx); + return; + } - SAFE_PRINT(Boot, L"-> "); + ctx->Boot = Boot; + ctx->depth = 0; + terminal_task = task_create(L"starling-term", starling_terminal_task, ctx); + if (terminal_task == NULL) { + SAFE_PRINT(Boot, L"Failed to start Starling Terminal task; falling back to kernel loop.\n\r"); + + /* + * Fall back to running the terminal loop directly in the core + * thread so the system remains usable even if task creation + * fails for some reason. + */ + starling_terminal_task(Boot); + return; + } + + SAFE_PRINT(Boot, L"[core] Started Starling Terminal (PID %d).\n\r", terminal_task->pid); + + /* Core thread becomes an idle loop, yielding to the terminal and others. */ while (TRUE) { - /* Try non-blocking read first; yield to other tasks while idle */ - if (Boot->try_read_key != NULL) { - Status = Boot->try_read_key(&Key); - if (Status != 0) { - task_yield(); - continue; - } - } else if (Boot->read_key != NULL) { - Status = Boot->read_key(&Key); - } else { - SAFE_PRINT(Boot, L"Console input unavailable.\n\r"); - break; - } - - if (Status != 0) { - read_errors++; - if (read_errors == 1 || (read_errors % 64) == 0) { - SAFE_PRINT(Boot, L"read_key failed (status=%ld)\n\r", - (UINT64)Status); - } - continue; - } - read_errors = 0; - - if (Key.unicode_char == L'\r' || Key.unicode_char == L'\n') { - /* Enter pressed: execute the buffered command */ - line[len] = L'\0'; - SAFE_PRINT(Boot, L"\n\r"); - execute_command(Boot, line); - - /* Reset for next command */ - len = 0; - SAFE_PRINT(Boot, L"-> "); - } else if (Key.scan_code == 0x08 || Key.unicode_char == L'\b' || Key.unicode_char == 0x7F) { - /* Backspace */ - if (len > 0) { - len--; - SAFE_PRINT(Boot, L"\b \b"); - } - } else if (Key.unicode_char >= 32 && Key.unicode_char < 127) { - /* Printable ASCII */ - if (len < (sizeof(line) / sizeof(line[0]) - 1)) { - line[len++] = Key.unicode_char; - SAFE_PRINT(Boot, L"%c", Key.unicode_char); - } - } + task_yield(); } } diff --git a/task.c b/task.c index f08e239..e4b00a2 100644 --- a/task.c +++ b/task.c @@ -1,7 +1,7 @@ /* * task.c – Cooperative multitasking: PCB pool, scheduler, yield/exit. * - * Task 0 is the kernel/shell thread (uses the boot stack). + * Task 0 is the always-present kernel core thread (uses the boot stack). * Additional tasks are created with task_create(), which allocates a * stack from the PMM and sets up a fake context-switch frame so that * context_switch() can "return" into a trampoline that calls the real @@ -57,7 +57,7 @@ static void wstrcpy16(CHAR16 *dst, const CHAR16 *src, UINTN max) /* * Initialise the scheduler: clear all PCB slots and register the - * currently running kernel thread as task 0. + * currently running kernel core thread as task 0. */ void task_init(BootInfo *Boot) { @@ -79,7 +79,7 @@ void task_init(BootInfo *Boot) } /* - * Task 0 = the currently running kernel thread (the shell). + * Task 0 = the currently running kernel core thread. * It already has a stack (the kernel's boot stack), so we don't * allocate one. Its saved_rsp will be filled in during the * first context_switch call in task_yield(). @@ -87,7 +87,7 @@ void task_init(BootInfo *Boot) tasks[0].pid = next_pid++; tasks[0].state = TASK_STATE_RUNNING; tasks[0].switches = 1; - wstrcpy16(tasks[0].name, L"kernel", TASK_NAME_LEN); + wstrcpy16(tasks[0].name, L"core", TASK_NAME_LEN); current_task = &tasks[0]; task_ready = TRUE; @@ -329,6 +329,25 @@ UINTN task_count(void) return count; } +/* ---------------------------------------------------------------- + * Wait for another task to finish + * ---------------------------------------------------------------- */ + +void task_wait(Task *t) +{ + if (!task_ready || t == NULL) { + return; + } + + /* + * Busy-wait cooperatively until the target task's PCB slot has + * been recycled back to FREE by task_exit(). + */ + while (t->state != TASK_STATE_FREE) { + task_yield(); + } +} + /* ---------------------------------------------------------------- * Print task list (implements the `ps` command) * ---------------------------------------------------------------- */ diff --git a/task.h b/task.h index 80ab2ba..0676cf2 100644 --- a/task.h +++ b/task.h @@ -74,6 +74,9 @@ Task *task_current(void); /* return the running task's PCB */ UINTN task_count(void); /* number of non-FREE tasks */ void task_print_list(BootInfo *Boot); /* print task table (for `ps`) */ +/* Block the current task until the target task has finished. */ +void task_wait(Task *t); + /* Assembly context switch (defined in context_switch.S). */ extern void context_switch(UINT64 *old_rsp, UINT64 new_rsp);