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
+28 -26
View File
@@ -30,10 +30,12 @@
//! stops the logger FIRST — reverse boot order — while fat is still up).
const std = @import("std");
const runtime = @import("runtime");
const fs = @import("file-system");
const ipc = @import("ipc");
const service = @import("service");
const time = @import("time");
const logging = @import("logging");
const system = runtime.system;
const fs = runtime.fs;
/// Where log trees live: the FHS path. The kernel VFS routes /var to whatever
/// volume the fat server mounted there (today: the /var subtree of the USB
@@ -52,13 +54,13 @@ const maximum_files = 24;
const CachedFile = struct {
used: bool = false,
name: [system.maximum_process_name]u8 = undefined,
name: [logging.maximum_process_name]u8 = undefined,
name_len: usize = 0,
file: fs.File = undefined,
};
var files: [maximum_files]CachedFile = @splat(.{});
var endpoint: runtime.ipc.Handle = 0;
var endpoint: ipc.Handle = 0;
/// The drain cursor into the ring's byte stream, and loss accounting.
var cursor: u64 = 0;
@@ -77,7 +79,7 @@ var announced = false;
var ticks_since_record: u32 = 0;
pub fn main() void {
runtime.service.run(64, .{
service.run(64, .{
.init = initialise,
.on_message = onMessage,
.on_notification = onNotification,
@@ -85,19 +87,19 @@ pub fn main() void {
});
}
fn initialise(harness_endpoint: runtime.ipc.Handle) bool {
fn initialise(harness_endpoint: ipc.Handle) bool {
endpoint = harness_endpoint;
const status = system.klogStatus() orelse return false;
const status = logging.klogStatus() orelse return false;
cursor = status.tail;
// Sequence expectations start at the tail record's sequence — discovered on
// the first drain; 0 is right for a fresh boot either way.
formatBootDirectory(status.boot_unix_seconds);
_ = system.timerOnce(endpoint, tick_ms);
_ = time.timerOnce(endpoint, tick_ms);
return true;
}
/// The logger serves no protocol; the ping is answered by the harness.
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 {
_ = message;
_ = reply;
_ = sender;
@@ -106,9 +108,9 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
}
fn onNotification(badge: u64) void {
if (badge & runtime.ipc.notify_timer_bit == 0) return;
if (badge & ipc.notify_timer_bit == 0) return;
tick();
_ = system.timerOnce(endpoint, tick_ms);
_ = time.timerOnce(endpoint, tick_ms);
}
fn onTerminate() void {
@@ -116,12 +118,12 @@ fn onTerminate() void {
// final drain, so the drain carries it into logger.log — a directory whose
// logger.log ends with this marker is complete through shutdown; one that
// doesn't was cut early and may be missing tails.
_ = system.write("logger: shutting down; final flush\n");
_ = logging.write("logger: shutting down; final flush\n");
drain();
closeAll();
// Serial-only epilogue (after the drain, so it reaches no file — by design).
var line: [96]u8 = undefined;
_ = system.write(std.fmt.bufPrint(&line, "logger: flushed through sequence {d}\n", .{next_expected_sequence}) catch return);
_ = logging.write(std.fmt.bufPrint(&line, "logger: flushed through sequence {d}\n", .{next_expected_sequence}) catch return);
}
fn tick() void {
@@ -134,7 +136,7 @@ fn tick() void {
if (!announced) {
announced = true; // once — a periodic line would feed the stream we drain
var line: [128]u8 = undefined;
_ = system.write(std.fmt.bufPrint(&line, "logger: logging to {s}\n", .{boot_directory[0..boot_directory_len]}) catch "");
_ = logging.write(std.fmt.bufPrint(&line, "logger: logging to {s}\n", .{boot_directory[0..boot_directory_len]}) catch "");
}
}
drain();
@@ -148,10 +150,10 @@ fn drain() void {
var chunk: [4096]u8 = undefined;
while (true) {
@memcpy(chunk[0..carry_len], carry[0..carry_len]);
const got = system.klogRead(cursor, chunk[carry_len..]) orelse {
const got = logging.klogRead(cursor, chunk[carry_len..]) orelse {
// Cursor overwritten: re-sync to the ring tail; the sequence gap is
// reported by the next record's header.
const status = system.klogStatus() orelse return;
const status = logging.klogStatus() orelse return;
cursor = status.tail;
carry_len = 0;
continue;
@@ -164,14 +166,14 @@ fn drain() void {
/// Parse whole records out of `bytes`; keep any trailing partial in `carry`.
fn consume(bytes: []u8) void {
const header_size = system.klog_record_header_size;
const header_size = logging.klog_record_header_size;
var offset: usize = 0;
while (bytes.len - offset >= header_size) {
const header = std.mem.bytesToValue(system.KlogRecordHeader, bytes[offset..][0..32]);
if (header.magic != system.klog_record_magic) {
const header = std.mem.bytesToValue(logging.KlogRecordHeader, bytes[offset..][0..32]);
if (header.magic != logging.klog_record_magic) {
// Corrupt frame — should not happen; drop the carry and re-sync.
carry_len = 0;
const status = system.klogStatus() orelse return;
const status = logging.klogStatus() orelse return;
cursor = status.head;
return;
}
@@ -191,7 +193,7 @@ fn consume(bytes: []u8) void {
carry_len = rest;
}
fn deliver(header: system.KlogRecordHeader, name: []const u8, message: []const u8) void {
fn deliver(header: logging.KlogRecordHeader, name: []const u8, message: []const u8) void {
ticks_since_record = 0;
const file = fileFor(if (header.pid == 0 or name.len == 0) "kernel" else name) orelse return;
@@ -218,7 +220,7 @@ fn deliver(header: system.KlogRecordHeader, name: []const u8, message: []const u
_ = file.writeAll(prefix);
} else |_| {}
_ = file.writeAll(message);
if (header.flags & system.klog_flag_truncated != 0) _ = file.writeAll("~");
if (header.flags & logging.klog_flag_truncated != 0) _ = file.writeAll("~");
_ = file.writeAll("\n");
}
@@ -238,7 +240,7 @@ fn fileFor(name: []const u8) ?*fs.File {
}
const cached = slot orelse evictOne() orelse return null;
var path: [base.len + 1 + 19 + 1 + system.maximum_process_name + 4]u8 = undefined;
var path: [base.len + 1 + 19 + 1 + logging.maximum_process_name + 4]u8 = undefined;
const relative = if (name.len != 0 and name[0] == '/') name[1..] else name;
const full = std.fmt.bufPrint(&path, "{s}/{s}.log", .{ boot_directory[0..boot_directory_len], relative }) catch return null;
@@ -278,8 +280,8 @@ fn closeAll() void {
}
}
fn recordLength(header: system.KlogRecordHeader) usize {
return std.mem.alignForward(usize, system.klog_record_header_size + header.name_len + header.message_len, system.klog_record_alignment);
fn recordLength(header: logging.KlogRecordHeader) usize {
return std.mem.alignForward(usize, logging.klog_record_header_size + header.name_len + header.message_len, logging.klog_record_alignment);
}
/// Format the per-boot directory "<base>/YYYY-MM-DDTHHMMSSZ" from the boot