Introduced a new CommandTaskContext structure to facilitate command execution in separate tasks. Updated the execute_command function to spawn tasks for commands, enhancing concurrency. Improved documentation for the 'memtest' and 'tasktest' commands in README.md to clarify their functionality and usage. Updated the interactive prompt reference to 'starling>'.
588 lines
18 KiB
C
588 lines
18 KiB
C
/*
|
||
* commands.c – Shell command registry and handler implementations.
|
||
*
|
||
* Each command is a { name, description, usage, handler } entry in the
|
||
* static `commands[]` table. execute_command() splits user input into
|
||
* command + arguments and dispatches to the matching handler.
|
||
*
|
||
* To add a new command:
|
||
* 1. Write a static handler cmd_foo(BootInfo *Boot, CHAR16 *Args)
|
||
* 2. Add a forward declaration above the table
|
||
* 3. Append an entry to commands[] (before the sentinel)
|
||
*/
|
||
|
||
#include "kernel_types.h"
|
||
#include "commands.h"
|
||
#include "string_utils.h"
|
||
#include "memory.h"
|
||
#include "task.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)
|
||
|
||
/* ================================================================
|
||
* Forward declarations
|
||
* ================================================================ */
|
||
|
||
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_help(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_man(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_clear(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_about(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_mem(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_ps(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_spawn(BootInfo *Boot, CHAR16 *Args);
|
||
static void cmd_memtest(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
|
||
* ================================================================ */
|
||
|
||
static Command commands[] = {
|
||
{
|
||
L"shutdown",
|
||
L"Shutdown the system",
|
||
L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.",
|
||
cmd_shutdown
|
||
},
|
||
{
|
||
L"help",
|
||
L"Display available commands",
|
||
L"Usage: help\n\r Lists all available commands with brief descriptions.",
|
||
cmd_help
|
||
},
|
||
{
|
||
L"man",
|
||
L"Display manual page for a command",
|
||
L"Usage: man <command>\n\r Shows detailed help for the specified command.",
|
||
cmd_man
|
||
},
|
||
{
|
||
L"clear",
|
||
L"Clear the screen",
|
||
L"Usage: clear\n\r Clears the console screen.",
|
||
cmd_clear
|
||
},
|
||
{
|
||
L"about",
|
||
L"Display system information",
|
||
L"Usage: about\n\r Shows information about this operating system.",
|
||
cmd_about
|
||
},
|
||
{
|
||
L"mem",
|
||
L"Display memory statistics",
|
||
L"Usage: mem\n\r Shows physical memory, heap, and paging information.",
|
||
cmd_mem
|
||
},
|
||
{
|
||
L"ps",
|
||
L"List running tasks",
|
||
L"Usage: ps\n\r Displays all active tasks with PID, state, and name.",
|
||
cmd_ps
|
||
},
|
||
{
|
||
L"spawn",
|
||
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.",
|
||
cmd_spawn
|
||
},
|
||
{
|
||
L"memtest",
|
||
L"Test memory allocation and deallocation",
|
||
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
|
||
},
|
||
{
|
||
L"tasktest",
|
||
L"Test task scheduler with multiple tasks",
|
||
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
|
||
},
|
||
{NULL, NULL, NULL, NULL} /* sentinel */
|
||
};
|
||
|
||
/* ================================================================
|
||
* System control
|
||
* ================================================================ */
|
||
|
||
static void request_shutdown(BootInfo *Boot)
|
||
{
|
||
if (Boot == NULL) {
|
||
return;
|
||
}
|
||
|
||
if (Boot->shutdown != NULL) {
|
||
Boot->shutdown();
|
||
return;
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"Shutdown service unavailable.\n\r");
|
||
}
|
||
|
||
/* ================================================================
|
||
* Built-in command handlers
|
||
* ================================================================ */
|
||
|
||
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
(void)Args;
|
||
SAFE_PRINT(Boot, L"Shutting down...\n\r");
|
||
request_shutdown(Boot);
|
||
}
|
||
|
||
static void cmd_help(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
(void)Args;
|
||
show_help(Boot);
|
||
}
|
||
|
||
static void cmd_man(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
UINTN i = 0;
|
||
|
||
if (Args == NULL || Args[0] == L'\0') {
|
||
SAFE_PRINT(Boot, L"Usage: man <command>\n\r");
|
||
SAFE_PRINT(Boot, L"Try 'help' for a list of available commands.\n\r");
|
||
return;
|
||
}
|
||
|
||
trim_spaces_inplace(Args);
|
||
|
||
for (i = 0; commands[i].name != NULL; i++) {
|
||
if (ascii_streq_ci(Args, commands[i].name)) {
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"NAME\n\r");
|
||
SAFE_PRINT(Boot, L" %s - %s\n\r\n\r", commands[i].name, commands[i].description);
|
||
SAFE_PRINT(Boot, L"DESCRIPTION\n\r");
|
||
SAFE_PRINT(Boot, L" %s\n\r", commands[i].usage);
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
return;
|
||
}
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"No manual entry for '%s'\n\r", Args);
|
||
SAFE_PRINT(Boot, L"Try 'help' for a list of available commands.\n\r");
|
||
}
|
||
|
||
static void cmd_clear(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
KSTATUS Status;
|
||
(void)Args;
|
||
|
||
if (Boot != NULL && Boot->clear_screen != NULL) {
|
||
Status = Boot->clear_screen();
|
||
if (Status != 0) {
|
||
SAFE_PRINT(Boot, L"Failed to clear screen (status=%ld)\n\r",
|
||
(UINT64)Status);
|
||
}
|
||
} else {
|
||
SAFE_PRINT(Boot, L"Clear screen function unavailable.\n\r");
|
||
}
|
||
}
|
||
|
||
static void cmd_about(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
(void)Args;
|
||
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"================================================\n\r");
|
||
SAFE_PRINT(Boot, L" Simple 64-bit Operating System\n\r");
|
||
SAFE_PRINT(Boot, L"================================================\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"A minimal bootable operating system written in C.\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"Features:\n\r");
|
||
SAFE_PRINT(Boot, L" - Flexible bootloader interface\n\r");
|
||
SAFE_PRINT(Boot, L" - Console I/O\n\r");
|
||
SAFE_PRINT(Boot, L" - ELF64 Kernel Loader\n\r");
|
||
SAFE_PRINT(Boot, L" - Memory Management (PMM + Heap)\n\r");
|
||
SAFE_PRINT(Boot, L" - Cooperative Multitasking\n\r");
|
||
SAFE_PRINT(Boot, L" - Interactive Command Interface\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"Type 'help' for available commands.\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
}
|
||
|
||
static void cmd_mem(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
(void)Args;
|
||
memory_print_stats(Boot);
|
||
}
|
||
|
||
/* ----------------------------------------------------------------
|
||
* Demo task – runs cooperatively, prints progress, then exits
|
||
* ---------------------------------------------------------------- */
|
||
|
||
static void demo_task_fn(void *arg)
|
||
{
|
||
BootInfo *Boot = (BootInfo *)arg;
|
||
Task *self = task_current();
|
||
UINTN i;
|
||
|
||
SAFE_PRINT(Boot, L"[%s:%d] started\n\r", self->name, self->pid);
|
||
|
||
for (i = 0; i < 5; i++) {
|
||
SAFE_PRINT(Boot, L"[%s:%d] tick %d/5\n\r",
|
||
self->name, self->pid, i + 1);
|
||
task_yield();
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"[%s:%d] finished\n\r", self->name, self->pid);
|
||
}
|
||
|
||
static void cmd_ps(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
(void)Args;
|
||
task_print_list(Boot);
|
||
}
|
||
|
||
static void cmd_spawn(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
Task *t;
|
||
const CHAR16 *name;
|
||
|
||
if (Args != NULL && Args[0] != L'\0') {
|
||
trim_spaces_inplace(Args);
|
||
name = Args;
|
||
} else {
|
||
name = L"demo";
|
||
}
|
||
|
||
t = task_create(name, demo_task_fn, Boot);
|
||
if (t == NULL) {
|
||
SAFE_PRINT(Boot, L"Failed to create task (out of slots or memory).\n\r");
|
||
return;
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"Created task '%s' (PID %d)\n\r", t->name, t->pid);
|
||
}
|
||
|
||
/* ----------------------------------------------------------------
|
||
* memtest – exercise the heap and PMM allocators
|
||
* ---------------------------------------------------------------- */
|
||
|
||
static void cmd_memtest(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
void *ptrs[8];
|
||
UINTN sizes[] = { 16, 64, 128, 256, 512, 1024, 2048, 4096 };
|
||
UINTN i;
|
||
UINT64 page;
|
||
UINTN h_total, h_used, h_free, h_blocks;
|
||
(void)Args;
|
||
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"Memory Test\n\r");
|
||
SAFE_PRINT(Boot, L"================================================\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
|
||
/* --- Heap allocation test --- */
|
||
SAFE_PRINT(Boot, L"[1] Heap allocation test\n\r");
|
||
for (i = 0; i < 8; i++) {
|
||
ptrs[i] = kmalloc(sizes[i]);
|
||
if (ptrs[i] != NULL) {
|
||
SAFE_PRINT(Boot, L" kmalloc(%4d) = 0x%lx OK\n\r",
|
||
sizes[i], (UINT64)(UINTN)ptrs[i]);
|
||
} else {
|
||
SAFE_PRINT(Boot, L" kmalloc(%4d) = NULL FAIL\n\r", sizes[i]);
|
||
}
|
||
}
|
||
|
||
heap_get_stats(&h_total, &h_used, &h_free, &h_blocks);
|
||
SAFE_PRINT(Boot, L" Heap after alloc: used=%d free=%d blocks=%d\n\r",
|
||
h_used, h_free, h_blocks);
|
||
|
||
/* --- Heap free test --- */
|
||
SAFE_PRINT(Boot, L"\n\r[2] Heap free test\n\r");
|
||
for (i = 0; i < 8; i++) {
|
||
if (ptrs[i] != NULL) {
|
||
kfree(ptrs[i]);
|
||
SAFE_PRINT(Boot, L" kfree(0x%lx) OK\n\r",
|
||
(UINT64)(UINTN)ptrs[i]);
|
||
}
|
||
}
|
||
|
||
heap_get_stats(&h_total, &h_used, &h_free, &h_blocks);
|
||
SAFE_PRINT(Boot, L" Heap after free: used=%d free=%d blocks=%d\n\r",
|
||
h_used, h_free, h_blocks);
|
||
|
||
/* --- PMM page allocation test --- */
|
||
SAFE_PRINT(Boot, L"\n\r[3] PMM page allocation test\n\r");
|
||
SAFE_PRINT(Boot, L" Free pages before: %d\n\r", pmm_get_free_pages());
|
||
|
||
page = pmm_alloc_page();
|
||
if (page != 0) {
|
||
SAFE_PRINT(Boot, L" pmm_alloc_page() = 0x%lx OK\n\r", page);
|
||
SAFE_PRINT(Boot, L" Free pages after alloc: %d\n\r", pmm_get_free_pages());
|
||
pmm_free_page(page);
|
||
SAFE_PRINT(Boot, L" pmm_free_page() OK\n\r");
|
||
SAFE_PRINT(Boot, L" Free pages after free: %d\n\r", pmm_get_free_pages());
|
||
} else {
|
||
SAFE_PRINT(Boot, L" pmm_alloc_page() = 0 FAIL (out of pages)\n\r");
|
||
}
|
||
|
||
/* --- Multi-page allocation test --- */
|
||
SAFE_PRINT(Boot, L"\n\r[4] PMM multi-page allocation test (4 pages)\n\r");
|
||
page = pmm_alloc_pages(4);
|
||
if (page != 0) {
|
||
SAFE_PRINT(Boot, L" pmm_alloc_pages(4) = 0x%lx OK\n\r", page);
|
||
SAFE_PRINT(Boot, L" Free pages after alloc: %d\n\r", pmm_get_free_pages());
|
||
pmm_free_pages(page, 4);
|
||
SAFE_PRINT(Boot, L" pmm_free_pages() OK\n\r");
|
||
SAFE_PRINT(Boot, L" Free pages after free: %d\n\r", pmm_get_free_pages());
|
||
} else {
|
||
SAFE_PRINT(Boot, L" pmm_alloc_pages(4) = 0 FAIL\n\r");
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"\n\rAll memory tests completed.\n\r\n\r");
|
||
}
|
||
|
||
/* ----------------------------------------------------------------
|
||
* tasktest – spawn multiple concurrent tasks to exercise scheduler
|
||
* ---------------------------------------------------------------- */
|
||
|
||
static void worker_task_fn(void *arg)
|
||
{
|
||
BootInfo *Boot = (BootInfo *)arg;
|
||
Task *self = task_current();
|
||
UINTN i;
|
||
|
||
for (i = 0; i < 3; i++) {
|
||
SAFE_PRINT(Boot, L" [%s:%d] working... step %d/3\n\r",
|
||
self->name, self->pid, i + 1);
|
||
task_yield();
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L" [%s:%d] done\n\r", self->name, self->pid);
|
||
}
|
||
|
||
static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args)
|
||
{
|
||
Task *t1, *t2, *t3;
|
||
UINTN i;
|
||
(void)Args;
|
||
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"Task Scheduler Test\n\r");
|
||
SAFE_PRINT(Boot, L"================================================\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
|
||
SAFE_PRINT(Boot, L"Spawning 3 worker tasks...\n\r\n\r");
|
||
|
||
t1 = task_create(L"worker-A", worker_task_fn, Boot);
|
||
t2 = task_create(L"worker-B", worker_task_fn, Boot);
|
||
t3 = task_create(L"worker-C", worker_task_fn, Boot);
|
||
|
||
if (t1 == NULL || t2 == NULL || t3 == NULL) {
|
||
SAFE_PRINT(Boot, L"Failed to create one or more tasks.\n\r");
|
||
return;
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"Created: %s (PID %d), %s (PID %d), %s (PID %d)\n\r",
|
||
t1->name, t1->pid, t2->name, t2->pid, t3->name, t3->pid);
|
||
SAFE_PRINT(Boot, L"\n\rYielding to let workers run:\n\r\n\r");
|
||
|
||
/* Yield enough times for all workers to complete (3 tasks x 3 steps) */
|
||
for (i = 0; i < 12; i++) {
|
||
task_yield();
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"\n\rTask list after test:\n\r");
|
||
task_print_list(Boot);
|
||
|
||
SAFE_PRINT(Boot, L"Task scheduler test completed.\n\r\n\r");
|
||
}
|
||
|
||
/* ================================================================
|
||
* Public API
|
||
* ================================================================ */
|
||
|
||
/* Print a formatted table of all registered commands. */
|
||
void show_help(BootInfo *Boot)
|
||
{
|
||
UINTN i = 0;
|
||
UINTN max_len = 0;
|
||
UINTN name_len = 0;
|
||
UINTN padding = 0;
|
||
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"Available commands:\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
|
||
/* Find the longest command name for column alignment */
|
||
for (i = 0; commands[i].name != NULL; i++) {
|
||
name_len = 0;
|
||
while (commands[i].name[name_len] != L'\0') {
|
||
name_len++;
|
||
}
|
||
if (name_len > max_len) {
|
||
max_len = name_len;
|
||
}
|
||
}
|
||
|
||
for (i = 0; commands[i].name != NULL; i++) {
|
||
SAFE_PRINT(Boot, L" %s", commands[i].name);
|
||
|
||
/* Pad to align descriptions */
|
||
name_len = 0;
|
||
while (commands[i].name[name_len] != L'\0') {
|
||
name_len++;
|
||
}
|
||
for (padding = 0; padding < (max_len - name_len + 2); padding++) {
|
||
SAFE_PRINT(Boot, L" ");
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"- %s\n\r", commands[i].description);
|
||
}
|
||
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
SAFE_PRINT(Boot, L"For detailed help on a command, type: man <command>\n\r");
|
||
SAFE_PRINT(Boot, L"\n\r");
|
||
}
|
||
|
||
/*
|
||
* Parse a line of user input into command + arguments and dispatch
|
||
* 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.
|
||
*/
|
||
Task *execute_command(BootInfo *Boot, CHAR16 *Input)
|
||
{
|
||
CHAR16 *cmd_start = NULL;
|
||
CHAR16 *args_start = NULL;
|
||
UINTN i = 0;
|
||
CommandTaskContext *ctx;
|
||
Task *t;
|
||
|
||
if (Boot == NULL || Input == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
trim_spaces_inplace(Input);
|
||
|
||
if (Input[0] == L'\0') {
|
||
return NULL;
|
||
}
|
||
|
||
/* Split input into command and argument strings */
|
||
cmd_start = Input;
|
||
args_start = Input;
|
||
|
||
/* Advance past the command keyword */
|
||
while (*args_start != L'\0' && !is_space16(*args_start)) {
|
||
args_start++;
|
||
}
|
||
|
||
/* NUL-terminate the command and skip leading whitespace in args */
|
||
if (*args_start != L'\0') {
|
||
*args_start = L'\0';
|
||
args_start++;
|
||
|
||
/* skip leading whitespace in args */
|
||
while (*args_start != L'\0' && is_space16(*args_start)) {
|
||
args_start++;
|
||
}
|
||
}
|
||
|
||
/* Look up and dispatch the command */
|
||
for (i = 0; commands[i].name != NULL; i++) {
|
||
if (ascii_streq_ci(cmd_start, commands[i].name)) {
|
||
/* Allocate a context block for the command task. */
|
||
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 */
|
||
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");
|
||
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);
|
||
}
|