moving efi code to boot
This commit is contained in:
@@ -99,10 +99,13 @@ pub fn build(b: *std.Build) void {
|
|||||||
|
|
||||||
b.installArtifact(exe);
|
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(.{
|
const efiexe = b.addExecutable(.{
|
||||||
.name = "BOOTX64",
|
.name = "BOOTX64",
|
||||||
.root_module = b.createModule(.{
|
.root_module = b.createModule(.{
|
||||||
.root_source_file = b.path("src/efi.zig"),
|
.root_source_file = b.path("src/boot/efi.zig"),
|
||||||
.target = b.resolveTargetQuery(.{
|
.target = b.resolveTargetQuery(.{
|
||||||
.cpu_arch = .x86_64,
|
.cpu_arch = .x86_64,
|
||||||
.os_tag = .uefi,
|
.os_tag = .uefi,
|
||||||
|
|||||||
+1
-1
@@ -91,7 +91,7 @@ behind the [arch](arch.md) boundary, and when idle, or on a panic, it **halts**
|
|||||||
|
|
||||||
| Area | Code |
|
| 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` |
|
| Kernel entry, panic, bring-up | `src/main.zig` |
|
||||||
| Shared loader↔kernel contract (`BootInfo`, `Framebuffer`, `MemoryMap`, ABI) | `src/root.zig` |
|
| Shared loader↔kernel contract (`BootInfo`, `Framebuffer`, `MemoryMap`, ABI) | `src/root.zig` |
|
||||||
| Physical frame allocator | `src/pmm.zig` |
|
| Physical frame allocator | `src/pmm.zig` |
|
||||||
|
|||||||
+1
-1
@@ -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 →
|
- **CPU architecture** (x86_64 vs AArch64): instructions, MMU, interrupts →
|
||||||
`src/arch/<cpu>/`.
|
`src/arch/<cpu>/`.
|
||||||
- **Boot protocol** (UEFI vs Raspberry Pi firmware + device tree): handled
|
- **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
|
`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
|
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.
|
device-tree pointer, so that entry work would live in the AArch64 arch code.
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ the DTB/ACPI tells you what devices exist.
|
|||||||
[`MemoryMap`](memory-map.md) — the same neutral handoff `efi.zig` produces, just
|
[`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
|
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.
|
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
|
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
|
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)
|
convention for the kernel jump. So an `aarch64`-UEFI target (QEMU `virt` + AAVMF)
|
||||||
|
|||||||
+3
-3
@@ -10,7 +10,7 @@ that hands us a working CPU, a memory map, and a screen, and then gets out of th
|
|||||||
way.
|
way.
|
||||||
|
|
||||||
The key thing to understand: **UEFI is not our OS, it's a stepping stone.** It
|
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
|
that the firmware runs — and its entire purpose is to gather what the kernel needs
|
||||||
and then jump into the kernel.
|
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
|
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
|
`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
|
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.
|
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
|
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
|
`BootInfo`, tears down the firmware, and jumps into the kernel — after which we're
|
||||||
on our own.
|
on our own.
|
||||||
|
|||||||
+1
-1
@@ -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
|
`arch.halt()`. A panic is unrecoverable here, so stopping the machine — rather
|
||||||
than limping on with corrupted state — is the safe response.
|
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
|
off to the kernel, `main` logs the error and parks the machine with the same
|
||||||
loop so the message stays on screen:
|
loop so the message stays on screen:
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ pub const BootInfo = extern struct {
|
|||||||
|
|
||||||
## The loader side (UEFI)
|
## 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`:
|
- **`classify`** maps each UEFI descriptor to a `MemoryKind`:
|
||||||
`conventional_memory` **and** `boot_services_code`/`boot_services_data → usable`;
|
`conventional_memory` **and** `boot_services_code`/`boot_services_data → usable`;
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ argument arrives in **RCX**, not RDI.
|
|||||||
|
|
||||||
danos's two binaries default to different conventions:
|
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).
|
Microsoft x64 (first argument → RCX).
|
||||||
- The kernel is freestanding, so its convention is SysV (first argument → RDI).
|
- The kernel is freestanding, so its convention is SysV (first argument → RDI).
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
//! Shared definitions that form the contract between the bootloader
|
//! Shared definitions that form the contract between a bootloader
|
||||||
//! (src/efi.zig, built as BOOTX64.efi) and the kernel (src/main.zig).
|
//! (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
|
//! Both binaries import this as the "danos" module, so the handoff layout is
|
||||||
//! defined in exactly one place.
|
//! defined in exactly one place.
|
||||||
|
|||||||
Reference in New Issue
Block a user