Added privilidges

This commit is contained in:
2026-02-27 21:04:56 +00:00
parent de161801c4
commit 08cb1db571
12 changed files with 395 additions and 94 deletions

View File

@@ -39,6 +39,7 @@ static void cmd_ps(BootInfo *Boot, CHAR16 *Args);
static void cmd_spawn(BootInfo *Boot, CHAR16 *Args); 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);
static void cmd_kusr(BootInfo *Boot, CHAR16 *Args);
/* Small helper struct used to pass arguments into per-command tasks. */ /* Small helper struct used to pass arguments into per-command tasks. */
typedef struct { typedef struct {
@@ -75,48 +76,56 @@ static Command commands[] = {
L"shutdown", L"shutdown",
L"Shutdown the system", L"Shutdown the system",
L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.", L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.",
TASK_PRIV_KERNEL,
cmd_shutdown cmd_shutdown
}, },
{ {
L"help", L"help",
L"Display available commands", L"Display available commands",
L"Usage: help\n\r Lists all available commands with brief descriptions.", L"Usage: help\n\r Lists all available commands with brief descriptions.",
TASK_PRIV_USER,
cmd_help cmd_help
}, },
{ {
L"man", L"man",
L"Display manual page for a command", L"Display manual page for a command",
L"Usage: man <command>\n\r Shows detailed help for the specified command.", L"Usage: man <command>\n\r Shows detailed help for the specified command.",
TASK_PRIV_USER,
cmd_man cmd_man
}, },
{ {
L"clear", L"clear",
L"Clear the screen", L"Clear the screen",
L"Usage: clear\n\r Clears the console screen.", L"Usage: clear\n\r Clears the console screen.",
TASK_PRIV_USER,
cmd_clear cmd_clear
}, },
{ {
L"about", L"about",
L"Display system information", L"Display system information",
L"Usage: about\n\r Shows information about this operating system.", L"Usage: about\n\r Shows information about this operating system.",
TASK_PRIV_USER,
cmd_about cmd_about
}, },
{ {
L"mem", L"mem",
L"Display memory statistics", L"Display memory statistics",
L"Usage: mem\n\r Shows physical memory, heap, and paging information.", L"Usage: mem\n\r Shows physical memory, heap, and paging information.",
TASK_PRIV_KERNEL,
cmd_mem cmd_mem
}, },
{ {
L"ps", L"ps",
L"List running tasks", L"List running tasks",
L"Usage: ps\n\r Displays all active tasks with PID, state, and name.", L"Usage: ps\n\r Displays all active tasks with PID, state, and name.",
TASK_PRIV_DRIVER,
cmd_ps cmd_ps
}, },
{ {
L"spawn", L"spawn",
L"Spawn a demo background task", L"Spawn a demo background task",
L"Usage: spawn [name]\n\r Creates a cooperative demo task.\n\r Optional argument sets the task name.", L"Usage: spawn [name]\n\r Creates a cooperative demo task.\n\r Optional argument sets the task name.",
TASK_PRIV_DRIVER,
cmd_spawn cmd_spawn
}, },
{ {
@@ -128,6 +137,7 @@ static Command commands[] = {
L" 2) Heap free and coalescing verification via kfree()\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" 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", L" 4) Multi-page (4-page) PMM allocate/free via pmm_alloc_pages()/pmm_free_pages()\n\r",
TASK_PRIV_KERNEL,
cmd_memtest cmd_memtest
}, },
{ {
@@ -138,9 +148,19 @@ static Command commands[] = {
L" each printing three progress steps and yielding between them.\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" After the workers finish, prints the final task list to\n\r"
L" demonstrate the cooperative round-robin scheduler.", L" demonstrate the cooperative round-robin scheduler.",
TASK_PRIV_DRIVER,
cmd_tasktest cmd_tasktest
}, },
{NULL, NULL, NULL, NULL} /* sentinel */ {
L"kusr",
L"Run a command with kernel privilege",
L"Usage: kusr <command> [args...]\n\r"
L" Temporarily elevates the current task to kernel privilege,\n\r"
L" executes the given command, then restores the original level.",
TASK_PRIV_USER,
cmd_kusr
},
{NULL, NULL, NULL, 0, NULL} /* sentinel */
}; };
/* ================================================================ /* ================================================================
@@ -149,10 +169,19 @@ static Command commands[] = {
static void request_shutdown(BootInfo *Boot) static void request_shutdown(BootInfo *Boot)
{ {
Task *caller;
if (Boot == NULL) { if (Boot == NULL) {
return; return;
} }
/* Subsystem-level privilege enforcement: shutdown requires KERNEL. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_KERNEL) {
SAFE_PRINT(Boot, L"Permission denied: shutdown requires kernel privilege.\n\r");
return;
}
if (Boot->shutdown != NULL) { if (Boot->shutdown != NULL) {
Boot->shutdown(); Boot->shutdown();
return; return;
@@ -310,8 +339,16 @@ static void cmd_memtest(BootInfo *Boot, CHAR16 *Args)
UINTN i; UINTN i;
UINT64 page; UINT64 page;
UINTN h_total, h_used, h_free, h_blocks; UINTN h_total, h_used, h_free, h_blocks;
Task *caller;
(void)Args; (void)Args;
/* Subsystem-level privilege enforcement: memtest requires KERNEL. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_KERNEL) {
SAFE_PRINT(Boot, L"Permission denied: memtest requires kernel privilege.\n\r");
return;
}
SAFE_PRINT(Boot, L"\n\r"); SAFE_PRINT(Boot, L"\n\r");
SAFE_PRINT(Boot, L"Memory Test\n\r"); SAFE_PRINT(Boot, L"Memory Test\n\r");
SAFE_PRINT(Boot, L"================================================\n\r"); SAFE_PRINT(Boot, L"================================================\n\r");
@@ -434,6 +471,40 @@ static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args)
SAFE_PRINT(Boot, L"Task scheduler test completed.\n\r\n\r"); SAFE_PRINT(Boot, L"Task scheduler test completed.\n\r\n\r");
} }
/* ----------------------------------------------------------------
* kusr run a command with escalated privilege
* ---------------------------------------------------------------- */
static void cmd_kusr(BootInfo *Boot, CHAR16 *Args)
{
Task *self;
TaskPrivilege saved_priv;
if (Args == NULL || Args[0] == L'\0') {
SAFE_PRINT(Boot, L"Usage: kusr <command> [args...]\n\r");
return;
}
self = task_current();
if (self == NULL) {
SAFE_PRINT(Boot, L"kusr: no task context available.\n\r");
return;
}
/* Elevate, dispatch, restore. */
saved_priv = task_get_privilege(self);
task_set_privilege(self, TASK_PRIV_KERNEL);
{
Task *cmd_task = execute_command(Boot, Args, TASK_PRIV_KERNEL);
if (cmd_task != NULL) {
task_wait(cmd_task);
}
}
task_set_privilege(self, saved_priv);
}
/* ================================================================ /* ================================================================
* Public API * Public API
* ================================================================ */ * ================================================================ */
@@ -490,7 +561,7 @@ void show_help(BootInfo *Boot)
* - Pointer to the spawned Task for the command, or NULL if the * - Pointer to the spawned Task for the command, or NULL if the
* command was not found or had to run synchronously. * command was not found or had to run synchronously.
*/ */
Task *execute_command(BootInfo *Boot, CHAR16 *Input) Task *execute_command(BootInfo *Boot, CHAR16 *Input, TaskPrivilege caller_priv)
{ {
CHAR16 *cmd_start = NULL; CHAR16 *cmd_start = NULL;
CHAR16 *args_start = NULL; CHAR16 *args_start = NULL;
@@ -543,7 +614,10 @@ Task *execute_command(BootInfo *Boot, CHAR16 *Input)
ctx->handler = commands[i].handler; ctx->handler = commands[i].handler;
wstrcpy16_local(ctx->args, args_start, sizeof(ctx->args) / sizeof(ctx->args[0])); wstrcpy16_local(ctx->args, args_start, sizeof(ctx->args) / sizeof(ctx->args[0]));
t = task_create(commands[i].name, command_task_entry, ctx); t = task_create_with_priv(commands[i].name,
command_task_entry,
ctx,
caller_priv);
if (t == NULL) { if (t == NULL) {
SAFE_PRINT(Boot, L"Failed to create task for command '%s'; running in core thread.\n\r", SAFE_PRINT(Boot, L"Failed to create task for command '%s'; running in core thread.\n\r",
commands[i].name); commands[i].name);

View File

@@ -22,9 +22,10 @@ typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args);
*/ */
typedef struct { typedef struct {
const CHAR16 *name; /* command keyword (e.g. L"help") */ const CHAR16 *name; /* command keyword (e.g. L"help") */
const CHAR16 *description; /* one-line summary for `help` */ const CHAR16 *description; /* one-line summary for `help` */
const CHAR16 *usage; /* detailed text shown by `man` */ const CHAR16 *usage; /* detailed text shown by `man` */
CommandHandlerFn handler; /* function that executes the cmd */ TaskPrivilege min_priv; /* minimum privilege required */
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.
@@ -33,7 +34,7 @@ typedef struct {
* - Pointer to a Task representing the spawned command process, * - Pointer to a Task representing the spawned command process,
* or NULL if the command was not found or ran synchronously. * or NULL if the command was not found or ran synchronously.
*/ */
Task *execute_command(BootInfo *Boot, CHAR16 *Input); Task *execute_command(BootInfo *Boot, CHAR16 *Input, TaskPrivilege caller_priv);
/* 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);

View File

@@ -3,7 +3,7 @@
User interaction with the kernel is mediated by the **Starling Terminal** and a command registry implemented in `kernel.c` and `commands.c`. User interaction with the kernel is mediated by the **Starling Terminal** and a command registry implemented in `kernel.c` and `commands.c`.
- The **Starling Terminal** is a task that runs a readevalprint loop, reading keystrokes via `BootInfo` services and dispatching commands. - The **Starling Terminal** is a task that runs a readevalprint loop, reading keystrokes via `BootInfo` services and dispatching commands.
- The **command subsystem** maintains a table of commands, each with a name, description, usage string, and handler function. - The **command subsystem** maintains a table of commands, each with a name, description, usage string, minimum privilege level, and handler function.
- Each command typically runs in its own task so that long-running work does not block the terminal. - Each command typically runs in its own task so that long-running work does not block the terminal.
--- ---
@@ -23,15 +23,18 @@ static void starling_terminal_task(void *arg)
CHAR16 line[128]; CHAR16 line[128];
UINTN len = 0; UINTN len = 0;
UINTN depth = 0; UINTN depth = 0;
TaskPrivilege shell_priv;
if (ctx == NULL || ctx->Boot == NULL) { if (ctx == NULL || ctx->Boot == NULL) {
return; return;
} }
Boot = ctx->Boot; Boot = ctx->Boot;
depth = ctx->depth; depth = ctx->depth;
shell_priv = ctx->shell_priv;
SAFE_PRINT(Boot, L"\n\r[Starling Terminal depth %d] ready.\n\r\n\r", depth); 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> "); SAFE_PRINT(Boot, L"starling> ");
while (TRUE) { while (TRUE) {
@@ -66,7 +69,7 @@ static void starling_terminal_task(void *arg)
trim_spaces_inplace(line); trim_spaces_inplace(line);
... ...
} else { } else {
Task *cmd_task = execute_command(Boot, line); Task *cmd_task = execute_command(Boot, line, shell_priv);
/* If a command task was spawned, wait for it to finish. */ /* If a command task was spawned, wait for it to finish. */
if (cmd_task != NULL) { if (cmd_task != NULL) {
@@ -117,16 +120,21 @@ ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext));
if (ctx == NULL) { if (ctx == NULL) {
SAFE_PRINT(Boot, L"Failed to allocate Starling Terminal context; starting inline.\n\r"); SAFE_PRINT(Boot, L"Failed to allocate Starling Terminal context; starting inline.\n\r");
StarlingContext inline_ctx; StarlingContext inline_ctx;
inline_ctx.Boot = Boot; inline_ctx.Boot = Boot;
inline_ctx.depth = 0; inline_ctx.depth = 0;
inline_ctx.shell_priv = TASK_PRIV_USER;
starling_terminal_task(&inline_ctx); starling_terminal_task(&inline_ctx);
return; return;
} }
ctx->Boot = Boot; ctx->Boot = Boot;
ctx->depth = 0; ctx->depth = 0;
ctx->shell_priv = TASK_PRIV_USER;
terminal_task = task_create(L"starling-term", starling_terminal_task, ctx); terminal_task = task_create_with_priv(L"starling-term",
starling_terminal_task,
ctx,
TASK_PRIV_USER);
if (terminal_task == NULL) { if (terminal_task == NULL) {
SAFE_PRINT(Boot, L"Failed to start Starling Terminal task; falling back to kernel loop.\n\r"); SAFE_PRINT(Boot, L"Failed to start Starling Terminal task; falling back to kernel loop.\n\r");
... ...
@@ -153,69 +161,88 @@ This ensures that:
The command registry is defined in `commands.c` as a static array: The command registry is defined in `commands.c` as a static array:
```73:144:/home/lochlan/Documents/Coding/c/os/commands.c ```73:164:/home/lochlan/Documents/Coding/c/os/commands.c
static Command commands[] = { static Command commands[] = {
{ {
L"shutdown", L"shutdown",
L"Shutdown the system", L"Shutdown the system",
L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.", L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.",
TASK_PRIV_KERNEL,
cmd_shutdown cmd_shutdown
}, },
{ {
L"help", L"help",
L"Display available commands", L"Display available commands",
L"Usage: help\n\r Lists all available commands with brief descriptions.", L"Usage: help\n\r Lists all available commands with brief descriptions.",
TASK_PRIV_USER,
cmd_help cmd_help
}, },
{ {
L"man", L"man",
L"Display manual page for a command", L"Display manual page for a command",
L"Usage: man <command>\n\r Shows detailed help for the specified command.", L"Usage: man <command>\n\r Shows detailed help for the specified command.",
TASK_PRIV_USER,
cmd_man cmd_man
}, },
{ {
L"clear", L"clear",
L"Clear the screen", L"Clear the screen",
L"Usage: clear\n\r Clears the console screen.", L"Usage: clear\n\r Clears the console screen.",
TASK_PRIV_USER,
cmd_clear cmd_clear
}, },
{ {
L"about", L"about",
L"Display system information", L"Display system information",
L"Usage: about\n\r Shows information about this operating system.", L"Usage: about\n\r Shows information about this operating system.",
TASK_PRIV_USER,
cmd_about cmd_about
}, },
{ {
L"mem", L"mem",
L"Display memory statistics", L"Display memory statistics",
L"Usage: mem\n\r Shows physical memory, heap, and paging information.", L"Usage: mem\n\r Shows physical memory, heap, and paging information.",
TASK_PRIV_KERNEL,
cmd_mem cmd_mem
}, },
{ {
L"ps", L"ps",
L"List running tasks", L"List running tasks",
L"Usage: ps\n\r Displays all active tasks with PID, state, and name.", L"Usage: ps\n\r Displays all active tasks with PID, state, and name.",
TASK_PRIV_DRIVER,
cmd_ps cmd_ps
}, },
{ {
L"spawn", L"spawn",
L"Spawn a demo background task", L"Spawn a demo background task",
L"Usage: spawn [name]\n\r Creates a cooperative demo task.\n\r Optional argument sets the task name.", L"Usage: spawn [name]\n\r Creates a cooperative demo task.\n\r Optional argument sets the task name.",
TASK_PRIV_DRIVER,
cmd_spawn cmd_spawn
}, },
{ {
L"memtest", L"memtest",
L"Test memory allocation and deallocation", L"Test memory allocation and deallocation",
... ...
TASK_PRIV_KERNEL,
cmd_memtest cmd_memtest
}, },
{ {
L"tasktest", L"tasktest",
L"Test task scheduler with multiple tasks", L"Test task scheduler with multiple tasks",
... ...
TASK_PRIV_DRIVER,
cmd_tasktest cmd_tasktest
}, },
{NULL, NULL, NULL, NULL} /* sentinel */ {
L"kusr",
L"Run a command with kernel privilege",
L"Usage: kusr <command> [args...]\n\r"
L" Temporarily elevates the current task to kernel privilege,\n\r"
L" executes the given command, then restores the original level.",
TASK_PRIV_USER,
cmd_kusr
},
{NULL, NULL, NULL, 0, NULL} /* sentinel */
}; };
``` ```
@@ -224,6 +251,7 @@ Each `Command` entry includes:
- `name` the token typed at the prompt. - `name` the token typed at the prompt.
- `description` a short summary used by `help`. - `description` a short summary used by `help`.
- `usage` a longer description and usage details for `man`. - `usage` a longer description and usage details for `man`.
- `min_priv` the minimum `TaskPrivilege` required to run the command (see `task.h`).
- `handler` a function of type: - `handler` a function of type:
```14:19:/home/lochlan/Documents/Coding/c/os/commands.h ```14:19:/home/lochlan/Documents/Coding/c/os/commands.h
@@ -246,7 +274,7 @@ To add a new command, follow the guide in the file header:
The central function that processes a line of user input is `execute_command`: The central function that processes a line of user input is `execute_command`:
```493:557:/home/lochlan/Documents/Coding/c/os/commands.c ```493:557:/home/lochlan/Documents/Coding/c/os/commands.c
Task *execute_command(BootInfo *Boot, CHAR16 *Input) Task *execute_command(BootInfo *Boot, CHAR16 *Input, TaskPrivilege caller_priv)
{ {
CHAR16 *cmd_start = NULL; CHAR16 *cmd_start = NULL;
CHAR16 *args_start = NULL; CHAR16 *args_start = NULL;
@@ -299,7 +327,10 @@ Task *execute_command(BootInfo *Boot, CHAR16 *Input)
ctx->handler = commands[i].handler; ctx->handler = commands[i].handler;
wstrcpy16_local(ctx->args, args_start, sizeof(ctx->args) / sizeof(ctx->args[0])); wstrcpy16_local(ctx->args, args_start, sizeof(ctx->args) / sizeof(ctx->args[0]));
t = task_create(commands[i].name, command_task_entry, ctx); t = task_create_with_priv(commands[i].name,
command_task_entry,
ctx,
caller_priv);
if (t == NULL) { if (t == NULL) {
SAFE_PRINT(Boot, L"Failed to create task for command '%s'; running in core thread.\n\r", SAFE_PRINT(Boot, L"Failed to create task for command '%s'; running in core thread.\n\r",
commands[i].name); commands[i].name);
@@ -336,10 +367,11 @@ Pipeline stages:
- `Boot` pointer. - `Boot` pointer.
- Handler function. - Handler function.
- A bounded copy of the argument string. - A bounded copy of the argument string.
- A new task is created via `task_create` with: - A new task is created via `task_create_with_priv` with:
- Task name = command name. - Task name = command name.
- Entry = `command_task_entry`. - Entry = `command_task_entry`.
- Argument = pointer to the context. - Argument = pointer to the context.
- Privilege = `caller_priv` (inherited from the calling shell).
- If task creation fails, the command handler is run synchronously in the current thread as a fallback. - If task creation fails, the command handler is run synchronously in the current thread as a fallback.
The terminal then optionally `task_wait`s on the returned `Task *`, serialising command execution from the user's perspective while still letting the scheduler run other tasks (e.g., background demos). The terminal then optionally `task_wait`s on the returned `Task *`, serialising command execution from the user's perspective while still letting the scheduler run other tasks (e.g., background demos).
@@ -396,19 +428,21 @@ This design gives each command:
Some notable built-in handlers: Some notable built-in handlers:
- **System control**: - **System control**:
- `shutdown` → `cmd_shutdown` calls `Boot->shutdown` via `request_shutdown` to power off the machine. - `shutdown` (KERNEL) → `cmd_shutdown` calls `Boot->shutdown` via `request_shutdown` to power off the machine. `request_shutdown` enforces kernel privilege.
- `clear` → `cmd_clear` uses `Boot->clear_screen` to wipe the console. - `clear` (USER) → `cmd_clear` uses `Boot->clear_screen` to wipe the console.
- **Information and help**: - **Information and help**:
- `help` → `cmd_help` calls `show_help` to print a formatted table of available commands. - `help` (USER) → `cmd_help` calls `show_help` to print a formatted table of available commands.
- `man` → `cmd_man` prints the `usage` field for a specific command. - `man` (USER) → `cmd_man` prints the `usage` field for a specific command.
- `about` → `cmd_about` prints OS information and feature list. - `about` (USER) → `cmd_about` prints OS information and feature list.
- **Diagnostics**: - **Diagnostics**:
- `mem` → `cmd_mem` calls `memory_print_stats` to show PMM and heap state. - `mem` (KERNEL) → `cmd_mem` calls `memory_print_stats` to show PMM and heap state. The callee enforces kernel privilege.
- `ps` → `cmd_ps` calls `task_print_list` to show current tasks. - `ps` (DRIVER) → `cmd_ps` calls `task_print_list` to show current tasks. The callee enforces driver privilege.
- `memtest` → `cmd_memtest` exercises heap and PMM allocations. - `memtest` (KERNEL) → `cmd_memtest` exercises heap and PMM allocations. The handler enforces kernel privilege.
- `tasktest` → `cmd_tasktest` spawns multiple worker tasks to demonstrate cooperative scheduling. - `tasktest` (DRIVER) → `cmd_tasktest` spawns multiple worker tasks to demonstrate cooperative scheduling.
- **Tasking demo**: - **Tasking demo**:
- `spawn` → `cmd_spawn` creates a demonstration task using `demo_task_fn`, which yields in a loop and reports progress. - `spawn` (DRIVER) → `cmd_spawn` creates a demonstration task using `demo_task_fn`, which yields in a loop and reports progress.
- **Privilege escalation**:
- `kusr` (USER) → `cmd_kusr` temporarily elevates the calling task to `TASK_PRIV_KERNEL`, dispatches the given sub-command, then restores the original privilege level.
Examples: Examples:
@@ -441,11 +475,12 @@ To add a new command `foo`:
1. **Declare the handler** near the top of `commands.c`: 1. **Declare the handler** near the top of `commands.c`:
```32:41:/home/lochlan/Documents/Coding/c/os/commands.c ```32:42:/home/lochlan/Documents/Coding/c/os/commands.c
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args); static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args);
static void cmd_help(BootInfo *Boot, CHAR16 *Args); static void cmd_help(BootInfo *Boot, CHAR16 *Args);
... ...
static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args); static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args);
static void cmd_kusr(BootInfo *Boot, CHAR16 *Args);
/* Add: */ /* Add: */
static void cmd_foo(BootInfo *Boot, CHAR16 *Args); static void cmd_foo(BootInfo *Boot, CHAR16 *Args);
``` ```
@@ -465,13 +500,14 @@ Use this as a template for `cmd_foo`, replacing the body with your logic and usi
3. **Register the command** in `commands[]` before the sentinel: 3. **Register the command** in `commands[]` before the sentinel:
```140:143:/home/lochlan/Documents/Coding/c/os/commands.c ```140:164:/home/lochlan/Documents/Coding/c/os/commands.c
{ {
L"tasktest", L"kusr",
... ...
cmd_tasktest TASK_PRIV_USER,
cmd_kusr
}, },
{NULL, NULL, NULL, NULL} /* sentinel */ {NULL, NULL, NULL, 0, NULL} /* sentinel */
``` ```
Insert a new block above the sentinel: Insert a new block above the sentinel:
@@ -481,6 +517,7 @@ Insert a new block above the sentinel:
L"foo", L"foo",
L"One-line description", L"One-line description",
L"Usage: foo [args]\n\r Detailed explanation...", L"Usage: foo [args]\n\r Detailed explanation...",
TASK_PRIV_USER, /* minimum privilege required */
cmd_foo cmd_foo
}, },
``` ```

View File

@@ -254,7 +254,7 @@ This approach preserves any firmware-installed handlers for higher interrupt vec
The IDT/ISR subsystem interacts with other parts of the kernel in the following ways: The IDT/ISR subsystem interacts with other parts of the kernel in the following ways:
- **BootInfo access**: - **BootInfo access**:
- `id_tinit` stores `Boot` in `gBoot` so that `isr_handler` can safely use `Boot->print` for diagnostics. - `idt_init` stores `Boot` in `gBoot` so that `isr_handler` can safely use `Boot->print` for diagnostics.
- **Memory subsystem**: - **Memory subsystem**:
- `isr_handler` reads CR2 for page faults; combined with `paging_get_phys` from `memory.c`, this can be used to inspect paging state. - `isr_handler` reads CR2 for page faults; combined with `paging_get_phys` from `memory.c`, this can be used to inspect paging state.
- **Tasks and scheduler**: - **Tasks and scheduler**:

View File

@@ -565,13 +565,21 @@ These statistics are surfaced to the user via the `mem` and `memtest` commands.
## Runtime memory diagnostics (`mem` and `memtest`) ## Runtime memory diagnostics (`mem` and `memtest`)
The `mem` command (in `commands.c`) prints a snapshot of PMM and heap state by calling `memory_print_stats`: The `mem` command (in `commands.c`) prints a snapshot of PMM and heap state by calling `memory_print_stats`. Access requires `TASK_PRIV_KERNEL`:
```525:562:/home/lochlan/Documents/Coding/c/os/memory.c ```525:572:/home/lochlan/Documents/Coding/c/os/memory.c
void memory_print_stats(BootInfo *Boot) void memory_print_stats(BootInfo *Boot)
{ {
UINTN h_total, h_used, h_free, h_blocks; UINTN h_total, h_used, h_free, h_blocks;
UINTN p_total, p_free, p_used; UINTN p_total, p_free, p_used;
Task *caller;
/* Subsystem-level privilege enforcement: memory stats require KERNEL. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_KERNEL) {
SAFE_PRINT(Boot, L"Permission denied: memory stats require kernel privilege.\n\r");
return;
}
p_total = pmm_get_total_pages(); p_total = pmm_get_total_pages();
p_free = pmm_get_free_pages(); p_free = pmm_get_free_pages();
@@ -590,7 +598,7 @@ void memory_print_stats(BootInfo *Boot)
} }
``` ```
The `memtest` command runs a scripted set of tests that exercise heap allocation, heap free/coalescing, and PMM single- and multi-page allocation: The `memtest` command runs a scripted set of tests that exercise heap allocation, heap free/coalescing, and PMM single- and multi-page allocation. It also enforces `TASK_PRIV_KERNEL`:
```306:379:/home/lochlan/Documents/Coding/c/os/commands.c ```306:379:/home/lochlan/Documents/Coding/c/os/commands.c
static void cmd_memtest(BootInfo *Boot, CHAR16 *Args) static void cmd_memtest(BootInfo *Boot, CHAR16 *Args)
@@ -600,8 +608,16 @@ static void cmd_memtest(BootInfo *Boot, CHAR16 *Args)
UINTN i; UINTN i;
UINT64 page; UINT64 page;
UINTN h_total, h_used, h_free, h_blocks; UINTN h_total, h_used, h_free, h_blocks;
Task *caller;
(void)Args; (void)Args;
/* Subsystem-level privilege enforcement: memtest requires KERNEL. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_KERNEL) {
SAFE_PRINT(Boot, L"Permission denied: memtest requires kernel privilege.\n\r");
return;
}
SAFE_PRINT(Boot, L"\n\r"); SAFE_PRINT(Boot, L"\n\r");
SAFE_PRINT(Boot, L"Memory Test\n\r"); SAFE_PRINT(Boot, L"Memory Test\n\r");
SAFE_PRINT(Boot, L"================================================\n\r"); SAFE_PRINT(Boot, L"================================================\n\r");

View File

@@ -157,7 +157,14 @@ void kmain(BootInfo *Boot)
/* ---- Spawn Starling Terminal as its own task ---- */ /* ---- Spawn Starling Terminal as its own task ---- */
ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext)); ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext));
... ...
terminal_task = task_create(L"starling-term", starling_terminal_task, ctx); 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) { if (terminal_task == NULL) {
... ...
starling_terminal_task(Boot); starling_terminal_task(Boot);
@@ -180,7 +187,7 @@ void kmain(BootInfo *Boot)
- `idt_init(Boot)` to install the kernel's Interrupt Descriptor Table and exception handlers. - `idt_init(Boot)` to install the kernel's Interrupt Descriptor Table and exception handlers.
- `memory_init(Boot)` to bring up the physical allocator, paging helpers, and heap. - `memory_init(Boot)` to bring up the physical allocator, paging helpers, and heap.
- `task_init(Boot)` to bootstrap the cooperative scheduler and register the current thread as task 0. - `task_init(Boot)` to bootstrap the cooperative scheduler and register the current thread as task 0.
3. **User interface** prints a banner and spawns the Starling Terminal as a separate task via `task_create`, then turns the core thread into an idle loop that continuously `task_yield`s to allow other tasks to run. 3. **User interface** prints a banner and spawns the Starling Terminal as a separate task via `task_create_with_priv` with `TASK_PRIV_USER` privilege, then turns the core thread into an idle loop that continuously `task_yield`s to allow other tasks to run.
At this point, the system has: At this point, the system has:
@@ -206,10 +213,12 @@ static void starling_terminal_task(void *arg)
return; return;
} }
Boot = ctx->Boot; Boot = ctx->Boot;
depth = ctx->depth; depth = ctx->depth;
shell_priv = ctx->shell_priv;
SAFE_PRINT(Boot, L"\n\r[Starling Terminal depth %d] ready.\n\r\n\r", depth); 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> "); SAFE_PRINT(Boot, L"starling> ");
while (TRUE) { while (TRUE) {
@@ -234,7 +243,7 @@ static void starling_terminal_task(void *arg)
trim_spaces_inplace(line); trim_spaces_inplace(line);
... ...
} else { } else {
Task *cmd_task = execute_command(Boot, line); Task *cmd_task = execute_command(Boot, line, shell_priv);
/* If a command task was spawned, wait for it to finish. */ /* If a command task was spawned, wait for it to finish. */
if (cmd_task != NULL) { if (cmd_task != NULL) {
@@ -258,7 +267,7 @@ Key points:
- **Non-blocking idle**: when `try_read_key` returns no key, the terminal calls `task_yield()` so other tasks can run while the user is idle. - **Non-blocking idle**: when `try_read_key` returns no key, the terminal calls `task_yield()` so other tasks can run while the user is idle.
- **Line editing**: handles printable ASCII and backspace to maintain a simple line buffer (`line[128]`). - **Line editing**: handles printable ASCII and backspace to maintain a simple line buffer (`line[128]`).
- **Command execution**: on Enter, the line is trimmed and passed to `execute_command(Boot, line)` in `commands.c`. If that function spawns a dedicated command task, the terminal waits for it via `task_wait`. - **Command execution**: on Enter, the line is trimmed and passed to `execute_command(Boot, line, shell_priv)` in `commands.c`, propagating the shell's privilege level. If that function spawns a dedicated command task, the terminal waits for it via `task_wait`.
- **Nested terminals**: entering `starling` recursively spawns another Starling Terminal task with increased `depth`, demonstrating multi-level shells. - **Nested terminals**: entering `starling` recursively spawns another Starling Terminal task with increased `depth`, demonstrating multi-level shells.
The command registry and dispatch path are documented in detail in `commands-and-terminal.md`. The command registry and dispatch path are documented in detail in `commands-and-terminal.md`.
@@ -278,15 +287,17 @@ The kernel is organised into focused subsystems, each in its own translation uni
- **Tasks and scheduler** (`task.c` + `task.h`): - **Tasks and scheduler** (`task.c` + `task.h`):
- Static process control block (PCB) pool. - Static process control block (PCB) pool.
- Cooperative round-robin scheduler. - Cooperative round-robin scheduler.
- Software privilege levels (`TASK_PRIV_USER`, `TASK_PRIV_DRIVER`, `TASK_PRIV_KERNEL`) for access control.
- Stack management and context switch support (via an external `context_switch` assembly routine). - Stack management and context switch support (via an external `context_switch` assembly routine).
- **Interrupts and exceptions** (`idt.c` + `idt.h`): - **Interrupts and exceptions** (`idt.c` + `idt.h`):
- IDT mirroring of firmware entries. - IDT mirroring of firmware entries.
- Replacement of CPU exception vectors 031 with kernel stubs. - Replacement of CPU exception vectors 031 with kernel stubs.
- Central `isr_handler` that prints diagnostics and halts on unrecoverable faults. - Central `isr_handler` that prints diagnostics and halts on unrecoverable faults.
- **Commands and shell** (`commands.c` + `commands.h`): - **Commands and shell** (`commands.c` + `commands.h`):
- Command registry and help/man system. - Command registry with per-command minimum privilege levels and help/man system.
- System control commands (`shutdown`, `about`, `mem`, `ps`). - System control commands (`shutdown`, `about`, `mem`, `ps`).
- Test commands (`memtest`, `tasktest`, `spawn`) that exercise memory and scheduler subsystems in isolation. - Test commands (`memtest`, `tasktest`, `spawn`) that exercise memory and scheduler subsystems in isolation.
- Privilege escalation command (`kusr`) for running commands with elevated privilege.
Each of these subsystems is covered in a dedicated document: Each of these subsystems is covered in a dedicated document:

View File

@@ -36,6 +36,7 @@ void task_init(BootInfo *Boot)
for (i = 0; i < TASK_MAX; i++) { for (i = 0; i < TASK_MAX; i++) {
tasks[i].state = TASK_STATE_FREE; tasks[i].state = TASK_STATE_FREE;
tasks[i].pid = 0; tasks[i].pid = 0;
tasks[i].privilege = TASK_PRIV_USER;
tasks[i].saved_rsp = 0; tasks[i].saved_rsp = 0;
tasks[i].stack_base = 0; tasks[i].stack_base = 0;
tasks[i].stack_pages = 0; tasks[i].stack_pages = 0;
@@ -51,9 +52,10 @@ void task_init(BootInfo *Boot)
* 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().
*/ */
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].privilege = TASK_PRIV_KERNEL;
tasks[0].switches = 1;
wstrcpy16(tasks[0].name, L"core", TASK_NAME_LEN); wstrcpy16(tasks[0].name, L"core", TASK_NAME_LEN);
current_task = &tasks[0]; current_task = &tasks[0];
@@ -66,22 +68,26 @@ void task_init(BootInfo *Boot)
Important points: Important points:
- Task 0 represents the **kernel core thread**, which uses the boot-time stack provided by the loader. - Task 0 represents the **kernel core thread**, which uses the boot-time stack provided by the loader. It receives `TASK_PRIV_KERNEL` privilege.
- No stack is allocated for task 0; its `saved_rsp` is populated the first time a context switch occurs. - No stack is allocated for task 0; its `saved_rsp` is populated the first time a context switch occurs.
- All other PCBs begin in `TASK_STATE_FREE`. - All other PCBs begin in `TASK_STATE_FREE` with `TASK_PRIV_USER`.
--- ---
## Task creation and stack layout ## Task creation and stack layout
New tasks are created via `task_create`, which: New tasks are created via `task_create_with_priv` (or its wrapper `task_create`), which:
1. Finds a free PCB slot. 1. Checks that the caller is not escalating privilege beyond its own level.
2. Allocates a stack from the PMM. 2. Finds a free PCB slot.
3. Sets up an initial stack frame so that `context_switch` can "return" into a C trampoline function. 3. Allocates a stack from the PMM.
4. Sets up an initial stack frame so that `context_switch` can "return" into a C trampoline function.
```121:197:/home/lochlan/Documents/Coding/c/os/task.c ```121:211:/home/lochlan/Documents/Coding/c/os/task.c
Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg) Task *task_create_with_priv(const CHAR16 *name,
TaskEntryFn entry,
void *arg,
TaskPrivilege privilege)
{ {
Task *t = NULL; Task *t = NULL;
UINTN i; UINTN i;
@@ -89,6 +95,14 @@ Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
UINT64 *sp; UINT64 *sp;
... ...
/* Subsystem-level privilege enforcement: prevent privilege escalation. */
{
Task *caller = task_current();
if (caller != NULL && privilege > task_get_privilege(caller)) {
return NULL;
}
}
/* Find a free PCB slot */ /* Find a free PCB slot */
for (i = 0; i < TASK_MAX; i++) { for (i = 0; i < TASK_MAX; i++) {
if (tasks[i].state == TASK_STATE_FREE) { if (tasks[i].state == TASK_STATE_FREE) {
@@ -107,6 +121,7 @@ Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
/* Fill in the PCB */ /* Fill in the PCB */
t->pid = next_pid++; t->pid = next_pid++;
t->state = TASK_STATE_READY; t->state = TASK_STATE_READY;
t->privilege = privilege;
t->entry = entry; t->entry = entry;
t->arg = arg; t->arg = arg;
t->switches = 0; t->switches = 0;
@@ -154,6 +169,19 @@ Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
} }
``` ```
The convenience wrapper `task_create` inherits the calling task's privilege level:
```213:220:/home/lochlan/Documents/Coding/c/os/task.c
Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
{
/* Inherit privilege from the calling task (kernel if no task context). */
Task *caller = task_current();
TaskPrivilege priv = (caller != NULL) ? task_get_privilege(caller)
: TASK_PRIV_KERNEL;
return task_create_with_priv(name, entry, arg, priv);
}
```
The effective stack layout (low to high addresses) after `task_create` is: The effective stack layout (low to high addresses) after `task_create` is:
- Saved `flags`, `r15`, `r14`, `r13`, `r12`, `rbx`, `rbp` (pushed by `context_switch` semantics). - Saved `flags`, `r15`, `r14`, `r13`, `r12`, `rbx`, `rbp` (pushed by `context_switch` semantics).
@@ -367,7 +395,7 @@ Because the scheduler is cooperative, this **busy-wait** loop is benign: it yiel
Example usage from the Starling Terminal: Example usage from the Starling Terminal:
```135:140:/home/lochlan/Documents/Coding/c/os/kernel.c ```135:140:/home/lochlan/Documents/Coding/c/os/kernel.c
Task *cmd_task = execute_command(Boot, line); Task *cmd_task = execute_command(Boot, line, shell_priv);
/* If a command task was spawned, wait for it to finish. */ /* If a command task was spawned, wait for it to finish. */
if (cmd_task != NULL) { if (cmd_task != NULL) {
@@ -379,25 +407,34 @@ if (cmd_task != NULL) {
## Task inspection (`ps` and `tasktest`) ## Task inspection (`ps` and `tasktest`)
The `ps` command uses `task_print_list` to show current tasks: The `ps` command uses `task_print_list` to show current tasks. Access requires at least `TASK_PRIV_DRIVER`:
```366:389:/home/lochlan/Documents/Coding/c/os/task.c ```407:439:/home/lochlan/Documents/Coding/c/os/task.c
void task_print_list(BootInfo *Boot) void task_print_list(BootInfo *Boot)
{ {
UINTN i; UINTN i;
Task *caller;
/* Subsystem-level privilege enforcement: task list requires DRIVER. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_DRIVER) {
SAFE_PRINT(Boot, L"Permission denied: task list requires driver privilege.\n\r");
return;
}
SAFE_PRINT(Boot, L"\n\r"); SAFE_PRINT(Boot, L"\n\r");
SAFE_PRINT(Boot, L" PID STATE SWITCHES NAME\n\r"); SAFE_PRINT(Boot, L" PID STATE PRIV SWITCHES NAME\n\r");
SAFE_PRINT(Boot, L" --- ---------- -------- ----\n\r"); SAFE_PRINT(Boot, L" --- ---------- ---- -------- ----\n\r");
for (i = 0; i < TASK_MAX; i++) { for (i = 0; i < TASK_MAX; i++) {
if (tasks[i].state == TASK_STATE_FREE) { if (tasks[i].state == TASK_STATE_FREE) {
continue; continue;
} }
SAFE_PRINT(Boot, L" %3d %-10s %8d %s\n\r", SAFE_PRINT(Boot, L" %3d %-10s %4d %8d %s\n\r",
tasks[i].pid, tasks[i].pid,
state_str(tasks[i].state), state_str(tasks[i].state),
(INT32)tasks[i].privilege,
tasks[i].switches, tasks[i].switches,
tasks[i].name); tasks[i].name);
} }
@@ -444,3 +481,28 @@ Each worker task:
This demonstrates how cooperative tasks interleave output and how `task_yield` drives scheduling. This demonstrates how cooperative tasks interleave output and how `task_yield` drives scheduling.
---
## Privilege system
Each task carries a `TaskPrivilege` level defined in `task.h`:
```47:52:/home/lochlan/Documents/Coding/c/os/task.h
typedef enum {
TASK_PRIV_USER = 0,
TASK_PRIV_DRIVER = 1,
TASK_PRIV_KERNEL = 2,
} TaskPrivilege;
```
All tasks still execute in CPU ring 0; this is a **software-only** hierarchy used for access control decisions:
- `task_create_with_priv` prevents a caller from creating a task with a higher privilege than its own.
- Subsystem functions like `memory_print_stats`, `task_print_list`, and `request_shutdown` check the calling task's privilege before proceeding.
- The `kusr` command (`commands.c`) temporarily elevates a task to `TASK_PRIV_KERNEL` to run a privileged sub-command, then restores the original level.
Accessors:
- `task_get_privilege(Task *t)` returns the task's current privilege level.
- `task_set_privilege(Task *t, TaskPrivilege p)` changes it (no enforcement; callers are responsible).

View File

@@ -36,8 +36,9 @@
/* Simple context passed to each Starling Terminal instance. */ /* Simple context passed to each Starling Terminal instance. */
typedef struct { typedef struct {
BootInfo *Boot; BootInfo *Boot;
UINTN depth; /* 0 = top-level, 1+ = nested shells */ UINTN depth; /* 0 = top-level, 1+ = nested shells */
TaskPrivilege shell_priv; /* logical privilege for this shell */
} StarlingContext; } StarlingContext;
/* ================================================================ /* ================================================================
@@ -54,15 +55,18 @@ static void starling_terminal_task(void *arg)
CHAR16 line[128]; CHAR16 line[128];
UINTN len = 0; UINTN len = 0;
UINTN depth = 0; UINTN depth = 0;
TaskPrivilege shell_priv;
if (ctx == NULL || ctx->Boot == NULL) { if (ctx == NULL || ctx->Boot == NULL) {
return; return;
} }
Boot = ctx->Boot; Boot = ctx->Boot;
depth = ctx->depth; depth = ctx->depth;
shell_priv = ctx->shell_priv;
SAFE_PRINT(Boot, L"\n\r[Starling Terminal depth %d] ready.\n\r\n\r", depth); 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> "); SAFE_PRINT(Boot, L"starling> ");
while (TRUE) { while (TRUE) {
@@ -114,10 +118,14 @@ static void starling_terminal_task(void *arg)
SAFE_PRINT(Boot, L"Starling: failed to allocate nested terminal context.\n\r"); SAFE_PRINT(Boot, L"Starling: failed to allocate nested terminal context.\n\r");
SAFE_PRINT(Boot, L"starling> "); SAFE_PRINT(Boot, L"starling> ");
} else { } else {
child_ctx->Boot = Boot; child_ctx->Boot = Boot;
child_ctx->depth = depth + 1; child_ctx->depth = depth + 1;
child_ctx->shell_priv = shell_priv;
child_task = task_create(L"starling-term", starling_terminal_task, child_ctx); child_task = task_create_with_priv(L"starling-term",
starling_terminal_task,
child_ctx,
shell_priv);
if (child_task == NULL) { if (child_task == NULL) {
SAFE_PRINT(Boot, L"Starling: failed to spawn nested terminal.\n\r"); SAFE_PRINT(Boot, L"Starling: failed to spawn nested terminal.\n\r");
kfree(child_ctx); kfree(child_ctx);
@@ -132,7 +140,7 @@ static void starling_terminal_task(void *arg)
} }
} }
} else { } else {
Task *cmd_task = execute_command(Boot, line); Task *cmd_task = execute_command(Boot, line, shell_priv);
/* If a command task was spawned, wait for it to finish. */ /* If a command task was spawned, wait for it to finish. */
if (cmd_task != NULL) { if (cmd_task != NULL) {
@@ -223,16 +231,21 @@ void kmain(BootInfo *Boot)
if (ctx == NULL) { if (ctx == NULL) {
SAFE_PRINT(Boot, L"Failed to allocate Starling Terminal context; starting inline.\n\r"); SAFE_PRINT(Boot, L"Failed to allocate Starling Terminal context; starting inline.\n\r");
StarlingContext inline_ctx; StarlingContext inline_ctx;
inline_ctx.Boot = Boot; inline_ctx.Boot = Boot;
inline_ctx.depth = 0; inline_ctx.depth = 0;
inline_ctx.shell_priv = TASK_PRIV_USER;
starling_terminal_task(&inline_ctx); starling_terminal_task(&inline_ctx);
return; return;
} }
ctx->Boot = Boot; ctx->Boot = Boot;
ctx->depth = 0; ctx->depth = 0;
ctx->shell_priv = TASK_PRIV_USER;
terminal_task = task_create(L"starling-term", starling_terminal_task, ctx); terminal_task = task_create_with_priv(L"starling-term",
starling_terminal_task,
ctx,
TASK_PRIV_USER);
if (terminal_task == NULL) { if (terminal_task == NULL) {
SAFE_PRINT(Boot, L"Failed to start Starling Terminal task; falling back to kernel loop.\n\r"); SAFE_PRINT(Boot, L"Failed to start Starling Terminal task; falling back to kernel loop.\n\r");

View File

@@ -18,6 +18,8 @@ typedef uint16_t UINT16;
typedef uint32_t UINT32; typedef uint32_t UINT32;
typedef uint64_t UINT64; typedef uint64_t UINT64;
typedef int32_t INT32;
typedef size_t UINTN; typedef size_t UINTN;
#ifndef BOOLEAN #ifndef BOOLEAN

View File

@@ -11,6 +11,7 @@
*/ */
#include "memory.h" #include "memory.h"
#include "task.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, ...) \
@@ -526,6 +527,14 @@ void memory_print_stats(BootInfo *Boot)
{ {
UINTN h_total, h_used, h_free, h_blocks; UINTN h_total, h_used, h_free, h_blocks;
UINTN p_total, p_free, p_used; UINTN p_total, p_free, p_used;
Task *caller;
/* Subsystem-level privilege enforcement: memory stats require KERNEL. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_KERNEL) {
SAFE_PRINT(Boot, L"Permission denied: memory stats require kernel privilege.\n\r");
return;
}
p_total = pmm_get_total_pages(); p_total = pmm_get_total_pages();
p_free = pmm_get_free_pages(); p_free = pmm_get_free_pages();

62
task.c
View File

@@ -69,6 +69,7 @@ void task_init(BootInfo *Boot)
for (i = 0; i < TASK_MAX; i++) { for (i = 0; i < TASK_MAX; i++) {
tasks[i].state = TASK_STATE_FREE; tasks[i].state = TASK_STATE_FREE;
tasks[i].pid = 0; tasks[i].pid = 0;
tasks[i].privilege = TASK_PRIV_USER;
tasks[i].saved_rsp = 0; tasks[i].saved_rsp = 0;
tasks[i].stack_base = 0; tasks[i].stack_base = 0;
tasks[i].stack_pages = 0; tasks[i].stack_pages = 0;
@@ -84,9 +85,10 @@ void task_init(BootInfo *Boot)
* 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().
*/ */
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].privilege = TASK_PRIV_KERNEL;
tasks[0].switches = 1;
wstrcpy16(tasks[0].name, L"core", TASK_NAME_LEN); wstrcpy16(tasks[0].name, L"core", TASK_NAME_LEN);
current_task = &tasks[0]; current_task = &tasks[0];
@@ -119,7 +121,10 @@ static void task_trampoline(void)
* Create a new task * Create a new task
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg) Task *task_create_with_priv(const CHAR16 *name,
TaskEntryFn entry,
void *arg,
TaskPrivilege privilege)
{ {
Task *t = NULL; Task *t = NULL;
UINTN i; UINTN i;
@@ -130,6 +135,14 @@ Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
return NULL; return NULL;
} }
/* Subsystem-level privilege enforcement: prevent privilege escalation. */
{
Task *caller = task_current();
if (caller != NULL && privilege > task_get_privilege(caller)) {
return NULL;
}
}
/* Find a free PCB slot */ /* Find a free PCB slot */
for (i = 0; i < TASK_MAX; i++) { for (i = 0; i < TASK_MAX; i++) {
if (tasks[i].state == TASK_STATE_FREE) { if (tasks[i].state == TASK_STATE_FREE) {
@@ -150,6 +163,7 @@ Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
/* Fill in the PCB */ /* Fill in the PCB */
t->pid = next_pid++; t->pid = next_pid++;
t->state = TASK_STATE_READY; t->state = TASK_STATE_READY;
t->privilege = privilege;
t->entry = entry; t->entry = entry;
t->arg = arg; t->arg = arg;
t->switches = 0; t->switches = 0;
@@ -196,6 +210,15 @@ Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
return t; return t;
} }
Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg)
{
/* Inherit privilege from the calling task (kernel if no task context). */
Task *caller = task_current();
TaskPrivilege priv = (caller != NULL) ? task_get_privilege(caller)
: TASK_PRIV_KERNEL;
return task_create_with_priv(name, entry, arg, priv);
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* Schedule pick the next READY task (round-robin) * Schedule pick the next READY task (round-robin)
* ---------------------------------------------------------------- */ * ---------------------------------------------------------------- */
@@ -318,6 +341,22 @@ Task *task_current(void)
return current_task; return current_task;
} }
TaskPrivilege task_get_privilege(Task *t)
{
if (t == NULL) {
return TASK_PRIV_KERNEL;
}
return t->privilege;
}
void task_set_privilege(Task *t, TaskPrivilege privilege)
{
if (t == NULL) {
return;
}
t->privilege = privilege;
}
UINTN task_count(void) UINTN task_count(void)
{ {
UINTN i, count = 0; UINTN i, count = 0;
@@ -366,19 +405,28 @@ static const CHAR16 *state_str(TaskState s)
void task_print_list(BootInfo *Boot) void task_print_list(BootInfo *Boot)
{ {
UINTN i; UINTN i;
Task *caller;
/* Subsystem-level privilege enforcement: task list requires DRIVER. */
caller = task_current();
if (caller != NULL && task_get_privilege(caller) < TASK_PRIV_DRIVER) {
SAFE_PRINT(Boot, L"Permission denied: task list requires driver privilege.\n\r");
return;
}
SAFE_PRINT(Boot, L"\n\r"); SAFE_PRINT(Boot, L"\n\r");
SAFE_PRINT(Boot, L" PID STATE SWITCHES NAME\n\r"); SAFE_PRINT(Boot, L" PID STATE PRIV SWITCHES NAME\n\r");
SAFE_PRINT(Boot, L" --- ---------- -------- ----\n\r"); SAFE_PRINT(Boot, L" --- ---------- ---- -------- ----\n\r");
for (i = 0; i < TASK_MAX; i++) { for (i = 0; i < TASK_MAX; i++) {
if (tasks[i].state == TASK_STATE_FREE) { if (tasks[i].state == TASK_STATE_FREE) {
continue; continue;
} }
SAFE_PRINT(Boot, L" %3d %-10s %8d %s\n\r", SAFE_PRINT(Boot, L" %3d %-10s %4d %8d %s\n\r",
tasks[i].pid, tasks[i].pid,
state_str(tasks[i].state), state_str(tasks[i].state),
(INT32)tasks[i].privilege,
tasks[i].switches, tasks[i].switches,
tasks[i].name); tasks[i].name);
} }

42
task.h
View File

@@ -27,11 +27,28 @@
typedef enum { typedef enum {
TASK_STATE_FREE = 0, /* PCB slot is unused */ TASK_STATE_FREE = 0, /* PCB slot is unused */
TASK_STATE_READY, /* runnable, waiting to be scheduled */ TASK_STATE_READY, /* runnable, waiting to be scheduled */
TASK_STATE_RUNNING, /* currently executing on the CPU */ TASK_STATE_RUNNING, /* currently executing on the CPU */
TASK_STATE_TERMINATED /* finished; slot will be recycled */ TASK_STATE_TERMINATED /* finished; slot will be recycled */
} TaskState; } TaskState;
/*
* Logical privilege level for a task.
*
* Higher numeric values are more privileged:
* USER (0) least privileged
* DRIVER (1) mid-level, can talk to hardware-facing subsystems
* KERNEL (2) most privileged, core kernel and management threads
*
* All tasks still execute in ring 0 today; this is a software
* hierarchy used for access control decisions and future paging work.
*/
typedef enum {
TASK_PRIV_USER = 0,
TASK_PRIV_DRIVER = 1,
TASK_PRIV_KERNEL = 2,
} TaskPrivilege;
/* ================================================================ /* ================================================================
* Task entry function * Task entry function
* ================================================================ */ * ================================================================ */
@@ -44,9 +61,10 @@ typedef void (*TaskEntryFn)(void *arg);
* ================================================================ */ * ================================================================ */
typedef struct Task { typedef struct Task {
UINT32 pid; /* unique process ID */ UINT32 pid; /* unique process ID */
TaskState state; /* current lifecycle state */ TaskState state; /* current lifecycle state */
CHAR16 name[TASK_NAME_LEN]; /* human-readable label */ TaskPrivilege privilege; /* logical privilege level */
CHAR16 name[TASK_NAME_LEN]; /* human-readable label */
/* Context switch state */ /* Context switch state */
UINT64 saved_rsp; /* RSP saved by context_switch() */ UINT64 saved_rsp; /* RSP saved by context_switch() */
@@ -66,11 +84,21 @@ typedef struct Task {
* ================================================================ */ * ================================================================ */
void task_init(BootInfo *Boot); /* initialise scheduler */ void task_init(BootInfo *Boot); /* initialise scheduler */
Task *task_create(const CHAR16 *name, /* spawn a new task */
/* Spawn a new task with a specific privilege level. */
Task *task_create_with_priv(const CHAR16 *name,
TaskEntryFn entry,
void *arg,
TaskPrivilege privilege);
/* Backwards-compatible helper: creates a kernel-privileged task. */
Task *task_create(const CHAR16 *name,
TaskEntryFn entry, void *arg); TaskEntryFn entry, void *arg);
void task_yield(void); /* voluntarily give up the CPU */ void task_yield(void); /* voluntarily give up the CPU */
void task_exit(void); /* terminate the current task */ void task_exit(void); /* terminate the current task */
Task *task_current(void); /* return the running task's PCB */ Task *task_current(void); /* return the running task's PCB */
TaskPrivilege task_get_privilege(Task *t);
void task_set_privilege(Task *t, TaskPrivilege privilege);
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`) */