diff --git a/README.md b/README.md index aa23ea1..fbcbea8 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ The loader expects `kernel.elf` at the root of the EFI partition (next to the `E ### Shell Commands -Once the OS boots, an interactive prompt (`->`) is displayed. The following commands are available: +Once the OS boots, the Starling Terminal displays an interactive prompt (`starling>`). The following built-in commands are available: | Command | Description | |---------|-------------| @@ -115,10 +115,17 @@ Once the OS boots, an interactive prompt (`->`) is displayed. The following comm | `about` | Display system information | | `mem` | Display memory statistics (PMM, heap, paging) | | `ps` | List all active tasks with PID, state, and name | -| `spawn [name]` | Create a demo background task | -| `memtest` | Run memory allocation/deallocation tests | +| `spawn [name]` | Create a demo background task (optional argument sets the task name) | +| `memtest` | Run memory allocation/deallocation and PMM tests | | `tasktest` | Spawn multiple concurrent tasks to test the scheduler | +In addition, the Starling Terminal itself recognises: + +| Command | Description | +|---------|-------------| +| `starling` | Spawn a nested Starling Terminal at increased depth | +| `exit` | Exit the current nested Starling Terminal (not allowed at depth 0) | + ### Example Session ``` @@ -185,8 +192,8 @@ UEFI Firmware └─ kmain(&Boot) [kernel.c] jump to kernel ├─ idt_init() install exception handlers ├─ memory_init() PMM → paging → heap - ├─ task_init() scheduler + task 0 - └─ shell loop read-eval-print with cooperative yield + ├─ task_init() scheduler + task 0 + └─ Starling Terminal task interactive read-eval-print loop ``` ### Memory Management diff --git a/commands.c b/commands.c index 8386980..914d75f 100644 --- a/commands.c +++ b/commands.c @@ -40,6 +40,32 @@ static void cmd_spawn(BootInfo *Boot, CHAR16 *Args); static void cmd_memtest(BootInfo *Boot, CHAR16 *Args); static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args); +/* Small helper struct used to pass arguments into per-command tasks. */ +typedef struct { + BootInfo *Boot; + CommandHandlerFn handler; + CHAR16 args[128]; +} CommandTaskContext; + +static void command_task_entry(void *arg); + +/* Local string copy helper (wide-char, bounded). */ +static void wstrcpy16_local(CHAR16 *dst, const CHAR16 *src, UINTN max) +{ + UINTN i = 0; + + if (dst == NULL || max == 0) { + return; + } + + while (src != NULL && src[i] != L'\0' && i < (max - 1)) { + dst[i] = src[i]; + i++; + } + + dst[i] = L'\0'; +} + /* ================================================================ * Command registry * ================================================================ */ @@ -96,13 +122,22 @@ static Command commands[] = { { L"memtest", L"Test memory allocation and deallocation", - L"Usage: memtest\n\r Allocates and frees heap and page memory to verify\n\r the memory manager is working correctly.", + L"Usage: memtest\n\r" + L" Run memory tests in four phases:\n\r" + L" 1) Heap allocation of 8 blocks (16–4096 bytes) via kmalloc()\n\r" + L" 2) Heap free and coalescing verification via kfree()\n\r" + L" 3) Single-page PMM allocate/free via pmm_alloc_page()/pmm_free_page()\n\r" + L" 4) Multi-page (4-page) PMM allocate/free via pmm_alloc_pages()/pmm_free_pages()\n\r", cmd_memtest }, { L"tasktest", L"Test task scheduler with multiple tasks", - L"Usage: tasktest\n\r Spawns several concurrent tasks that run cooperatively\n\r and report their progress, demonstrating context switching.", + L"Usage: tasktest\n\r" + L" Spawns three worker tasks (worker-A/B/C) that run cooperatively,\n\r" + L" each printing three progress steps and yielding between them.\n\r" + L" After the workers finish, prints the final task list to\n\r" + L" demonstrate the cooperative round-robin scheduler.", cmd_tasktest }, {NULL, NULL, NULL, NULL} /* sentinel */ @@ -448,22 +483,29 @@ void show_help(BootInfo *Boot) /* * Parse a line of user input into command + arguments and dispatch - * to the matching handler. Unknown commands print an error. + * by spawning a dedicated task for the matching handler. Unknown + * commands print an error. + * + * Returns: + * - Pointer to the spawned Task for the command, or NULL if the + * command was not found or had to run synchronously. */ -void execute_command(BootInfo *Boot, CHAR16 *Input) +Task *execute_command(BootInfo *Boot, CHAR16 *Input) { CHAR16 *cmd_start = NULL; CHAR16 *args_start = NULL; UINTN i = 0; + CommandTaskContext *ctx; + Task *t; if (Boot == NULL || Input == NULL) { - return; + return NULL; } trim_spaces_inplace(Input); if (Input[0] == L'\0') { - return; + return NULL; } /* Split input into command and argument strings */ @@ -489,12 +531,57 @@ void execute_command(BootInfo *Boot, CHAR16 *Input) /* Look up and dispatch the command */ for (i = 0; commands[i].name != NULL; i++) { if (ascii_streq_ci(cmd_start, commands[i].name)) { - commands[i].handler(Boot, args_start); - return; + /* Allocate a context block for the command task. */ + ctx = (CommandTaskContext *)kmalloc(sizeof(CommandTaskContext)); + if (ctx == NULL) { + SAFE_PRINT(Boot, L"Failed to allocate command context; running in core thread.\n\r"); + commands[i].handler(Boot, args_start); + return NULL; + } + + ctx->Boot = Boot; + ctx->handler = commands[i].handler; + wstrcpy16_local(ctx->args, args_start, sizeof(ctx->args) / sizeof(ctx->args[0])); + + t = task_create(commands[i].name, command_task_entry, ctx); + if (t == NULL) { + SAFE_PRINT(Boot, L"Failed to create task for command '%s'; running in core thread.\n\r", + commands[i].name); + kfree(ctx); + commands[i].handler(Boot, args_start); + return NULL; + } + + SAFE_PRINT(Boot, L"[starling] spawned '%s' as PID %d\n\r", t->name, t->pid); + return t; } } /* Command not found */ SAFE_PRINT(Boot, L"Unknown command: %s\n\r", cmd_start); SAFE_PRINT(Boot, L"Type 'help' for a list of available commands.\n\r"); + return NULL; +} + +/* ---------------------------------------------------------------- + * Command task entry – executes one command in its own task context + * ---------------------------------------------------------------- */ + +static void command_task_entry(void *arg) +{ + CommandTaskContext *ctx = (CommandTaskContext *)arg; + BootInfo *Boot = NULL; + + if (ctx == NULL) { + return; + } + + Boot = ctx->Boot; + + if (ctx->handler != NULL) { + ctx->handler(Boot, ctx->args); + } + + /* Context was heap-allocated in execute_command. */ + kfree(ctx); }