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.

This commit is contained in:
2026-02-27 20:14:24 +00:00
parent 7ecf26cbd9
commit 9b1a70e3a5
4 changed files with 199 additions and 61 deletions

View File

@@ -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);

220
kernel.c
View File

@@ -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();
}
}

27
task.c
View File

@@ -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)
* ---------------------------------------------------------------- */

3
task.h
View File

@@ -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);