From 8eb4210251cdc7cbcbb1903bccdaa8f0f25aceb8 Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:48:26 +0100 Subject: [PATCH] docs: document the supervision hierarchy and driver discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers.md gains a "How a driver gets started" section — the doc explained what a running driver does but never who starts it. It lays out the three-level supervision hierarchy (kernel spawns init; init spawns the services; the device-manager discovers, matches, and spawns the drivers) and names the two user-space policies that "configure" drivers today: init's service list and the device-manager's match table. driver-model.md's "what exists today" adds system_spawn and the supervision model, and the drivers.md restart bullet is refreshed: a spawning supervisor now exists, a restarting one still doesn't. --- docs/driver-model.md | 10 +++++++-- docs/drivers.md | 52 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/docs/driver-model.md b/docs/driver-model.md index d940ae5..31b5819 100644 --- a/docs/driver-model.md +++ b/docs/driver-model.md @@ -132,9 +132,15 @@ If a class driver needs `mmio`, it has become an HCD and should be one. - **M11** — `irq_bind` / `irq_ack`. IRQ delivered as an IPC notification; mask before EOI; `irq_ack` is the unmask. - **M12** — `parent` in `DeviceDesc`, `device_register` with resource containment. +- **`system_spawn`** — a user-space supervisor starts a driver: `system_spawn(name)` + loads a binary bundled in the initial-ramdisk as a fresh ring-3 process. This is what + turned the device manager from "log the match" into "run the driver": the kernel now + spawns only `init`, `init` spawns the services, and the **device-manager** discovers + the hardware and spawns each driver ([drivers.md](drivers.md)). Ungated for now — a + spawn capability is future work. -So: **bus drivers work now.** HCDs and class drivers do not. Here is exactly why, and -exactly what would fix it. +So: **bus drivers work now, and they're started by the device manager, not the kernel.** +HCDs and class drivers do not work yet. Here is exactly why, and exactly what would fix it. --- diff --git a/docs/drivers.md b/docs/drivers.md index 3197e11..2578f94 100644 --- a/docs/drivers.md +++ b/docs/drivers.md @@ -20,6 +20,49 @@ them for itself: A driver is, in one sentence, *a process that sleeps until its device has something to say.* +## How a driver gets started: discover, match, spawn + +Nothing in the kernel decides that the HPET needs the `hpet` driver — that is policy, +and policy lives in user space. Boot brings user space up as a three-level supervision +hierarchy, each level owning one job: + +``` +kernel ──spawns──► init (PID 1) ──spawns──► device-manager ──spawns──► hpet + | | | + spawns only init, the service supervisor: the driver supervisor: enumerates + publishes the starts the system /system/devices, matches each device + initial-ramdisk services (vfs, the to a driver, and system_spawn's it + so user space can device-manager). Its + system_spawn from it list is init policy. +``` + +The kernel launches exactly one process — `init` — and hands it nothing but the raw +ability to start more (`system_spawn(name)`, which loads a binary bundled in the +initial-ramdisk as a fresh ring-3 process). Everything else is a user-space decision: + +- **init** ([system/services/init](system/services/init/init.zig)) is the **service + supervisor**. It spawns the system services danos brings up at boot — today `vfs` and + the `device-manager` — from a small list. Drivers are deliberately *not* its job. +- **device-manager** ([system/services/device-manager](system/services/device-manager/device-manager.zig)) + is the **driver supervisor**. It does the three steps a monolithic kernel would do in + its probe path, entirely from ring 3: + 1. **Discover** — `device_enumerate` snapshots the device table the kernel built from + ACPI/PCI ([discovery](discovery.md)). + 2. **Match** — for each device it looks up a driver by `DeviceClass`. The match policy + is a table (`driverFor`): today a static `timer → hpet` map; a fuller system reads + what each driver *binds* (a manifest under `/system/drivers`, or the driver + describing its own match). + 3. **Spawn** — `system_spawn(driver_name)` starts the matched driver, which then claims + its device and runs the event loop below. + +So "how is a driver discovered and configured" has two halves: **discovery** is the +kernel's device table, read by anyone; **configuration** is two user-space policies — +init's service list and the device-manager's match table. Both are hardcoded in their +respective programs today; the natural next step is to move them into `/etc` (see the +milestone notes in [driver-model.md](driver-model.md)). `system_spawn` is currently +ungated — any process may spawn any bundled binary — because there is no spawn +capability yet. + ## The capability: claim before touch The driver syscall numbers (`system/abi.zig`) with the device types they carry @@ -312,9 +355,12 @@ controller drivers), and the IOMMU — have proposed signatures in - **Unregistering children.** `device_register` only appends. A USB device that is unplugged cannot be removed, and a bus driver in a loop can exhaust the 64-entry table. -- **Restart.** A driver that dies should release its claim, have its device quiesced, - and be respawned by a supervisor. Some pieces (`releaseIrqs`, `device_grant` - teardown, the claim table) exist; the policy doesn't. +- **Restart.** A supervisor that *spawns* drivers now exists — the device-manager starts + them with `system_spawn` — but a supervisor that *restarts* them does not. A driver that + dies should release its claim, have its device quiesced, and be respawned; today nothing + notices the death. Some pieces (`releaseIrqs`, `device_grant` teardown, the claim table) + exist, and `dev_release` (below) is the missing mechanism; the restart policy is the + resilience track ([resilience.md](resilience.md)). - **Interrupt priority / threaded IRQ latency.** `notifyFromIsr` enqueues the woken driver but doesn't preempt (`wakeLocked` deliberately leaves that to the caller), so a woken driver waits for the next scheduling point.