C2: migrate consumers off the runtime shim to direct concern-module imports

Every user binary and the two device-logic library modules (pci, usb) now
`@import` the concern modules directly instead of aliasing through `runtime`:

  runtime.ipc/process/time/service/input/block/display  -> @import("<module>")
  runtime.device / runtime.device_manager               -> @import("driver")
  runtime.fs                                             -> @import("file-system")
  runtime.Thread                                         -> @import("thread").Thread
  runtime.system.{write,writeRecord,klog*}              -> logging.*
  runtime.system.{sleep,timerOnce,wallClock,clock}     -> time.*
  runtime.system.{spawn*,kill,exit,yield,processes,...}-> process.*
  runtime.system.{mmap,munmap,PROT_*}                  -> memory.*
  runtime.dma.* / runtime.shared_memory.* / runtime.allocator -> memory.*

Each consumer keeps its own alias name (e.g. `const device = @import("driver")`),
so call sites are unchanged and there are no collisions with local `driver`
variables. build.zig now injects the concern modules into every user binary via
`default_imports`; pci/usb module import lists were updated to match.

The `runtime` and `system` shims remain for one more step (root.zig still uses
runtime); they are deleted in C5. Nothing but root.zig imports `runtime` now.

Verified: zig build, zig build test, and 17 QEMU cases (smoke, device-manager,
logger, fat-mount, fat-mutations, usb-storage, usb-hid, display-native,
virtio-gpu, input, thread-spawn, thread-mutex, process-kill, shared-memory,
driver-restart, acpi-ps2, pci-scan).
This commit is contained in:
Daniel Samson
2026-07-22 23:28:34 +01:00
parent dded46726b
commit 23bcd77c58
37 changed files with 783 additions and 692 deletions
@@ -16,13 +16,17 @@
//! restart. Tree reports (`child_added`) land in M18.2.
const std = @import("std");
const runtime = @import("runtime");
const device = @import("driver");
const ipc = @import("ipc");
const process = @import("process");
const service = @import("service");
const time = @import("time");
const memory = @import("memory");
const logging = @import("logging");
const acpi_ids = @import("acpi-ids");
const pci_class = @import("pci-class");
const usb_ids = @import("usb-ids");
const device_manager_protocol = @import("device-manager-protocol");
const device = runtime.device;
const system = runtime.system;
/// The PCI class/subclass/prog-IF triple of an xHCI (USB 3) host controller —
/// Serial Bus Controller / USB Controller / XHCI — named from pci-class.zig rather
@@ -154,7 +158,7 @@ const Driver = struct {
const maximum_drivers = 16;
var drivers: [maximum_drivers]Driver = .{Driver{}} ** maximum_drivers;
var manager_endpoint: runtime.ipc.Handle = 0;
var manager_endpoint: ipc.Handle = 0;
var test_restart_mode = false;
var test_usb_restart_mode = false;
var test_usb_killed = false;
@@ -169,14 +173,14 @@ var test_kill_due_ns: u64 = 0;
/// buffered message. A subscriber whose endpoint stops accepting (it died) is
/// dropped on the failed send.
const maximum_subscribers = 8;
var subscribers: [maximum_subscribers]?runtime.ipc.Handle = .{null} ** maximum_subscribers;
var subscribers: [maximum_subscribers]?ipc.Handle = .{null} ** maximum_subscribers;
/// Publish one event (a ChildAdded or ChildRemoved struct, the same encoding
/// the bus drivers send) to every subscriber.
fn publishEvent(event: []const u8) void {
for (&subscribers) |*slot| {
if (slot.*) |handle| {
if (!runtime.ipc.send(handle, event)) slot.* = null; // dead subscriber
if (!ipc.send(handle, event)) slot.* = null; // dead subscriber
}
}
}
@@ -284,17 +288,17 @@ fn spawnDriver(driver: *Driver) void {
arguments[0] = std.fmt.bufPrint(&id_text, "{d}", .{driver.device_id}) catch return;
argument_count = 1;
}
const child = system.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
const child = process.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
std.log.info("failed to spawn {s}", .{driver.name()});
driver.state = .failed;
return;
};
driver.process_id = child;
driver.spawn_ns = system.clock();
driver.spawn_ns = time.clock();
if (driver.speaks_protocol) {
driver.state = .awaiting_hello;
driver.hello_deadline_ns = driver.spawn_ns + hello_deadline_ms * 1_000_000;
_ = system.timerOnce(manager_endpoint, hello_deadline_ms + 100);
_ = time.timerOnce(manager_endpoint, hello_deadline_ms + 100);
} else {
driver.state = .running;
}
@@ -310,13 +314,13 @@ fn spawnDriver(driver: *Driver) void {
/// restarts with backoff until the crash-loop cap.
fn onDriverExit(driver: *Driver) void {
pruneChildrenOf(driver.process_id);
const reason = runtime.process.exitReason(driver.process_id) orelse .fault;
const reason = process.exitReason(driver.process_id) orelse .fault;
if (reason == .exited) {
driver.state = .stopped;
std.log.info("{s} exited cleanly; not restarting", .{driver.name()});
return;
}
const now = system.clock();
const now = time.clock();
const alive_ns = now - driver.spawn_ns;
driver.restarts = if (alive_ns < fast_death_ns) driver.restarts + 1 else 1;
if (driver.restarts >= crash_loop_cap) {
@@ -328,7 +332,7 @@ fn onDriverExit(driver: *Driver) void {
driver.state = .restarting;
driver.restart_due_ns = now + delay_ms * 1_000_000;
std.log.info("restarting {s} in {d} ms (died: {s})", .{ driver.name(), delay_ms, @tagName(reason) });
_ = system.timerOnce(manager_endpoint, delay_ms + 50);
_ = time.timerOnce(manager_endpoint, delay_ms + 50);
}
/// A timer landed: sweep every deadline. Overdue hellos are killed (the exit
@@ -336,10 +340,10 @@ fn onDriverExit(driver: *Driver) void {
/// respawn. Timers carry no id on purpose — the table is the state, and one
/// sweep serves every armed deadline.
fn sweepDeadlines() void {
const now = system.clock();
const now = time.clock();
if (test_kill_pid != 0 and now >= test_kill_due_ns) {
std.log.info("test mode: killing the reporter", .{});
_ = system.kill(test_kill_pid);
_ = process.kill(test_kill_pid);
test_kill_pid = 0;
}
for (&drivers) |*driver| {
@@ -347,7 +351,7 @@ fn sweepDeadlines() void {
switch (driver.state) {
.awaiting_hello => if (now >= driver.hello_deadline_ns) {
std.log.info("{s} missed its hello deadline", .{driver.name()});
_ = system.kill(driver.process_id);
_ = process.kill(driver.process_id);
// The exit notification finishes the job via onDriverExit.
},
.restarting => if (now >= driver.restart_due_ns) spawnDriver(driver),
@@ -358,12 +362,12 @@ fn sweepDeadlines() void {
// --- the harness callbacks -----------------------------------------------------
fn initialise(endpoint: runtime.ipc.Handle) bool {
fn initialise(endpoint: ipc.Handle) bool {
manager_endpoint = endpoint;
// 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("/system/services/device-manager: out of memory\n");
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = logging.write("/system/services/device-manager: out of memory\n");
return false;
};
const total = device.enumerate(buffer);
@@ -400,14 +404,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
}
if (matched == 0) {
_ = runtime.system.write("/system/services/device-manager: no matchable devices\n");
_ = logging.write("/system/services/device-manager: no matchable devices\n");
} else {
_ = runtime.system.write("/system/services/device-manager: ok\n");
_ = logging.write("/system/services/device-manager: ok\n");
}
return true;
}
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
if (message.len < 1) return 0;
switch (message[0]) {
@intFromEnum(device_manager_protocol.Operation.child_added) => return onChildAdded(message, reply, sender),
@@ -432,8 +436,8 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
if (test_scanout_restart_mode and !test_scanout_killed and std.mem.eql(u8, driver.name(), "/system/drivers/virtio-gpu")) {
test_scanout_killed = true;
test_kill_pid = sender;
test_kill_due_ns = system.clock() + 1_500_000_000;
_ = system.timerOnce(manager_endpoint, 1600);
test_kill_due_ns = time.clock() + 1_500_000_000;
_ = time.timerOnce(manager_endpoint, 1600);
}
} else {
status = -1;
@@ -489,8 +493,8 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
// (M19.0 idempotence, proven end to end by pci-scan).
test_usb_killed = true;
test_kill_pid = sender;
test_kill_due_ns = system.clock() + 1_000_000_000;
_ = system.timerOnce(manager_endpoint, 1100);
test_kill_due_ns = time.clock() + 1_000_000_000;
_ = time.timerOnce(manager_endpoint, 1100);
}
}
}
@@ -503,8 +507,8 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
// needs a window to enumerate and subscribe before the events.
test_usb_killed = true;
test_kill_pid = sender;
test_kill_due_ns = system.clock() + 2_000_000_000;
_ = system.timerOnce(manager_endpoint, 2100);
test_kill_due_ns = time.clock() + 2_000_000_000;
_ = time.timerOnce(manager_endpoint, 2100);
}
}
}
@@ -547,7 +551,7 @@ fn onEnumerate(reply: []u8) usize {
}
/// An application subscribed: its endpoint arrived as the call's capability.
fn onSubscribe(reply: []u8, capability: ?runtime.ipc.Handle) usize {
fn onSubscribe(reply: []u8, capability: ?ipc.Handle) usize {
var status: i32 = -1;
if (capability) |handle| {
for (&subscribers) |*slot| {
@@ -564,22 +568,22 @@ fn onSubscribe(reply: []u8, capability: ?runtime.ipc.Handle) usize {
}
fn onNotification(badge: u64) void {
if (badge & runtime.ipc.notify_exit_bit != 0) {
const dead: u32 = @intCast(badge & ~(runtime.ipc.notify_badge_bit | runtime.ipc.notify_exit_bit));
if (badge & ipc.notify_exit_bit != 0) {
const dead: u32 = @intCast(badge & ~(ipc.notify_badge_bit | ipc.notify_exit_bit));
if (driverByProcess(dead)) |driver| onDriverExit(driver);
return;
}
if (badge & runtime.ipc.notify_timer_bit != 0) sweepDeadlines();
if (badge & ipc.notify_timer_bit != 0) sweepDeadlines();
}
pub fn main(init: runtime.process.Init) void {
pub fn main(init: process.Init) void {
if (init.arguments.get(1)) |mode| {
test_restart_mode = std.mem.eql(u8, mode, "test-restart");
test_usb_restart_mode = std.mem.eql(u8, mode, "test-usb-restart");
test_pci_restart_mode = std.mem.eql(u8, mode, "test-pci-restart");
test_scanout_restart_mode = std.mem.eql(u8, mode, "test-scanout-restart");
}
runtime.service.run(device_manager_protocol.message_maximum, .{
service.run(device_manager_protocol.message_maximum, .{
.service = .device_manager,
.init = initialise,
.on_message = onMessage,