docs: full docs-vs-code audit — fix every stale claim across 40 docs
Every doc verified claim-by-claim against the code by parallel audit agents, then fixed and adversarially re-verified. Two waves of staleness corrected: the originally audited findings (higher-half boot handoff, kernel VFS takeover, fault isolation + claim release + driver restart, AML/S5 moving to ring 3, threading's shipped design, USB+FAT landing) and a second pass of adjacent claims the verifiers caught (smp.md 'not built yet' intro, system-requirements' PS/2-only and no-storage claims, halting.md's red-panic and no-IDT text, testing.md's serial mirroring, router-era vfs-protocol wording, capsule-first boot loading). threading.md now documents the shared-fate gap explicitly: the design says a process dies whole, the kernel today kills only the offending thread. Also fixes three stale code comments (isr.s exceptionHandler, acpi.zig sleepValue, build.zig boot-volume) — comments only, no behavior change.
This commit is contained in:
+27
-21
@@ -16,11 +16,11 @@ the RSDT's address is a field *inside* the RSDP. The platform follows that point
|
||||
UEFI configuration table
|
||||
│ the loader reads the RSDP's physical address
|
||||
▼
|
||||
BootInfo.acpi_rsdp (u64, in the loader↔kernel handoff) system/boot-handoff.zig
|
||||
│ the kernel forwards the whole BootInfo
|
||||
BootInformation.acpi_rsdp (u64, in the loader↔kernel handoff) system/boot-handoff.zig
|
||||
│ the kernel forwards the whole BootInformation
|
||||
▼
|
||||
platform.discover(boot_info, …) system/devices/platform.zig
|
||||
│ reads boot_info.acpi_rsdp, hands it to the ACPI backend
|
||||
platform.discover(boot_information, …) system/devices/platform.zig
|
||||
│ reads boot_information.acpi_rsdp, hands it to the ACPI backend
|
||||
▼
|
||||
acpi.discover(rsdp_phys, …) system/devices/acpi.zig
|
||||
│ dereferences the RSDP, reads the pointer it contains
|
||||
@@ -40,11 +40,12 @@ still up. `acpiRootSystemDescriptorPointer()` in `boot/efi.zig` walks the UEFI
|
||||
"grab it before `ExitBootServices`" pattern as the [framebuffer](framebuffer.md) and
|
||||
the [memory map](memory-map.md).
|
||||
|
||||
## Step 2 — the handoff: a physical address in `BootInfo`
|
||||
## Step 2 — the handoff: a physical address in `BootInformation`
|
||||
|
||||
The loader can't just call the device module: the bootloader binary and the kernel
|
||||
binary are compiled separately, and **the loader isn't linked against the `platform`
|
||||
module at all** (it imports only the `boot-handoff` contract). So instead of a call, it
|
||||
module at all** (it imports only the `boot-handoff` contract and the
|
||||
`initial-ramdisk` module). So instead of a call, it
|
||||
deposits a value in the handoff struct:
|
||||
|
||||
```zig
|
||||
@@ -56,13 +57,14 @@ Two things about what crosses the boundary:
|
||||
|
||||
- **It's a *physical* address, not a Zig pointer.** The loader and kernel don't share
|
||||
an address space at the moment of the jump, so a raw `u64` physical address is the
|
||||
only thing that survives the handoff. `BootInfo.acpi_rsdp` is `0` when the firmware
|
||||
only thing that survives the handoff. `BootInformation.acpi_rsdp` is `0` when the firmware
|
||||
exposed no ACPI (e.g. a future device-tree machine, which would fill a different
|
||||
field instead — the kernel never learns which firmware booted it).
|
||||
- **The kernel can dereference it because it identity-maps ACPI memory.** The RSDP
|
||||
lives in ACPI-reclaim memory, which [paging.zig](paging.md) identity-maps along with
|
||||
the rest of RAM, so by the time discovery runs `@ptrFromInt(rsdp_phys)` is a valid
|
||||
pointer.
|
||||
- **The kernel can dereference it through the physmap.** The RSDP lives in
|
||||
ACPI-reclaim memory, which [paging.zig](paging.md) maps — along with the rest of
|
||||
RAM — into the higher-half **physmap** (there is no identity mapping; the low half
|
||||
belongs to user space). So by the time discovery runs,
|
||||
`@ptrFromInt(physicalToVirtual(rsdp_phys))` is a valid pointer.
|
||||
|
||||
This is the concrete form of the "capture the description pointer" step sketched in
|
||||
[discovery.md](discovery.md) — a plain `acpi_rsdp: u64` rather than a tagged handle,
|
||||
@@ -75,13 +77,13 @@ and then reads the root-table pointer *out of it*. Which pointer depends on the
|
||||
version, because the RSDP carries **both**:
|
||||
|
||||
```zig
|
||||
const rsdp: *const RootSystemDescriptionPointer = @ptrFromInt(rsdp_phys);
|
||||
const rsdp: *const RootSystemDescriptionPointer = @ptrFromInt(physicalToVirtual(rsdp_phys));
|
||||
if (!std.mem.eql(u8, &rsdp.signature, "RSD PTR ")) return error.BadRsdpSignature;
|
||||
if (!checksumOk(@ptrFromInt(rsdp_phys), 20)) return error.BadRsdpChecksum;
|
||||
if (!checksumOk(@ptrFromInt(physicalToVirtual(rsdp_phys)), 20)) return error.BadRsdpChecksum;
|
||||
|
||||
if (rsdp.revision >= 2) {
|
||||
// ACPI 2.0+: use the 64-bit XSDT pointer (the 32-bit RSDT is deprecated)
|
||||
const xsdp: *const ExtendedSystemDescriptorPointer = @ptrFromInt(rsdp_phys);
|
||||
const xsdp: *const ExtendedSystemDescriptorPointer = @ptrFromInt(physicalToVirtual(rsdp_phys));
|
||||
try walkRoot(u64, xsdp.extended_system_descriptor_table_address, …);
|
||||
} else {
|
||||
// ACPI 1.0: use the 32-bit RSDT pointer
|
||||
@@ -120,13 +122,15 @@ namespace and the port grant.
|
||||
|
||||
**The kernel hands the service what it needs and no more.** Reading PM1 event
|
||||
blocks and GPE blocks requires the FADT, which the kernel already parses for its
|
||||
own `\_S5` poweroff. Rather than re-parse, the kernel appends the **FADT as one
|
||||
own power register map (feeding reboot), the PM timer, and the SCI line — the
|
||||
kernel itself has no S5/poweroff path. Rather than re-parse, the kernel appends the **FADT as one
|
||||
more memory resource** on the `acpi-tables` node; the service tells it apart
|
||||
from the AML blob resources by signature — the FADT keeps its intact `"FACP"`
|
||||
header, while the blob resources are header-stripped bytecode that starts with
|
||||
no signature. The kernel's own FADT parse is untouched; the service reads the
|
||||
PM1 *event* blocks (which the kernel never parsed — it only needs PM1 *control*
|
||||
for `\_S5`) and the GPE0/GPE1 blocks straight from its copy. The **SCI itself**
|
||||
PM1 *event* blocks (which the kernel never parsed — it extracts only the PM1
|
||||
*control* register, and it is the service, not the kernel, that writes it for
|
||||
`\_S5`) and the GPE0/GPE1 blocks straight from its copy. The **SCI itself**
|
||||
arrives as the node's one `len == 1` irq resource (distinct from the broad
|
||||
`[0, 256)` window that covers children's legacy lines), which is how the service
|
||||
finds the line to `irq_bind`.
|
||||
@@ -141,8 +145,9 @@ some firmwares boot with it already set), sets `PWRBTN_EN`, and on each SCI:
|
||||
service evaluates its `\_GPE._L%02X` (level) or `_E%02X` (edge) handler
|
||||
method, drains the **Notify** queue that method produced, maps each notified
|
||||
device to an event (battery, AC, lid, or a generic `notify` with its code),
|
||||
and clears the status bit. A missing handler method is clear-and-log, not an
|
||||
error. Making GPEs work required teaching the interpreter one opcode it never
|
||||
and clears the status bit. A missing handler method is not an error: the
|
||||
status bit is cleared and the event silently dropped. Making GPEs work
|
||||
required teaching the interpreter one opcode it never
|
||||
handled — `Notify` (`0x86`) — which it now folds into a bounded queue drained
|
||||
per evaluation; everything else a handler needs (field access, control flow,
|
||||
method calls) was already proven by the ring-3 `_STA`/`_CRS` work.
|
||||
@@ -152,8 +157,9 @@ so GPE/Notify correctness is proven by **host unit tests** — hand-encoded AML
|
||||
with a `Notify` inside a method body, run under `zig build test`. The QEMU
|
||||
`power-button` scenario proves the fixed-event path end to end: a QMP
|
||||
`system_powerdown` injects a real ACPI power-button press, and the service's SCI
|
||||
handler must log it. Battery/AC/lid and the embedded controller's `_Qxx` queries
|
||||
are interface-complete but validated on real hardware later.
|
||||
handler must log it. Battery/AC/lid mapping is interface-complete but validated
|
||||
on real hardware later; the embedded controller's `_Qxx` queries are out of
|
||||
scope.
|
||||
|
||||
The service surface these events are *published on* — subscription, the event
|
||||
vocabulary, and orderly shutdown — is the power service, [power.md](power.md).
|
||||
|
||||
Reference in New Issue
Block a user