Compare commits
2 Commits
d17daf7aac
...
9b1a70e3a5
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b1a70e3a5 | |||
| 7ecf26cbd9 |
17
README.md
17
README.md
@@ -104,7 +104,7 @@ The loader expects `kernel.elf` at the root of the EFI partition (next to the `E
|
|||||||
|
|
||||||
### Shell Commands
|
### 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 |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
@@ -115,10 +115,17 @@ Once the OS boots, an interactive prompt (`->`) is displayed. The following comm
|
|||||||
| `about` | Display system information |
|
| `about` | Display system information |
|
||||||
| `mem` | Display memory statistics (PMM, heap, paging) |
|
| `mem` | Display memory statistics (PMM, heap, paging) |
|
||||||
| `ps` | List all active tasks with PID, state, and name |
|
| `ps` | List all active tasks with PID, state, and name |
|
||||||
| `spawn [name]` | Create a demo background task |
|
| `spawn [name]` | Create a demo background task (optional argument sets the task name) |
|
||||||
| `memtest` | Run memory allocation/deallocation tests |
|
| `memtest` | Run memory allocation/deallocation and PMM tests |
|
||||||
| `tasktest` | Spawn multiple concurrent tasks to test the scheduler |
|
| `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
|
### Example Session
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -185,8 +192,8 @@ UEFI Firmware
|
|||||||
└─ kmain(&Boot) [kernel.c] jump to kernel
|
└─ kmain(&Boot) [kernel.c] jump to kernel
|
||||||
├─ idt_init() install exception handlers
|
├─ idt_init() install exception handlers
|
||||||
├─ memory_init() PMM → paging → heap
|
├─ memory_init() PMM → paging → heap
|
||||||
├─ task_init() scheduler + task 0
|
├─ task_init() scheduler + task 0
|
||||||
└─ shell loop read-eval-print with cooperative yield
|
└─ Starling Terminal task interactive read-eval-print loop
|
||||||
```
|
```
|
||||||
|
|
||||||
### Memory Management
|
### Memory Management
|
||||||
|
|||||||
103
commands.c
103
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_memtest(BootInfo *Boot, CHAR16 *Args);
|
||||||
static void cmd_tasktest(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
|
* Command registry
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
@@ -96,13 +122,22 @@ static Command commands[] = {
|
|||||||
{
|
{
|
||||||
L"memtest",
|
L"memtest",
|
||||||
L"Test memory allocation and deallocation",
|
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
|
cmd_memtest
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
L"tasktest",
|
L"tasktest",
|
||||||
L"Test task scheduler with multiple tasks",
|
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
|
cmd_tasktest
|
||||||
},
|
},
|
||||||
{NULL, NULL, NULL, NULL} /* sentinel */
|
{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
|
* 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 *cmd_start = NULL;
|
||||||
CHAR16 *args_start = NULL;
|
CHAR16 *args_start = NULL;
|
||||||
UINTN i = 0;
|
UINTN i = 0;
|
||||||
|
CommandTaskContext *ctx;
|
||||||
|
Task *t;
|
||||||
|
|
||||||
if (Boot == NULL || Input == NULL) {
|
if (Boot == NULL || Input == NULL) {
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
trim_spaces_inplace(Input);
|
trim_spaces_inplace(Input);
|
||||||
|
|
||||||
if (Input[0] == L'\0') {
|
if (Input[0] == L'\0') {
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Split input into command and argument strings */
|
/* Split input into command and argument strings */
|
||||||
@@ -489,12 +531,57 @@ void execute_command(BootInfo *Boot, CHAR16 *Input)
|
|||||||
/* Look up and dispatch the command */
|
/* Look up and dispatch the command */
|
||||||
for (i = 0; commands[i].name != NULL; i++) {
|
for (i = 0; commands[i].name != NULL; i++) {
|
||||||
if (ascii_streq_ci(cmd_start, commands[i].name)) {
|
if (ascii_streq_ci(cmd_start, commands[i].name)) {
|
||||||
commands[i].handler(Boot, args_start);
|
/* Allocate a context block for the command task. */
|
||||||
return;
|
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 */
|
/* Command not found */
|
||||||
SAFE_PRINT(Boot, L"Unknown command: %s\n\r", cmd_start);
|
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");
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
10
commands.h
10
commands.h
@@ -9,6 +9,7 @@
|
|||||||
#define COMMANDS_H
|
#define COMMANDS_H
|
||||||
|
|
||||||
#include "boot_info.h"
|
#include "boot_info.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
/* Handler function signature: receives BootInfo and any argument text. */
|
/* Handler function signature: receives BootInfo and any argument text. */
|
||||||
typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args);
|
typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args);
|
||||||
@@ -26,8 +27,13 @@ typedef struct {
|
|||||||
CommandHandlerFn handler; /* function that executes the cmd */
|
CommandHandlerFn handler; /* function that executes the cmd */
|
||||||
} Command;
|
} Command;
|
||||||
|
|
||||||
/* Parse and dispatch a line of user input. */
|
/* Parse and dispatch a line of user input.
|
||||||
void execute_command(BootInfo *Boot, CHAR16 *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. */
|
/* Print a formatted list of all registered commands. */
|
||||||
void show_help(BootInfo *Boot);
|
void show_help(BootInfo *Boot);
|
||||||
|
|||||||
220
kernel.c
220
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
|
* kmain() is called by the loader (main.c) after the ELF kernel has been
|
||||||
* mapped into memory. It initialises subsystems (IDT, memory, tasks),
|
* 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.
|
* 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.
|
* scheduler so that background tasks can make progress.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include "idt.h"
|
#include "idt.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
/* Null-safe print helper used throughout the kernel. */
|
/* Null-safe print helper used throughout the kernel. */
|
||||||
#define SAFE_PRINT(Boot, ...) \
|
#define SAFE_PRINT(Boot, ...) \
|
||||||
@@ -32,15 +34,143 @@
|
|||||||
#define COLOR_BLACK 0x0
|
#define COLOR_BLACK 0x0
|
||||||
#define COLOR_LIGHTGREEN 0xA
|
#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
|
* Kernel entry point
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
void kmain(BootInfo *Boot)
|
void kmain(BootInfo *Boot)
|
||||||
{
|
{
|
||||||
KeyEvent Key;
|
|
||||||
KSTATUS Status;
|
KSTATUS Status;
|
||||||
UINTN read_errors = 0;
|
Task *terminal_task = NULL;
|
||||||
|
StarlingContext *ctx = NULL;
|
||||||
|
|
||||||
if (Boot == NULL) {
|
if (Boot == NULL) {
|
||||||
return;
|
return;
|
||||||
@@ -84,61 +214,41 @@ void kmain(BootInfo *Boot)
|
|||||||
SAFE_PRINT(Boot, L"Available Services:\n\r");
|
SAFE_PRINT(Boot, L"Available Services:\n\r");
|
||||||
SAFE_PRINT(Boot, L" - Console Input/Output: %s\n\r",
|
SAFE_PRINT(Boot, L" - Console Input/Output: %s\n\r",
|
||||||
(Boot->read_key != NULL && Boot->print != NULL) ? L"Active" : L"Unavailable");
|
(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"\n\r");
|
||||||
SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r");
|
SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r");
|
||||||
|
|
||||||
/* ---- Interactive shell loop ---- */
|
/* ---- Spawn Starling Terminal as its own task ---- */
|
||||||
CHAR16 line[128];
|
ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext));
|
||||||
UINTN len = 0;
|
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) {
|
while (TRUE) {
|
||||||
/* Try non-blocking read first; yield to other tasks while idle */
|
task_yield();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
task.c
27
task.c
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* task.c – Cooperative multitasking: PCB pool, scheduler, yield/exit.
|
* 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
|
* Additional tasks are created with task_create(), which allocates a
|
||||||
* stack from the PMM and sets up a fake context-switch frame so that
|
* 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
|
* 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
|
* 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)
|
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
|
* 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
|
* allocate one. Its saved_rsp will be filled in during the
|
||||||
* first context_switch call in task_yield().
|
* first context_switch call in task_yield().
|
||||||
@@ -87,7 +87,7 @@ void task_init(BootInfo *Boot)
|
|||||||
tasks[0].pid = next_pid++;
|
tasks[0].pid = next_pid++;
|
||||||
tasks[0].state = TASK_STATE_RUNNING;
|
tasks[0].state = TASK_STATE_RUNNING;
|
||||||
tasks[0].switches = 1;
|
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];
|
current_task = &tasks[0];
|
||||||
task_ready = TRUE;
|
task_ready = TRUE;
|
||||||
@@ -329,6 +329,25 @@ UINTN task_count(void)
|
|||||||
return count;
|
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)
|
* Print task list (implements the `ps` command)
|
||||||
* ---------------------------------------------------------------- */
|
* ---------------------------------------------------------------- */
|
||||||
|
|||||||
3
task.h
3
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 */
|
UINTN task_count(void); /* number of non-FREE tasks */
|
||||||
void task_print_list(BootInfo *Boot); /* print task table (for `ps`) */
|
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). */
|
/* Assembly context switch (defined in context_switch.S). */
|
||||||
extern void context_switch(UINT64 *old_rsp, UINT64 new_rsp);
|
extern void context_switch(UINT64 *old_rsp, UINT64 new_rsp);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user