booting into the kernel

This commit is contained in:
2026-07-01 19:51:06 +01:00
parent 3c8656fb25
commit ca3c3d131c
5 changed files with 342 additions and 22 deletions
+43 -10
View File
@@ -4,22 +4,46 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Shared handoff definitions (BootInfo, Framebuffer, ...). No target is set,
// so the module inherits the target of whichever binary imports it — the
// freestanding kernel or the UEFI bootloader.
const mod = b.addModule("danos", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
});
// --- Kernel: freestanding x86_64 ELF, jumped to by the bootloader ---
// SSE2 is part of the x86_64 baseline and UEFI leaves it enabled at handoff,
// so we keep it: disabling it forces soft-float and makes the compiler unable
// to encode the vector ops that std's formatting/runtime still emit.
const kernel_target = b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .freestanding,
.abi = .none,
});
const exe = b.addExecutable(.{
.name = "danos",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.target = kernel_target,
.optimize = optimize,
.code_model = .small, // kernel is linked in the low 2 GiB (see image_base)
.red_zone = false, // interrupts would corrupt the SysV red zone
.single_threaded = true, // no scheduler yet; avoids pulling in TLS/atomics
.sanitize_c = .off, // the UBSan runtime needs f128/SSE support we don't provide
.stack_check = false, // stack-probe calls have no runtime to land in
.stack_protector = false,
.imports = &.{
.{ .name = "danos", .module = mod },
},
}),
});
exe.setLinkerScript(b.path("src/linker.ld"));
exe.entry = .{ .symbol_name = "_start" };
// Physical address the bootloader loads the kernel to (identity-mapped under
// UEFI). Overrides Zig's default image base so the linker script's layout is
// honoured; adjust here if it collides with firmware-reserved memory.
exe.image_base = 0x100000; // 1 MiB
b.installArtifact(exe);
@@ -32,6 +56,9 @@ pub fn build(b: *std.Build) void {
.os_tag = .uefi,
}),
.optimize = optimize,
.imports = &.{
.{ .name = "danos", .module = mod },
},
}),
});
@@ -55,6 +82,11 @@ pub fn build(b: *std.Build) void {
const efi_install = b.addInstallArtifact(efiexe, .{
.dest_dir = .{ .override = .{ .custom = "esp/EFI/BOOT" } },
});
// The bootloader loads the kernel by name from the volume root, so drop the
// kernel ELF at esp/danos.
const kernel_install = b.addInstallArtifact(exe, .{
.dest_dir = .{ .override = .{ .custom = "esp" } },
});
// The firmware needs to write NVRAM, so give it a writable copy of the vars.
const vars_copy = b.addSystemCommand(&.{ "cp", "-f", ovmf_vars });
@@ -79,6 +111,7 @@ pub fn build(b: *std.Build) void {
"none",
});
run_efi.step.dependOn(&efi_install.step);
run_efi.step.dependOn(&kernel_install.step);
const run_efi_step = b.step("run-efi", "Boot the EFI app in QEMU (OVMF)");
run_efi_step.dependOn(&run_efi.step);
@@ -92,19 +125,19 @@ pub fn build(b: *std.Build) void {
// run_cmd.addArgs(args);
// }
// Tests run on the host. The kernel and bootloader target freestanding/UEFI
// and can't be executed natively, so only the shared module is unit-tested
// here (compiled for the host rather than inheriting a freestanding target).
const mod_tests = b.addTest(.{
.root_module = mod,
.root_module = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
}),
});
const run_mod_tests = b.addRunArtifact(mod_tests);
const exe_tests = b.addTest(.{
.root_module = exe.root_module,
});
const run_exe_tests = b.addRunArtifact(exe_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
test_step.dependOn(&run_exe_tests.step);
}