Device manager (increment 2): system_spawn — actually start the driver

Add a `system_spawn(name)` system call: the kernel loads a binary bundled in the
initial-ramdisk, by name, as a fresh ring-3 process. It's the mechanism a user-space
supervisor needs — discovery and policy stay in user space, the kernel only spawns.
The kernel already holds the initial-ramdisk image from the boot handoff; it now
stashes it (process.setInitialRamdisk) so the handler can resolve names, bounds-checks
the name into the user half like debug_write, and returns -1 for an unknown name or a
load failure. Ungated for now (any process may spawn any bundled binary); a spawn
capability belongs here once the model grows one.

The device manager stops logging "would spawn it" and calls runtime.system.spawn on
its matched driver. On QEMU it discovers the HPET, matches `hpet`, and spawns it — and
the driver comes all the way up (claims the timer, maps its MMIO, binds and services
its IRQ, prints "hpet: ok"). The device-manager test now keys on that final marker:
since only the manager is spawned, `hpet: ok` appearing proves the whole
discover -> match -> system_spawn -> driver-up chain end to end.

Transitional: the kernel still auto-spawns the whole initial-ramdisk at boot, so a
real boot briefly double-spawns hpet (the second claim fails harmlessly). Increment 3
removes that redundancy so the manager is the sole owner of driver spawning. Suite
36/36 plus host tests.
This commit is contained in:
Daniel Samson
2026-07-10 18:24:46 +01:00
parent b61b7775b9
commit afbf10f7fc
6 changed files with 79 additions and 12 deletions
@@ -6,11 +6,12 @@
//! with no special privilege — it uses the same `device_*` system calls any process
//! could ([drivers.md](../../../docs/drivers.md), [driver-model.md]).
//!
//! Increment 1 (this file): enumerate /system/devices and *match* each device to a
//! driver, logging the decision. It does not spawn anything yet — spawning needs a
//! `system_spawn` system call (the kernel spawns every initial-ramdisk binary in a
//! loop today; see system/kernel/kernel.zig). Increment 2 adds that call and turns
//! these decisions into actual spawns.
//! Increment 2 (this file): enumerate /system/devices, *match* each device to a
//! driver, and *spawn* it with `system_spawn` — the kernel loads the named binary
//! from the initial-ramdisk as a fresh ring-3 process. On QEMU this discovers the
//! HPET, decides `hpet` serves it, and brings that driver all the way up. (The
//! kernel still auto-spawns the whole initial-ramdisk at boot; increment 3 removes
//! that redundancy so the manager is the sole owner of driver spawning.)
const runtime = @import("runtime");
const device = runtime.device;
@@ -36,12 +37,16 @@ pub fn main() void {
var matched: usize = 0;
for (buffer[0..count]) |descriptor| {
const driver_name = driverFor(descriptor.class) orelse continue;
// Increment 2 will `system_spawn(driver_name)` here; for now, record the
// decision so the policy is observable and testable.
_ = runtime.system.write("device-manager: match ");
_ = runtime.system.write(driver_name);
_ = runtime.system.write(" -> would spawn it\n");
matched += 1;
if (runtime.system.spawn(driver_name)) {
_ = runtime.system.write("device-manager: spawned ");
_ = runtime.system.write(driver_name);
_ = runtime.system.write("\n");
} else {
_ = runtime.system.write("device-manager: failed to spawn ");
_ = runtime.system.write(driver_name);
_ = runtime.system.write("\n");
}
}
if (matched == 0) {