Files
danos/system/services/device-manager/device-manager.zig
T
Daniel Samson 47610e8ee2 Device manager (increment 1): discover + match
The device manager is the ring-3 process that turns the device tree into a running
system — the udev-analog. It is mechanism-vs-policy done right: the kernel
enumerates the hardware and enforces the claim capability; this decides which
driver serves which device, using no special privilege (the same device_enumerate
any process could call).

This first increment does the discovery + matching half: system/services/
device-manager enumerates /system/devices, matches each device to a driver by
class (a small static policy table), and logs the decision — finding the HPET
(a timer) and deciding `hpet` serves it. It does not spawn yet: spawning needs a
`system_spawn` system call (the kernel spawns every initial-ramdisk binary in a
loop today), which is the next increment. New `device-manager` test; suite 36/36
plus host tests.
2026-07-10 14:20:14 +01:00

59 lines
2.6 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 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.
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;
// 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 (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
}