boot: Add EFISTUB and refactor boot protocols
This commit is contained in:
+122
@@ -0,0 +1,122 @@
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// Defined in linker script.
|
||||
extern const struct cmdline_option* __ld_cmdline_start[];
|
||||
extern const struct cmdline_option* __ld_cmdline_end[];
|
||||
|
||||
static char cmdline_buf[0x1000];
|
||||
static char cmdline_raw[0x1000];
|
||||
|
||||
const char* cmdline_get() {
|
||||
return cmdline_raw;
|
||||
}
|
||||
|
||||
void cmdline_parse_buf(
|
||||
char* cmdline_buf,
|
||||
size_t len,
|
||||
const struct cmdline_option* const* start,
|
||||
const struct cmdline_option* const* end
|
||||
) {
|
||||
size_t idx = 0;
|
||||
|
||||
while (1) {
|
||||
const struct cmdline_option* const* opt = start;
|
||||
char* name = nullptr;
|
||||
char* value = nullptr;
|
||||
|
||||
// Skip all leading spaces.
|
||||
while (idx < len && cmdline_buf[idx] == ' ')
|
||||
idx++;
|
||||
if (idx >= len)
|
||||
break;
|
||||
size_t name_idx = idx;
|
||||
name = cmdline_buf + name_idx;
|
||||
|
||||
// Find the next equal sign or space.
|
||||
while (idx < len && cmdline_buf[idx] != '=' && cmdline_buf[idx] != ' ')
|
||||
idx++;
|
||||
if (idx > len)
|
||||
break;
|
||||
|
||||
// Check if the option has a value (=foo).
|
||||
char seperator = cmdline_buf[idx];
|
||||
cmdline_buf[idx++] = 0;
|
||||
if (seperator == '=') {
|
||||
// Check if we need to escape the value.
|
||||
char check;
|
||||
if (cmdline_buf[idx] == '"') {
|
||||
check = '"';
|
||||
cmdline_buf[idx++] = 0;
|
||||
} else {
|
||||
check = ' ';
|
||||
}
|
||||
|
||||
value = cmdline_buf + idx;
|
||||
|
||||
// Skip the value.
|
||||
while (idx < len && cmdline_buf[idx] != check)
|
||||
idx++;
|
||||
if (idx > len)
|
||||
break;
|
||||
cmdline_buf[idx++] = 0;
|
||||
}
|
||||
|
||||
// Find the corresponding option.
|
||||
while (opt < end) {
|
||||
const struct cmdline_option* current = *opt;
|
||||
if (!strcmp(current->name, name)) {
|
||||
if (value == nullptr)
|
||||
break;
|
||||
switch (current->option_type) {
|
||||
case CMDLINE_STRING:
|
||||
current->func_str(value);
|
||||
break;
|
||||
case CMDLINE_BOOL:
|
||||
if (!strcmp(value, "on") || !strcmp(value, "yes") || !strcmp(value, "true") || !strcmp(value, "1"))
|
||||
current->func_bool(true);
|
||||
else
|
||||
current->func_bool(false);
|
||||
break;
|
||||
case CMDLINE_INTPTR: {
|
||||
int base = 10;
|
||||
if (!strncmp(value, "0x", 2)) {
|
||||
base = 0x10;
|
||||
value += 2;
|
||||
}
|
||||
current->func_intptr(atol(value, base));
|
||||
break;
|
||||
}
|
||||
case CMDLINE_UINTPTR: {
|
||||
int base = 10;
|
||||
if (!strncmp(value, "0x", 2)) {
|
||||
base = 0x10;
|
||||
value += 2;
|
||||
}
|
||||
current->func_uintptr(atolu(value, base));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
opt++;
|
||||
}
|
||||
|
||||
if (idx >= len)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
void cmdline_parse(const char* cmdline) {
|
||||
if (cmdline == nullptr)
|
||||
cmdline = "";
|
||||
|
||||
const size_t len = strnlen(cmdline, sizeof(cmdline_buf) - 1);
|
||||
memcpy(cmdline_buf, cmdline, len);
|
||||
memcpy(cmdline_raw, cmdline, len);
|
||||
cmdline_parse_buf(cmdline_buf, len, __ld_cmdline_start, __ld_cmdline_end);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/boot.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/fdt.h>
|
||||
#include <zinnia/init.h>
|
||||
|
||||
[[__section(".boot.info")]]
|
||||
struct boot_info boot_info;
|
||||
|
||||
[[__init]]
|
||||
void kernel_entry() {
|
||||
kernel_early_init();
|
||||
|
||||
assert(boot_info.num_mem_maps, "Loader did not provide a memory map");
|
||||
|
||||
if (boot_info.dtb)
|
||||
boot_info.dtb_size = fdt_read_u32((const uint8_t*)(boot_info.hhdm_base + boot_info.dtb) + 4);
|
||||
|
||||
kernel_main(&boot_info);
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/elf.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/initramfs.h>
|
||||
#include <zinnia/irq.h>
|
||||
#include <zinnia/module.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/process.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vfs.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <zinnia/vmo.h>
|
||||
#include <config.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const char zinnia_banner[] = "Zinnia " ZINNIA_VERSION " " CONFIG_KERNEL_FLAVOR " (" ZINNIA_ARCH
|
||||
", " ZINNIA_COMPILER_ID ", " ZINNIA_LINKER_ID ")";
|
||||
|
||||
// Path to the init executable.
|
||||
static const char* init_path = "/init";
|
||||
static void init_option(const char* value) {
|
||||
init_path = value;
|
||||
}
|
||||
CMDLINE_OPTION("init", "Path to the init executable", init_option);
|
||||
|
||||
static void kernel_main_task(uintptr_t arg0) {
|
||||
errno_t status = 0;
|
||||
struct boot_info* info = (struct boot_info*)arg0;
|
||||
|
||||
log("Hello from main task!\n");
|
||||
|
||||
// Finish the initgraph.
|
||||
initgraph_run(nullptr, info);
|
||||
|
||||
// After everything is loaded, load all builtin drivers.
|
||||
module_init();
|
||||
|
||||
// Load all initramfs files.
|
||||
initramfs_load_boot_modules(info);
|
||||
|
||||
const char* init_argv[] = {init_path, nullptr};
|
||||
const char* init_envp[] = {"HOME=/", "TERM=console", nullptr};
|
||||
|
||||
log("Starting init \"%s\"\n", init_path);
|
||||
log("With arguments:\n");
|
||||
for (const char** arg = init_argv; *arg; arg++)
|
||||
log(" %s\n", *arg);
|
||||
log("With environment:\n");
|
||||
for (const char** env = init_envp; *env; env++)
|
||||
log(" %s\n", *env);
|
||||
|
||||
status = process_spawn_init(init_path, init_argv, init_envp);
|
||||
assert(status == 0, "Failed to start init \"%s\" (%i)", init_path, status);
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
void kernel_early_init() {
|
||||
percpu_bsp_init();
|
||||
percpu_get()->online = true;
|
||||
irq_lock();
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main(struct boot_info* info) {
|
||||
cmdline_parse(info->cmdline);
|
||||
|
||||
log("%s\n", zinnia_banner); // Say hello!
|
||||
log("Boot loader: %s\n", info->loader);
|
||||
log("Command line: %s\n", info->cmdline);
|
||||
|
||||
initgraph_run(sched_stage, info);
|
||||
|
||||
struct task* main_task;
|
||||
task_create("main", process_kernel(), kernel_main_task, (uintptr_t)info, &main_task);
|
||||
sched_add_task(&percpu_get()->sched, main_task);
|
||||
|
||||
irq_unlock();
|
||||
sched_yield(&percpu_get()->sched);
|
||||
|
||||
unreachable();
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
kernel_sources += files(
|
||||
'clock.c',
|
||||
'cmdline.c',
|
||||
'condvar.c',
|
||||
'console.c',
|
||||
'entry.c',
|
||||
'event.c',
|
||||
'guid.c',
|
||||
'initgraph.c',
|
||||
'iovec.c',
|
||||
'irq.c',
|
||||
'ksyms.c',
|
||||
'main.c',
|
||||
'module.c',
|
||||
'mutex.c',
|
||||
'panic.c',
|
||||
|
||||
Reference in New Issue
Block a user