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.
64 lines
2.8 KiB
Zig
64 lines
2.8 KiB
Zig
//! /system/services/device-manager — the ring-3 process that turns the device
|
|
//! tree into a running system. The kernel enumerates the hardware and enforces the
|
|
//! claim capability (mechanism); this decides *which driver serves which device*
|
|
//! and, eventually, spawns it (policy). Keeping that split in user space is the
|
|
//! whole point of the microkernel: the manager is an ordinary, restartable process
|
|
//! with no special privilege — it uses the same `device_*` system calls any process
|
|
//! could ([drivers.md](../../../docs/drivers.md), [driver-model.md]).
|
|
//!
|
|
//! 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;
|
|
|
|
/// The driver that serves each device class — the policy table. In a fuller system
|
|
/// this comes from the drivers describing what they bind (or a manifest under
|
|
/// /system/drivers); for now it is a small static map, which is enough to prove the
|
|
/// manager reads the tree and decides. `null` = no driver for this class yet.
|
|
fn driverFor(class: u64) ?[]const u8 {
|
|
if (class == @intFromEnum(device.DeviceClass.timer)) return "hpet"; // the HPET
|
|
return null;
|
|
}
|
|
|
|
pub fn main() void {
|
|
// Enumerate into a heap buffer (too big for the one-page user stack).
|
|
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
|
_ = runtime.system.write("device-manager: out of memory\n");
|
|
return;
|
|
};
|
|
const total = device.enumerate(buffer);
|
|
const count = @min(total, buffer.len);
|
|
|
|
var matched: usize = 0;
|
|
for (buffer[0..count]) |descriptor| {
|
|
const driver_name = driverFor(descriptor.class) orelse continue;
|
|
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) {
|
|
_ = runtime.system.write("device-manager: no matchable devices\n");
|
|
return;
|
|
}
|
|
_ = runtime.system.write("device-manager: ok\n");
|
|
while (true) runtime.system.sleep(1000);
|
|
}
|
|
|
|
pub const panic = runtime.panic;
|
|
comptime {
|
|
_ = &runtime.start._start; // pull the runtime entry shim into the image
|
|
}
|