Plan the discovery migration (M19 pci-bus, M20 acpi service)

This commit is contained in:
Daniel Samson
2026-07-13 01:15:17 +01:00
parent 7798706b41
commit 470f93a83d
+140
View File
@@ -0,0 +1,140 @@
# M19–M20 execution plan: discovery migration
The operational plan for [device-manager.md](device-manager.md)'s increment 8:
discovery leaves the kernel — a **pci-bus driver** (M19) and an **acpi service**
(M20), with the kernel's device enumeration retired behind them. Same rules as
[m17-m18-plan.md](m17-m18-plan.md): one phase at a time, each green before the
next; this file is the build order and the checklist.
**Definition of green, every phase:** `zig build` clean, `zig build test` clean,
`python3 test/qemu_test.py` passes (existing scenarios plus the phase's new
one), and the relevant design doc updated. Commit per green phase (no co-author
trailers). The full suite is the regression net — the existing
`driver-restart` / `usb-report` / `device-list` / `input` scenarios must stay
green *through* the migration, which is the whole point: the system must not be
able to tell who enumerated it.
**Workflow:** dedicated worktree; branches off `main` — `feat/pci-bus`
(M19.0–19.3), `feat/acpi-service` (M20.1–20.3); auto-merge to main when a
branch is green; keep branches; push everything.
## Settled decisions (2026-07-13 — veto before the loop starts)
1. **What "retiring the kernel scan" means.** The kernel keeps, forever, the
parses it needs before user space exists: RSDP/XSDT location, MADT (SMP),
the HPET table (the tick), FADT + the AML `\_S5` evaluation (poweroff — the
power tests prove it), and MCFG (the host bridge node). What retires is
**device enumeration**: the ECAM function walk (M19.3) and the DSDT/SSDT
namespace walk that builds device nodes (M20.3). The AML module stays a
shared build module compiled into both the kernel (for `\_S5`) and the acpi
service (for everything else) — same source, two builds, no fork.
2. **Bridge apertures come from the firmware memory map, not AML.** Registered
PCI functions carry BAR resources, and containment demands the bridge own
windows that cover them. The apertures are derived kernel-side from the
boot memory map's MMIO holes (regions that are neither RAM nor tables) —
mechanical, AML-free, and available at boot regardless of what later moved
to user space. (The bridge today carries only ECAM + bus range; this is the
prerequisite M19.0 exists for.)
3. **`device_register` becomes idempotent on exact match.** A re-registration
with identical (parent, class, resources) returns the existing id instead
of appending. The kernel table has no unregister, so without this a
restarted registering bus would duplicate its children on every respawn —
idempotence makes restart-and-re-report safe for every future bus, not just
PCI.
4. **The manager matches from reports.** `ChildAdded` gains a `device_id`
field (the kernel-registered id, `no_device` for unregistered leaves like
USB ports). After the M19.3 flip, PCI driver matching keys off reported
identity (the class triple) instead of the manager's boot-time snapshot —
the snapshot match remains only for what the kernel still seeds. One flip
phase changes both sides at once so no device is ever matched twice.
5. **The acpi service's authority is one node.** The kernel publishes an
`acpi-tables` device: memory resources covering the table blobs plus a
broad `io_port` resource — the documented trust grant to exactly one
process (AML OperationRegions reach EC/PM ports; the claim-gated
io_read/io_write calls already exist). The service claims it, maps the
tables, and runs the shared AML module in ring 3 behind a `Hal` backed by
`mmio_map` + `io_read`/`io_write`.
6. **Both new processes are protocol drivers** under the manager: hello,
supervision, restart with backoff — all inherited from M18.1 for free.
Registration idempotence (decision 3) is what makes their restarts sound.
## Status
- [ ] **M19.0** — prerequisites on `feat/pci-bus`: bridge MMIO apertures from
the memory-map holes; `device_register` idempotence (+ kernel unit
checks); `ChildAdded.device_id`; archive note on m17-m18-plan.md.
- [ ] **M19.1** — pci-bus driver, scan only: claim the host bridge, map the
ECAM window, walk bus/device/function headers, log what it finds.
Scenario `pci-scan`: the kernel test compares the driver's reported count
against the broker table's `pci_device` count — equivalence, per class.
- [ ] **M19.2** — register + report: each function registered under the bridge
(config-space slice + BARs, `pci_class` in the descriptor), reported with
`child_added { device_id, identity = class triple }`. Manager mirrors;
spawn-from-reports stays **off**. Scenario extends `pci-scan`:
registered ids resolve, no duplicates after a forced driver restart
(idempotence proven end to end).
- [ ] **M19.3** — the flip: kernel `enumeratePci` call removed (bridge node
stays); manager matches PCI drivers from reports. One commit. The
existing xHCI scenarios (`driver-restart`, `usb-report`, `device-list`)
are the assertion — xhci must come up spawned off a pci-bus report, and
the suite must not be able to tell the difference. discovery.md updated.
- [ ] **merge** `feat/pci-bus` → main, push.
- [ ] **M20.1** — acpi service, parse only: kernel publishes `acpi-tables`
(decision 5); the service claims it, maps the blobs, runs the shared AML
module in ring 3, logs the namespace device count and `_HID`s. Scenario
`acpi-parse`: user-space count equals the kernel walk's count.
- [ ] **M20.2** — register + report: namespace devices with `_HID` + `_CRS`
resources registered under `acpi-tables` (its io_port + the memory-map
holes give containment), reported to the manager. Spawn-from-reports for
ACPI matches stays off. Scenario: the reported set includes the PS/2
keyboard and mouse nodes with their IRQ resources.
- [ ] **M20.3** — the flip: kernel DSDT device-node building removed (static
tables + `\_S5` stay, decision 1); manager matches ACPI-hid drivers
(ps2-bus) from reports. The `input` and `device-manager` scenarios are
the assertion. discovery.md + acpi.md + device-manager.md updated;
device-manager.md increment 8 closed.
- [ ] **merge** `feat/acpi-service` → main, push — **loop ends here**.
---
## Phase notes
**M19.0 apertures:** the boot memory map already crosses the handoff
([boot-handoff]); the holes computation belongs where the bridge node is built
(`parseMcfg`). Sanity-check on QEMU q35: the xHCI BAR (`0xc0000000`-region
values seen in the M18 logs) must land inside a derived aperture, asserted in
the kernel unit test.
**M19.1 scanning without owning config access twice:** the driver reads config
space through its ECAM mmio_map grant of the *bridge* window — the same bytes
the kernel walk read. Vendor-id `0xFFFF` skip, header-type multifunction rule,
no bridge recursion (matches the kernel's current single-segment walk).
**M19.2 BAR sizing:** the classic size probe (write all-ones, read mask,
restore) is deferred — the BARs' current programmed values and types are
enough for containment-checked registration at bring-up; sizing lands with the
first driver that needs to *move* a BAR. Log what is registered so the
scenario can assert it.
**M19.3 what the manager still seeds from the snapshot:** everything the
kernel still enumerates (timers, ACPI nodes until M20.3). The PCI arm of
`pciDriverFor` switches source; `driverFor` doesn't move until M20.3.
**M20.1 Hal in ring 3:** `mapMmio` → `device.mmioMap` over the claimed
acpi-tables node (plus a table-offset map for blobs); `pioRead`/`pioWrite` →
`device.ioRead`/`ioWrite` against its io_port resource. The interpreter cannot
tell it moved — that is the assertion of `acpi-parse`.
**M20.2 containment for `_CRS`:** io ports fall inside the node's broad
io_port resource; MMIO windows (HPET, LAPIC ranges some firmwares list) fall
inside the memory-map holes added to the node in M20.1. Anything that doesn't
fit is logged and skipped, loudly — bring-up honesty over silent drops.
**M20.3 ps2 ordering:** ps2-bus binds nodes the acpi service now reports, so
its spawn moves behind the report (the manager's matching handles this once
the source flips); the `input` scenario proves the keyboard still types.
**Explicitly out of scope:** PCI bridge recursion (single segment, flat bus
walk stays); BAR reprogramming/sizing; hotplug (PCIe native or ACPI);
interrupt routing changes (`_PRT` stays wherever it is today); the USB
descriptor track; multi-segment ECAM.