moving efi code to boot

This commit is contained in:
2026-07-05 10:01:01 +01:00
parent ae1552414d
commit 7c3cffb337
10 changed files with 15 additions and 12 deletions
+4 -1
View File
@@ -99,10 +99,13 @@ pub fn build(b: *std.Build) void {
b.installArtifact(exe);
// Boot methods live in src/boot/, one per way of getting the kernel running.
// Each is its own binary/entry (a loader is built for its own target); today
// that's UEFI for x86-64, with room for e.g. a device-tree path for the Pis.
const efiexe = b.addExecutable(.{
.name = "BOOTX64",
.root_module = b.createModule(.{
.root_source_file = b.path("src/efi.zig"),
.root_source_file = b.path("src/boot/efi.zig"),
.target = b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .uefi,
+1 -1
View File
@@ -91,7 +91,7 @@ behind the [arch](arch.md) boundary, and when idle, or on a panic, it **halts**
| Area | Code |
|------|------|
| Bootloader (UEFI app) | `src/efi.zig` → `BOOTX64.efi` |
| Boot methods (one per way of booting the kernel) | `src/boot/` — `efi.zig` (UEFI) → `BOOTX64.efi` |
| Kernel entry, panic, bring-up | `src/main.zig` |
| Shared loader↔kernel contract (`BootInfo`, `Framebuffer`, `MemoryMap`, ABI) | `src/root.zig` |
| Physical frame allocator | `src/pmm.zig` |
+1 -1
View File
@@ -53,7 +53,7 @@ There are really two independent questions, and it's worth not conflating them:
- **CPU architecture** (x86_64 vs AArch64): instructions, MMU, interrupts →
`src/arch/<cpu>/`.
- **Boot protocol** (UEFI vs Raspberry Pi firmware + device tree): handled
*separately*, because loaders are their own binaries. `src/efi.zig` builds
*separately*, because loaders are their own binaries. `src/boot/efi.zig` builds
`BOOTX64.efi`, a distinct executable from the kernel ELF. On a Pi there is no
separate loader at all — the firmware jumps straight into the kernel with a
device-tree pointer, so that entry work would live in the AArch64 arch code.
+1 -1
View File
@@ -66,7 +66,7 @@ the DTB/ACPI tells you what devices exist.
[`MemoryMap`](memory-map.md) — the same neutral handoff `efi.zig` produces, just
from a different source. This is where keeping boot-protocol knowledge on the
loader side (as we did for the UEFI memory-map classification) pays off.
- **The UEFI loader mostly carries over.** `src/efi.zig` is largely
- **The UEFI loader mostly carries over.** `src/boot/efi.zig` is largely
boot-*protocol* code (`std.os.uefi` protocol calls), not x86 code. Its only truly
x86-specific bits are the ELF machine check (`.X86_64`) and the SysV calling
convention for the kernel jump. So an `aarch64`-UEFI target (QEMU `virt` + AAVMF)
+3 -3
View File
@@ -10,7 +10,7 @@ that hands us a working CPU, a memory map, and a screen, and then gets out of th
way.
The key thing to understand: **UEFI is not our OS, it's a stepping stone.** It
exists to load *us*. Our `src/efi.zig` is a UEFI *application* — a normal program
exists to load *us*. Our `src/boot/efi.zig` is a UEFI *application* — a normal program
that the firmware runs — and its entire purpose is to gather what the kernel needs
and then jump into the kernel.
@@ -23,7 +23,7 @@ Partition (ESP)** and running a file at a well-known fallback path:
esp/EFI/BOOT/BOOTX64.efi <- the "removable media" default for x86-64
```
That's exactly the layout `build.zig` assembles. It builds `src/efi.zig` for the
That's exactly the layout `build.zig` assembles. It builds `src/boot/efi.zig` for the
`uefi` target, installs it to `esp/EFI/BOOT/BOOTX64.efi`, and drops the kernel ELF
at `esp/danos`. The `run-x86-64` step then points QEMU at OVMF (UEFI firmware for
virtual machines) and presents that `esp/` directory to the guest as a FAT drive.
@@ -174,6 +174,6 @@ power on
```
Bottom line: **UEFI's job is to give us a CPU, memory, and a framebuffer, then
disappear.** `src/efi.zig` is the thin bridge that collects those gifts into a
disappear.** `src/boot/efi.zig` is the thin bridge that collects those gifts into a
`BootInfo`, tears down the firmware, and jumps into the kernel — after which we're
on our own.
+1 -1
View File
@@ -106,7 +106,7 @@ There are three halt sites, and they're all the same idea:
`arch.halt()`. A panic is unrecoverable here, so stopping the machine — rather
than limping on with corrupted state — is the safe response.
3. **Bootloader failure** — in `src/efi.zig`, if `boot()` fails *before* handing
3. **Bootloader failure** — in `src/boot/efi.zig`, if `boot()` fails *before* handing
off to the kernel, `main` logs the error and parks the machine with the same
loop so the message stays on screen:
+1 -1
View File
@@ -68,7 +68,7 @@ pub const BootInfo = extern struct {
## The loader side (UEFI)
Two functions in `src/efi.zig`, called from `exitBootServices`:
Two functions in `src/boot/efi.zig`, called from `exitBootServices`:
- **`classify`** maps each UEFI descriptor to a `MemoryKind`:
`conventional_memory` **and** `boot_services_code`/`boot_services_data → usable`;
+1 -1
View File
@@ -52,7 +52,7 @@ argument arrives in **RCX**, not RDI.
danos's two binaries default to different conventions:
- `src/efi.zig` is built for the UEFI target, so its default C convention is
- `src/boot/efi.zig` is built for the UEFI target, so its default C convention is
Microsoft x64 (first argument → RCX).
- The kernel is freestanding, so its convention is SysV (first argument → RDI).
View File
+2 -2
View File
@@ -1,5 +1,5 @@
//! Shared definitions that form the contract between the bootloader
//! (src/efi.zig, built as BOOTX64.efi) and the kernel (src/main.zig).
//! Shared definitions that form the contract between a bootloader
//! (src/boot/, e.g. efi.zig built as BOOTX64.efi) and the kernel (src/main.zig).
//!
//! Both binaries import this as the "danos" module, so the handoff layout is
//! defined in exactly one place.