268 lines
9.7 KiB
C
268 lines
9.7 KiB
C
/*
|
||
* 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 */
|
||
TaskPrivilege shell_priv; /* logical privilege for this shell */
|
||
} 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;
|
||
TaskPrivilege shell_priv;
|
||
|
||
if (ctx == NULL || ctx->Boot == NULL) {
|
||
return;
|
||
}
|
||
|
||
Boot = ctx->Boot;
|
||
depth = ctx->depth;
|
||
shell_priv = ctx->shell_priv;
|
||
|
||
SAFE_PRINT(Boot, L"\n\r[Starling Terminal depth %d, priv %d] ready.\n\r\n\r",
|
||
depth, (INT32)shell_priv);
|
||
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_ctx->shell_priv = shell_priv;
|
||
|
||
child_task = task_create_with_priv(L"starling-term",
|
||
starling_terminal_task,
|
||
child_ctx,
|
||
shell_priv);
|
||
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, shell_priv);
|
||
|
||
/* 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;
|
||
inline_ctx.shell_priv = TASK_PRIV_USER;
|
||
starling_terminal_task(&inline_ctx);
|
||
return;
|
||
}
|
||
|
||
ctx->Boot = Boot;
|
||
ctx->depth = 0;
|
||
ctx->shell_priv = TASK_PRIV_USER;
|
||
|
||
terminal_task = task_create_with_priv(L"starling-term",
|
||
starling_terminal_task,
|
||
ctx,
|
||
TASK_PRIV_USER);
|
||
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();
|
||
}
|
||
}
|