/* * 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 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 the terminal waits for keyboard input it yields to the cooperative * scheduler so that background tasks can make progress. */ #include "kernel_types.h" #include "boot_info.h" #include "commands.h" #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, ...) \ do { \ if ((Boot) != NULL && (Boot)->print != NULL) { \ (Boot)->print(__VA_ARGS__); \ } \ } while (0) /* Simple text attribute helper (modelled after UEFI TEXT_ATTR). */ #define TEXT_ATTR(fg, bg) (UINTN)(((fg) & 0x0F) | (((bg) & 0x0F) << 4)) /* Basic colour constants (compatible with UEFI's palette). */ #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) { KSTATUS Status; Task *terminal_task = NULL; StarlingContext *ctx = NULL; if (Boot == NULL) { return; } if (Boot->clear_screen != NULL) { Status = Boot->clear_screen(); if (Status != 0) { SAFE_PRINT(Boot, L"clear_screen failed (status=%ld)\n\r", (UINT64)Status); } } if (Boot->set_attribute != NULL) { Status = Boot->set_attribute(TEXT_ATTR(COLOR_LIGHTGREEN, COLOR_BLACK)); if (Status != 0) { SAFE_PRINT(Boot, L"set_attribute failed (status=%ld)\n\r", (UINT64)Status); } } /* ---- Subsystem initialisation ---- */ idt_init(Boot); memory_init(Boot); task_init(Boot); /* ---- Welcome banner ---- */ SAFE_PRINT(Boot, L" Welcome to Simple 64-bit Operating System!\n\r"); SAFE_PRINT(Boot, L"================================================\n\r"); SAFE_PRINT(Boot, L"\n\r"); SAFE_PRINT(Boot, L"System Information:\n\r"); if (Boot->firmware_vendor != NULL) { SAFE_PRINT(Boot, L" Firmware Vendor: %s\n\r", Boot->firmware_vendor); SAFE_PRINT(Boot, L" Firmware Revision: %d.%d\n\r", Boot->firmware_major, Boot->firmware_minor); } else { SAFE_PRINT(Boot, L" Firmware information: Unavailable\n\r"); } SAFE_PRINT(Boot, L"\n\r"); 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"); /* ---- 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; } 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) { task_yield(); } }