runtime: std.log for every user binary — kernel-stamped attribution

library/runtime/log.zig wires std.log to the tagged ring: the root shim
installs std_options for every binary (programs may override), logFn
formats one line per record and emits it with its level via
debug_write — the payload no longer carries the process's name; the
kernel stamps identity structurally and the serial renderer prints the
'<binary path>: ' prefix, so the transcript keeps its shape.

Migrate every writeLine/logLine/log helper family (init, vfs, fat,
device-manager, acpi, pci-bus, ps2-bus x3, usb-hid x2, usb-storage,
usb-xhci-bus, virtio-gpu — 104 call sites) to std.log.info, dropping
the hand-written prefixes. A leveled record is a complete line by
contract (raw emissions may still build lines from pieces). Test
fixtures and the display/input services keep raw writes for now — their
ring records are attributed by the kernel regardless.

Harness regexes follow the renamed prefixes (usb-hid-keyboard,
discovery) and path-named restart lines.
This commit is contained in:
Daniel Samson
2026-07-21 15:39:51 +01:00
parent 9f18d8340e
commit f480c5d790
19 changed files with 172 additions and 193 deletions
+51
View File
@@ -0,0 +1,51 @@
//! The per-process logger: std.log wired to the tagged kernel log ring.
//!
//! A program just calls `std.log.info("mounted {s}", .{path})` (or a scoped
//! logger); this backend formats the line into a fixed buffer and emits ONE
//! `debug_write` record carrying the level. The kernel stamps the record with
//! the sender's pid and task name (its binary path) — the process does NOT put
//! its own name in the payload; attribution is the kernel's, structural and
//! unforgeable. Serial shows the kernel-rendered `<path>: message` line, and
//! the logger service demultiplexes the ring into one file per process.
//!
//! Installed for every user binary by the root shim (library/runtime/root.zig)
//! via `std_options`; a program can override by declaring its own
//! `pub const std_options`.
const std = @import("std");
const system = @import("system.zig");
fn levelOf(comptime level: std.log.Level) system.KlogLevel {
return switch (level) {
.err => .err,
.warn => .warn,
.info => .info,
.debug => .debug,
};
}
pub fn logFn(
comptime level: std.log.Level,
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
// One record = one line = at most klog_maximum_message bytes of payload.
// On overflow keep what fits and end with "~" so the record is still a
// whole line (the kernel would split an embedded rest anyway).
var buffer: [256]u8 = undefined;
const prefix = if (scope == .default) "" else "(" ++ @tagName(scope) ++ ") ";
const line = std.fmt.bufPrint(&buffer, prefix ++ format, args) catch truncated: {
buffer[buffer.len - 1] = '~';
break :truncated buffer[0..];
};
_ = system.writeRecord(levelOf(level), line);
}
/// The std.Options the root shim installs unless the program overrides it.
/// Debug level: filtering is the log *reader's* job here — the ring is cheap,
/// serial is a dev convenience, and the logger service keeps everything.
pub const default_options: std.Options = .{
.log_level = .debug,
.logFn = logFn,
};
+6
View File
@@ -14,6 +14,12 @@ pub const main = program.main;
/// The panic handler for every safety check in the image (runtime.start.panic).
pub const panic = runtime.panic;
/// std.log for every user binary goes to the tagged kernel log ring (the kernel
/// stamps the sender; see runtime.log). A program overrides by declaring its
/// own `pub const std_options`.
pub const std_options: @import("std").Options =
if (@hasDecl(program, "std_options")) program.std_options else runtime.log.default_options;
comptime {
_ = &runtime.start._start; // pull the runtime entry shim into the image
}
+1
View File
@@ -11,6 +11,7 @@
//! nothing to declare per source file.
pub const system = @import("system.zig");
pub const log = @import("log.zig");
/// Monotonic time, delays, and deadlines over the kernel clock/sleep/timer syscalls
/// — an `Instant`/`Duration` front door, no time service (docs/timers.md).
pub const time = @import("time.zig");
+6 -11
View File
@@ -16,11 +16,6 @@ const protocol = runtime.device_manager_protocol;
const device = runtime.device;
const pci_class = @import("pci-class");
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
/// Log a discovered function with its (class / subclass / prog-IF) triple decoded
/// to human names — the boot-log breadcrumb that says *what* the hardware is, so
/// "class 0x01 (Mass Storage Controller) subclass 0x06 (Serial ATA Controller)
@@ -74,7 +69,7 @@ fn configWrite16(bus: u64, dev: u64, function: u64, offset: u64, value: u16) voi
fn initialise(endpoint: runtime.ipc.Handle) bool {
_ = endpoint;
if (!device.claim(bridge_id)) {
writeLine("/system/drivers/pci-bus: unable to claim bridge device {d}\n", .{bridge_id});
std.log.info("unable to claim bridge device {d}", .{bridge_id});
return false;
}
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
@@ -85,7 +80,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
if (d.id == bridge_id) break d;
} else {
writeLine("/system/drivers/pci-bus: device {d} not in the device tree\n", .{bridge_id});
std.log.info("device {d} not in the device tree", .{bridge_id});
return false;
};
// Resource 0 is the ECAM window (1 MiB of config space per bus); the bus
@@ -159,7 +154,7 @@ fn scan() void {
}
}
}
writeLine("/system/drivers/pci-bus: {d} functions found\n", .{found});
std.log.info("{d} functions found", .{found});
}
/// Register one function under the bridge and report it to the manager. The
@@ -229,7 +224,7 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
}
const registered = device.register(bridge_id, &descriptor) orelse {
writeLine("/system/drivers/pci-bus: register refused for {d}:{d}.{d}\n", .{ bus, dev, function });
std.log.info("register refused for {d}:{d}.{d}", .{ bus, dev, function });
return;
};
const report = protocol.ChildAdded{
@@ -240,7 +235,7 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
};
var reply: [protocol.message_maximum]u8 = undefined;
_ = runtime.ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
writeLine("/system/drivers/pci-bus: child report for {d}:{d}.{d} failed\n", .{ bus, dev, function });
std.log.info("child report for {d}:{d}.{d} failed", .{ bus, dev, function });
};
}
@@ -255,7 +250,7 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
pub fn main(init: runtime.process.Init) void {
const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent
bridge_id = std.fmt.parseInt(u64, argument, 10) catch {
writeLine("/system/drivers/pci-bus: malformed bridge device id '{s}'\n", .{argument});
std.log.info("malformed bridge device id '{s}'", .{argument});
return;
};
runtime.service.run(protocol.message_maximum, .{
+3 -8
View File
@@ -23,11 +23,6 @@ const device = runtime.device;
const ipc = runtime.ipc;
const protocol = runtime.input_protocol;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
/// registering) is still coming up.
fn lookupBus() ?ipc.Handle {
@@ -75,14 +70,14 @@ pub fn main(init: runtime.process.Init) void {
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: no HID argument\n");
return;
}
writeLine("/system/drivers/ps2-bus/keyboard: starting for hid {s}\n", .{hid});
std.log.info("starting for hid {s}", .{hid});
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
return;
};
if (device.findDeviceDescriptorByHid(buffer, hid) == null) {
writeLine("/system/drivers/ps2-bus/keyboard: no device for hid {s}\n", .{hid});
std.log.info("no device for hid {s}", .{hid});
return;
}
@@ -90,7 +85,7 @@ pub fn main(init: runtime.process.Init) void {
// absent (as today) it defaults to us.
const layout_name = init.arguments.get(2) orelse "us";
const layout = xkb.byName(layout_name) orelse xkb.us;
writeLine("/system/drivers/ps2-bus/keyboard: layout {s}\n", .{layout.name});
std.log.info("layout {s}", .{layout.name});
// Attach to the bus: hand it our endpoint, and it forwards every byte the
// keyboard sends (it owns the controller; we own the decoding).
+2 -7
View File
@@ -22,11 +22,6 @@ const device = runtime.device;
const ipc = runtime.ipc;
const protocol = runtime.input_protocol;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
/// registering) is still coming up.
fn lookupBus() ?ipc.Handle {
@@ -54,14 +49,14 @@ pub fn main(init: runtime.process.Init) void {
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
return;
}
writeLine("/system/drivers/ps2-bus/mouse: starting for hid {s}\n", .{hid});
std.log.info("starting for hid {s}", .{hid});
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: out of memory\n");
return;
};
if (ps2.findMouseDescriptor(buffer) == null) {
writeLine("/system/drivers/ps2-bus/mouse: no device for hid {s}\n", .{hid});
std.log.info("no device for hid {s}", .{hid});
return;
}
+5 -12
View File
@@ -16,13 +16,6 @@ const ps2 = @import("ps2-library.zig");
const device = runtime.device;
const ipc = runtime.ipc;
/// Format one whole log line and emit it in a single `debug_write`, so output
/// from the child drivers (which run concurrently) can never interleave with it.
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
/// Ask the device on `port` what it is, then spawn the matching driver from the
/// initial-ramdisk, handing it the device's HID as argv[1]. The driver is chosen
/// from what the device reports, not from the port number. Returns the identified
@@ -30,19 +23,19 @@ fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
/// attaches, or null if nothing was spawned.
fn spawnIdentifiedDriver(controller: ps2.Controller, port: ps2.Port) ?ps2.DeviceType {
const device_type = controller.identifyDevice(port) orelse {
writeLine("/system/drivers/ps2-bus: identify timed out on port {s}\n", .{@tagName(port)});
std.log.info("identify timed out on port {s}", .{@tagName(port)});
return null;
};
const driver_name = device_type.driverName() orelse {
writeLine("/system/drivers/ps2-bus: unrecognized device on port {s}\n", .{@tagName(port)});
std.log.info("unrecognized device on port {s}", .{@tagName(port)});
return null;
};
const hid = device_type.hid() orelse "";
if (runtime.system.spawnWithArguments(driver_name, &.{hid}) != null) {
writeLine("/system/drivers/ps2-bus: port {s} is a {s}, spawned {s}\n", .{ @tagName(port), hid, driver_name });
std.log.info("port {s} is a {s}, spawned {s}", .{ @tagName(port), hid, driver_name });
return device_type;
}
writeLine("/system/drivers/ps2-bus: failed to spawn {s}\n", .{driver_name});
std.log.info("failed to spawn {s}", .{driver_name});
return null;
}
@@ -83,7 +76,7 @@ fn handleAttach(message: []const u8, got: ipc.Received, out: []u8) usize {
const device_type = maybe_type orelse continue;
if (@intFromEnum(device_type) != request.device_type) continue;
port_endpoints[port_index] = endpoint;
writeLine("/system/drivers/ps2-bus: {s} driver attached\n", .{@tagName(device_type)});
std.log.info("{s} driver attached", .{@tagName(device_type)});
return reply.write(out, .ok);
}
return reply.write(out, .no_such_device);
+3 -8
View File
@@ -23,11 +23,6 @@ const ipc = runtime.ipc;
const process = runtime.process;
const input_protocol = runtime.input_protocol;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
// The modifier state a character lookup needs — derived from the report's
// modifier byte, plus the driver-tracked caps-lock toggle.
const ModifierSnapshot = struct {
@@ -71,7 +66,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
writeLine("/system/drivers/usb-hid/keyboard: malformed device id '{s}'\n", .{argument});
std.log.info("malformed device id '{s}'", .{argument});
return;
};
const layout = xkb.byName(init.arguments.get(2) orelse "us") orelse xkb.us;
@@ -82,7 +77,7 @@ pub fn main(init: runtime.process.Init) void {
return;
}
var device = runtime.usb.open(device_id) orelse {
writeLine("/system/drivers/usb-hid/keyboard: could not open device {d}\n", .{device_id});
std.log.info("could not open device {d}", .{device_id});
return;
};
const endpoint = device.findEndpoint(runtime.usb.transfer_type_interrupt, true) orelse {
@@ -104,7 +99,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
_ = process.bindSignals(device.endpoint);
writeLine("/system/drivers/usb-hid/keyboard: ok (device {d}, interface {d}, layout {s})\n", .{ device_id, device.interface_number, layout.name });
std.log.info("ok (device {d}, interface {d}, layout {s})", .{ device_id, device.interface_number, layout.name });
var decoder = hid.KeyboardDecoder{};
var caps_lock = false;
+3 -8
View File
@@ -18,11 +18,6 @@ const ipc = runtime.ipc;
const process = runtime.process;
const input_protocol = runtime.input_protocol;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
// The current pressed-button bitmask in input-protocol terms.
fn buttonMask(buttons: u8) u32 {
var mask: u32 = 0;
@@ -38,7 +33,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
writeLine("/system/drivers/usb-hid/mouse: malformed device id '{s}'\n", .{argument});
std.log.info("malformed device id '{s}'", .{argument});
return;
};
@@ -47,7 +42,7 @@ pub fn main(init: runtime.process.Init) void {
return;
}
var device = runtime.usb.open(device_id) orelse {
writeLine("/system/drivers/usb-hid/mouse: could not open device {d}\n", .{device_id});
std.log.info("could not open device {d}", .{device_id});
return;
};
const endpoint = device.findEndpoint(runtime.usb.transfer_type_interrupt, true) orelse {
@@ -67,7 +62,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
_ = process.bindSignals(device.endpoint);
writeLine("/system/drivers/usb-hid/mouse: ok (device {d}, interface {d})\n", .{ device_id, device.interface_number });
std.log.info("ok (device {d}, interface {d})", .{ device_id, device.interface_number });
var previous_buttons: u8 = 0;
var receive: [64]u8 = undefined;
+4 -9
View File
@@ -18,11 +18,6 @@ const bot = @import("bulk-only-transport.zig");
const block_protocol = @import("block-protocol");
const dma = runtime.dma;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
var device_id: u64 = 0;
var device: runtime.usb.Device = undefined;
var bulk_in: runtime.usb.Endpoint = undefined;
@@ -73,7 +68,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
return false;
}
device = runtime.usb.open(device_id) orelse {
writeLine("/system/drivers/usb-storage: could not open device {d}\n", .{device_id});
std.log.info("could not open device {d}", .{device_id});
return false;
};
bulk_in = device.findEndpoint(runtime.usb.transfer_type_bulk, true) orelse {
@@ -112,14 +107,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
const capacity = scsi.parseCapacity(capacity_bytes);
block_size = capacity.block_size;
block_count = @as(u64, capacity.last_lba) + 1;
writeLine("/system/drivers/usb-storage: ready ({d} blocks x {d} bytes)\n", .{ block_count, block_size });
std.log.info("ready ({d} blocks x {d} bytes)", .{ block_count, block_size });
// Self-check: read block 0 and log its trailing signature (0x55AA for a boot
// sector) — proof READ(10) works end to end over the bulk path.
const read0 = scsi.read10(0, 1);
if (block_size <= 4096 and transact(&read0, true, command_data.physical, block_size)) {
const sector: [*]const u8 = @ptrFromInt(command_data.virtual);
writeLine("/system/drivers/usb-storage: block 0 signature 0x{x:0>2}{x:0>2}\n", .{ sector[510], sector[511] });
std.log.info("block 0 signature 0x{x:0>2}{x:0>2}", .{ sector[510], sector[511] });
}
return true;
}
@@ -171,7 +166,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
device_id = std.fmt.parseInt(u64, argument, 10) catch {
writeLine("/system/drivers/usb-storage: malformed device id '{s}'\n", .{argument});
std.log.info("malformed device id '{s}'", .{argument});
return;
};
runtime.service.run(block_protocol.message_maximum, .{
+14 -21
View File
@@ -64,13 +64,6 @@ fn reportEndpointFor(device_token: u64) ?usize {
return null;
}
/// Format one whole log line and emit it in a single `debug_write`, so
/// concurrent instances (one per controller) can never interleave mid-line.
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
var controller_id: u64 = protocol.no_device;
/// Claim the assigned controller, find its register window, and hello the
@@ -79,7 +72,7 @@ var controller_id: u64 = protocol.no_device;
fn initialise(endpoint: runtime.ipc.Handle) bool {
service_endpoint = endpoint;
if (!device.claim(controller_id)) {
writeLine("/system/drivers/usb-xhci-bus: unable to claim controller device {d}\n", .{controller_id});
std.log.info("unable to claim controller device {d}", .{controller_id});
return false;
}
@@ -92,7 +85,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
if (d.id == controller_id) break d;
} else {
writeLine("/system/drivers/usb-xhci-bus: device {d} not in the device tree\n", .{controller_id});
std.log.info("device {d} not in the device tree", .{controller_id});
return false;
};
@@ -105,10 +98,10 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
break resource;
}
} else {
writeLine("/system/drivers/usb-xhci-bus: controller device {d} has no register BAR\n", .{controller_id});
std.log.info("controller device {d} has no register BAR", .{controller_id});
return false;
};
writeLine("/system/drivers/usb-xhci-bus: claimed controller device {d} (registers at 0x{x}, {d} bytes)\n", .{
std.log.info("claimed controller device {d} (registers at 0x{x}, {d} bytes)", .{
controller_id,
register_window.start,
register_window.len,
@@ -124,7 +117,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
return false;
};
writeLine("/system/drivers/usb-xhci-bus: controller running ({d} slots, {d}-byte contexts)\n", .{
std.log.info("controller running ({d} slots, {d}-byte contexts)", .{
controller.?.max_slots,
controller.?.context_size,
});
@@ -198,7 +191,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
return;
};
writeLine("/system/drivers/usb-xhci-bus: {d} root-hub ports\n", .{engine.max_ports});
std.log.info("{d} root-hub ports", .{engine.max_ports});
var port: u32 = 1;
var connected: u32 = 0;
@@ -207,17 +200,17 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
if (port_status & 1 == 0) continue; // CCS: nothing connected
connected += 1;
const speed = (port_status >> 10) & 0xF; // the PORTSC port-speed class
writeLine("/system/drivers/usb-xhci-bus: port {d} connected — {s} (speed class {d})\n", .{ port, speedName(speed), speed });
std.log.info("port {d} connected — {s} (speed class {d})", .{ port, speedName(speed), speed });
const usb_device = engine.setupDevice(port, speed) orelse {
writeLine("/system/drivers/usb-xhci-bus: port {d} device setup failed\n", .{port});
std.log.info("port {d} device setup failed", .{port});
continue;
};
if (!engine.enumerate(usb_device)) {
writeLine("/system/drivers/usb-xhci-bus: port {d} enumeration failed\n", .{port});
std.log.info("port {d} enumeration failed", .{port});
continue;
}
writeLine("/system/drivers/usb-xhci-bus: port {d} device vendor 0x{x:0>4} product 0x{x:0>4}, {d} interface(s)\n", .{
std.log.info("port {d} device vendor 0x{x:0>4} product 0x{x:0>4}, {d} interface(s)", .{
port,
usb_device.device_descriptor.vendor_id,
usb_device.device_descriptor.product_id,
@@ -259,7 +252,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
descriptor.hid_len = hid_text.len;
@memcpy(descriptor.hid[0..hid_text.len], hid_text);
const registered = device.register(controller_id, &descriptor) orelse {
writeLine("/system/drivers/usb-xhci-bus: register refused for port {d} interface {d}\n", .{ port, interface.number });
std.log.info("register refused for port {d} interface {d}", .{ port, interface.number });
return null;
};
@@ -271,10 +264,10 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
};
var reply: [protocol.message_maximum]u8 = undefined;
_ = runtime.ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
writeLine("/system/drivers/usb-xhci-bus: child report for port {d} interface {d} failed\n", .{ port, interface.number });
std.log.info("child report for port {d} interface {d} failed", .{ port, interface.number });
return null;
};
writeLine("/system/drivers/usb-xhci-bus: port {d} interface {d} class {d}/{d}/{d} registered as device {d}\n", .{
std.log.info("port {d} interface {d} class {d}/{d}/{d} registered as device {d}", .{
port,
interface.number,
interface.class,
@@ -407,7 +400,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
writeLine("/system/drivers/usb-xhci-bus: malformed controller device id '{s}'\n", .{argument});
std.log.info("malformed controller device id '{s}'", .{argument});
return;
};
runtime.service.run(transfer.message_maximum, .{
+33 -40
View File
@@ -103,13 +103,6 @@ var surface: shm.Region = undefined;
var avail_shadow: u16 = 0;
var used_shadow: u16 = 0;
/// Format one whole log line and emit it in a single `write`, so this driver's output can
/// never interleave mid-line with the other drivers the manager runs concurrently.
fn log(comptime fmt: []const u8, arguments: anytype) void {
var line: [160]u8 = undefined;
_ = system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
// --- common-config register access (little-endian MMIO at `common_base`) ---------------
fn cfgRead(comptime T: type, comptime field: []const u8) T {
@@ -154,7 +147,7 @@ fn mapBar(config: usize, descriptor: *const device.DeviceDescriptor, bar: u8) ?u
return v;
}
}
log("virtio-gpu: BAR {d} (physical 0x{x}) is not a mapped resource\n", .{ bar, base });
std.log.info("BAR {d} (physical 0x{x}) is not a mapped resource", .{ bar, base });
return null;
}
@@ -162,7 +155,7 @@ fn mapBar(config: usize, descriptor: *const device.DeviceDescriptor, bar: u8) ?u
/// notify structures (the only two V3 needs). Returns false if either is missing.
fn walkCapabilities(config: usize, descriptor: *const device.DeviceDescriptor) bool {
if (mmio.read(u16, config + 0x06) & 0x10 == 0) { // Status bit 4: capabilities list present
log("virtio-gpu: device has no PCI capability list\n", .{});
std.log.info("device has no PCI capability list", .{});
return false;
}
var cap: u8 = @as(u8, @truncate(mmio.read(u8, config + 0x34))) & 0xFC;
@@ -193,7 +186,7 @@ fn walkCapabilities(config: usize, descriptor: *const device.DeviceDescriptor) b
cap = next;
}
if (common_base == 0 or notify_base == 0) {
log("virtio-gpu: missing common-config or notify capability\n", .{});
std.log.info("missing common-config or notify capability", .{});
return false;
}
return true;
@@ -275,7 +268,7 @@ fn testPixel(index: u32) u32 {
fn initialise(endpoint: ipc.Handle) bool {
_ = endpoint;
if (!device.claim(device_id)) {
log("virtio-gpu: unable to claim device {d}\n", .{device_id});
std.log.info("unable to claim device {d}", .{device_id});
return false;
}
@@ -284,7 +277,7 @@ fn initialise(endpoint: ipc.Handle) bool {
const descriptor = for (descriptors[0..@min(total, descriptors.len)]) |*d| {
if (d.id == device_id) break d;
} else {
log("virtio-gpu: device {d} not in the device tree\n", .{device_id});
std.log.info("device {d} not in the device tree", .{device_id});
return false;
};
@@ -292,13 +285,13 @@ fn initialise(endpoint: ipc.Handle) bool {
// decode + bus mastering (the device DMAs the ring and backing out of RAM); pci-bus only
// preserves whatever the firmware left, and a secondary display is often left disabled.
const config = device.mmioMap(device_id, 0) orelse {
log("virtio-gpu: config-space map failed\n", .{});
std.log.info("config-space map failed", .{});
return false;
};
const vendor = mmio.read(u16, config + 0x00);
const dev = mmio.read(u16, config + 0x02);
if (vendor != virtio_vendor or dev != virtio_gpu_device) {
log("virtio-gpu: not a virtio-gpu (vendor 0x{x} device 0x{x})\n", .{ vendor, dev });
std.log.info("not a virtio-gpu (vendor 0x{x} device 0x{x})", .{ vendor, dev });
return false;
}
mmio.write(u16, config + 0x04, mmio.read(u16, config + 0x04) | 0x06); // MEM + bus master
@@ -317,7 +310,7 @@ fn initialise(endpoint: ipc.Handle) bool {
// High feature word: VERSION_1 (bit 32) is required for a modern device.
cfgWrite(u32, "device_feature_select", vp.feature_version_1_word);
if (cfgRead(u32, "device_feature") & vp.feature_version_1_bit == 0) {
log("virtio-gpu: device does not offer VERSION_1 (not a modern device)\n", .{});
std.log.info("device does not offer VERSION_1 (not a modern device)", .{});
return false;
}
// Accept exactly VERSION_1, plus EDID when the device offered it (never a feature it didn't).
@@ -327,7 +320,7 @@ fn initialise(endpoint: ipc.Handle) bool {
cfgWrite(u32, "driver_feature", vp.feature_version_1_bit);
orStatus(vp.status_features_ok);
if (cfgRead(u8, "device_status") & vp.status_features_ok == 0) {
log("virtio-gpu: device rejected the negotiated features\n", .{});
std.log.info("device rejected the negotiated features", .{});
return false;
}
@@ -335,15 +328,15 @@ fn initialise(endpoint: ipc.Handle) bool {
cfgWrite(u16, "queue_select", 0);
const device_qsize = cfgRead(u16, "queue_size");
if (device_qsize < queue_size) {
log("virtio-gpu: control queue too small ({d})\n", .{device_qsize});
std.log.info("control queue too small ({d})", .{device_qsize});
return false;
}
ring = dma.alloc(4096, dma.coherent) orelse {
log("virtio-gpu: virtqueue allocation failed\n", .{});
std.log.info("virtqueue allocation failed", .{});
return false;
};
command = dma.alloc(4096, dma.coherent) orelse {
log("virtio-gpu: command-buffer allocation failed\n", .{});
std.log.info("command-buffer allocation failed", .{});
return false;
};
mmio.write(u16, ring.virtual + avail_offset, 1); // VIRTQ_AVAIL_F_NO_INTERRUPT: we poll
@@ -371,7 +364,7 @@ fn initialise(endpoint: ipc.Handle) bool {
.height = max_height,
};
if (command_nodata(@sizeOf(vg.ResourceCreate2d)) != ok_nodata) {
log("virtio-gpu: resource_create_2d failed\n", .{});
std.log.info("resource_create_2d failed", .{});
return false;
}
}
@@ -379,11 +372,11 @@ fn initialise(endpoint: ipc.Handle) bool {
// Back the resource with a shared (shm) surface, so the compositor and the device work
// the same physical pages. The device needs the guest-physical base for attach_backing.
surface = shm.create(scanout_bytes) orelse {
log("virtio-gpu: scanout surface allocation failed\n", .{});
std.log.info("scanout surface allocation failed", .{});
return false;
};
const surface_physical = shm.physical(surface.handle) orelse {
log("virtio-gpu: could not resolve the scanout surface physical address\n", .{});
std.log.info("could not resolve the scanout surface physical address", .{});
return false;
};
{
@@ -396,15 +389,15 @@ fn initialise(endpoint: ipc.Handle) bool {
const entry: *vg.MemEntry = @ptrFromInt(command.virtual + request_offset + @sizeOf(vg.ResourceAttachBacking));
entry.* = .{ .addr = surface_physical, .length = @intCast(scanout_bytes) };
if (command_nodata(@sizeOf(vg.ResourceAttachBacking) + @sizeOf(vg.MemEntry)) != ok_nodata) {
log("virtio-gpu: resource_attach_backing failed\n", .{});
std.log.info("resource_attach_backing failed", .{});
return false;
}
}
if (!setScanoutRect()) {
log("virtio-gpu: set_scanout failed\n", .{});
std.log.info("set_scanout failed", .{});
return false;
}
log("virtio-gpu: scanout {d}x{d} online\n", .{ current_width, current_height });
std.log.info("scanout {d}x{d} online", .{ current_width, current_height });
// Hello the device manager so it counts us as up (and does not stop us at the hello
// deadline). A restarted instance re-hellos here and re-announces below — the compositor
@@ -422,17 +415,17 @@ fn initialise(endpoint: ipc.Handle) bool {
for (0..pixel_count) |i| pixels[i] = testPixel(@intCast(i));
if (!presentFull()) {
log("virtio-gpu: initial present failed\n", .{});
std.log.info("initial present failed", .{});
return false;
}
// The scanout surface is CPU-visible RAM: read the pattern back to prove the mapping,
// which together with the flush ack above is the automated stand-in for "it's on screen".
mmio.rmb();
if (pixels[0] != testPixel(0) or pixels[pixel_count / 2] != testPixel(@intCast(pixel_count / 2))) {
log("virtio-gpu: pixel read-back mismatch\n", .{});
std.log.info("pixel read-back mismatch", .{});
return false;
}
log("virtio-gpu: flush acked, pixel check ok\n", .{});
std.log.info("flush acked, pixel check ok", .{});
// Offer the shared surface to the compositor so it upgrades off the GOP floor (V4).
announce();
@@ -456,18 +449,18 @@ fn setScanoutRect() bool {
/// a device that doesn't offer EDID, or a missing/short block, is logged and ignored.
fn readEdid() void {
if (!edid_available) {
log("virtio-gpu: EDID not offered by device\n", .{});
std.log.info("EDID not offered by device", .{});
return;
}
const request = requestAt(vg.GetEdid);
request.* = .{ .hdr = .{ .type = @intFromEnum(vg.CmdType.get_edid) }, .scanout = 0 };
if (!submit(@sizeOf(vg.GetEdid), @sizeOf(vg.RespEdid))) {
log("virtio-gpu: EDID request not acked\n", .{});
std.log.info("EDID request not acked", .{});
return;
}
const response: *vg.RespEdid = @ptrFromInt(command.virtual + response_offset);
if (response.hdr.type != @intFromEnum(vg.CmdType.resp_ok_edid) or response.size < 64) {
log("virtio-gpu: EDID unavailable\n", .{});
std.log.info("EDID unavailable", .{});
return;
}
// The first detailed timing descriptor (EDID base-block offset 54) is the preferred mode:
@@ -483,7 +476,7 @@ fn readEdid() void {
const v_blank = @as(u64, e[60]) | (@as(u64, e[61] & 0x0F) << 8);
const total = (@as(u64, h_active) + h_blank) * (@as(u64, v_active) + v_blank);
if (total != 0) edid_refresh_hz = @intCast((clock_hz + total / 2) / total);
log("virtio-gpu: EDID preferred mode {d}x{d} @ {d} Hz\n", .{ h_active, v_active, edid_refresh_hz });
std.log.info("EDID preferred mode {d}x{d} @ {d} Hz", .{ h_active, v_active, edid_refresh_hz });
}
/// Present the whole surface: copy the guest backing into the host resource, then flush it to
@@ -529,20 +522,20 @@ fn helloManager() void {
if (ipc.lookup(.device_manager)) |h| break h;
system.sleep(20);
} else {
log("virtio-gpu: no device manager to hello\n", .{});
std.log.info("no device manager to hello", .{});
return;
};
const hello = dm.Hello{ .role = @intFromEnum(dm.Role.bus), .device_id = device_id };
var reply: [dm.reply_size]u8 = undefined;
const n = ipc.call(manager, std.mem.asBytes(&hello), &reply) catch {
log("virtio-gpu: hello call failed\n", .{});
std.log.info("hello call failed", .{});
return;
};
if (n < dm.reply_size or std.mem.bytesToValue(dm.HelloReply, reply[0..dm.reply_size]).status != 0) {
log("virtio-gpu: hello refused\n", .{});
std.log.info("hello refused", .{});
return;
}
log("virtio-gpu: hello acknowledged\n", .{});
std.log.info("hello acknowledged", .{});
}
/// Announce the scanout to the display service so it upgrades off the GOP framebuffer: hand it
@@ -556,7 +549,7 @@ fn announce() void {
if (ipc.lookup(.display)) |h| break h;
system.sleep(20);
} else {
log("virtio-gpu: no display service to announce to (scanout-only)\n", .{});
std.log.info("no display service to announce to (scanout-only)", .{});
return;
};
var request = dp.Request{
@@ -569,10 +562,10 @@ fn announce() void {
};
var reply: [dp.reply_size]u8 = undefined;
_ = ipc.callCap(display, std.mem.asBytes(&request), &reply, surface.handle) catch {
log("virtio-gpu: announce to display failed\n", .{});
std.log.info("announce to display failed", .{});
return;
};
log("virtio-gpu: announced scanout to display\n", .{});
std.log.info("announced scanout to display", .{});
}
/// A `sp.Reply{status}` written into `reply`.
@@ -621,7 +614,7 @@ pub fn main(init: runtime.process.Init) void {
return;
};
device_id = std.fmt.parseInt(u64, argument, 10) catch {
log("virtio-gpu: malformed device id '{s}'\n", .{argument});
std.log.info("malformed device id '{s}'", .{argument});
return;
};
runtime.service.run(256, .{
+5 -3
View File
@@ -118,10 +118,12 @@ fn appendLocked(pid: u32, name: []const u8, level: abi.KlogLevel, now: u64, byte
var rest = bytes;
while (rest.len != 0) {
const newline = std.mem.indexOfScalar(u8, rest, '\n');
// The record payload excludes the newline: a record IS a line (or the
// open tail of one when the emission didn't end in '\n').
// The record payload excludes the newline: a record IS a line. Raw
// emissions may leave a line open (kernel boot tables build lines from
// pieces); a LEVELED record is a complete line by contract — std.log
// payloads carry no trailing newline.
const line = if (newline) |i| rest[0..i] else rest;
const line_complete = newline != null;
const line_complete = newline != null or level != .raw;
if (line.len != 0 or line_complete)
_ = ring.append(pid, name, level, now, line, line.len > abi.klog_maximum_message);
render(pid, name, level, line, line_complete);
+7 -12
View File
@@ -21,11 +21,6 @@ const power = runtime.power_protocol;
/// integer decode names the opcodes instead of bare 0x0A/0x0B/… (docs/coding-standards.md).
const opcodes = aml.opcodes;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
// The claimed acpi-tables node and the resource index of its broad io_port
// window — the Hal routes every port access through this one claim.
var node_id: u64 = 0;
@@ -163,12 +158,12 @@ pub fn main(init: runtime.process.Init) void {
};
var namespace = result.namespace;
const devices = aml.deviceCount(&namespace);
writeLine("/system/services/acpi: parsed {d} AML blob(s), {d} namespace devices\n", .{ block_count, devices });
std.log.info("parsed {d} AML blob(s), {d} namespace devices", .{ block_count, devices });
if (floor) |minimum| {
if (devices >= minimum) {
_ = runtime.system.write("acpi-parse: ok\n");
} else {
writeLine("acpi-parse: too few (ring-3 {d} < floor {d})\n", .{ devices, minimum });
std.log.info("acpi-parse: too few (ring-3 {d} < floor {d})", .{ devices, minimum });
}
// Self-verify mode is standalone (no manager); stop before reporting.
while (true) runtime.system.sleep(1000);
@@ -216,9 +211,9 @@ fn onInit(endpoint: runtime.ipc.Handle) bool {
const hid = entry.hid[0..entry.hid_len];
const desc = acpi_ids.description(hid);
if (desc.len != 0)
writeLine("/system/services/acpi: reported {s} (device {d}, {d} resources) — {s}\n", .{ hid, entry.device_id, entry.resource_count, desc })
std.log.info("reported {s} (device {d}, {d} resources) — {s}", .{ hid, entry.device_id, entry.resource_count, desc })
else
writeLine("/system/services/acpi: reported {s} (device {d}, {d} resources)\n", .{ hid, entry.device_id, entry.resource_count });
std.log.info("reported {s} (device {d}, {d} resources)", .{ hid, entry.device_id, entry.resource_count });
if (manager) |h| {
var report = protocol.ChildAdded{ .parent = node_id, .bus_address = entry.device_id, .identity = 0, .device_id = entry.device_id };
@memcpy(report.hid[0..entry.hid_len], entry.hid[0..entry.hid_len]);
@@ -226,7 +221,7 @@ fn onInit(endpoint: runtime.ipc.Handle) bool {
_ = runtime.ipc.call(h, std.mem.asBytes(&report), &reply) catch {};
}
}
writeLine("/system/services/acpi: reported {d} device(s) to the manager\n", .{registered_count});
std.log.info("reported {d} device(s) to the manager", .{registered_count});
armPowerButton(endpoint);
return true;
@@ -373,7 +368,7 @@ fn publishNotify(node: *aml.Node, code: u64) void {
const which: power.Event = if (std.mem.eql(u8, hid[0..7], "PNP0C0A")) .battery else if (std.mem.eql(u8, hid[0..7], "ACPI0003")) .ac else if (std.mem.eql(u8, hid[0..7], "PNP0C0D")) .lid else .notify;
var event = power.EventMessage{ .event = @intFromEnum(which), .code = @truncate(code) };
event.hid = hid;
writeLine("power: notify {s} code {d}\n", .{ hid[0..7], code });
std.log.info("power: notify {s} code {d}", .{ hid[0..7], code });
publishEvent(std.mem.asBytes(&event));
}
@@ -500,7 +495,7 @@ fn registerDevice(node: *aml.Node, hid: [8]u8, interpreter: *aml.Interpreter) vo
applyCrs(&descriptor, node, interpreter);
const id = device.register(node_id, &descriptor) orelse {
writeLine("/system/services/acpi: register refused for {s}\n", .{hid[0..@intCast(hid_len)]});
std.log.info("register refused for {s}", .{hid[0..@intCast(hid_len)]});
return;
};
registered[registered_count] = .{ .hid = hid, .hid_len = @intCast(hid_len), .device_id = id, .resource_count = descriptor.resource_count };
@@ -24,14 +24,6 @@ const protocol = runtime.device_manager_protocol;
const device = runtime.device;
const system = runtime.system;
/// Format one whole log line and emit it in a single `debug_write`, so output
/// from the drivers this manager starts (which run concurrently) can never land
/// in the middle of it.
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
/// 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
/// than written as the bare 0x0C0330 (docs/coding-standards.md, "Named values").
@@ -223,7 +215,7 @@ fn addChild(parent: u64, bus_address: u64, identity: u64, device_id: u64, report
fn pruneChildrenOf(reporter: u32) void {
for (&children) |*child| {
if (child.used and child.reporter == reporter) {
writeLine("/system/services/device-manager: child removed (device {d} port {d})\n", .{ child.parent, child.bus_address });
std.log.info("child removed (device {d} port {d})", .{ child.parent, child.bus_address });
child.used = false;
const event = protocol.ChildRemoved{ .parent = child.parent, .bus_address = child.bus_address };
publishEvent(std.mem.asBytes(&event));
@@ -270,7 +262,7 @@ fn addDriver(name: []const u8, device_id: u64, speaks_protocol: bool) void {
spawnDriver(driver);
return;
}
writeLine("/system/services/device-manager: driver table full; cannot supervise {s}\n", .{name});
std.log.info("driver table full; cannot supervise {s}", .{name});
}
/// (Re)spawn a driver instance: supervised on the manager's own endpoint, the
@@ -285,7 +277,7 @@ fn spawnDriver(driver: *Driver) void {
argument_count = 1;
}
const child = system.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
writeLine("/system/services/device-manager: failed to spawn {s}\n", .{driver.name()});
std.log.info("failed to spawn {s}", .{driver.name()});
driver.state = .failed;
return;
};
@@ -299,9 +291,9 @@ fn spawnDriver(driver: *Driver) void {
driver.state = .running;
}
if (driver.device_id != protocol.no_device) {
writeLine("/system/services/device-manager: spawned {s} for device {d}\n", .{ driver.name(), driver.device_id });
std.log.info("spawned {s} for device {d}", .{ driver.name(), driver.device_id });
} else {
writeLine("/system/services/device-manager: spawned {s}\n", .{driver.name()});
std.log.info("spawned {s}", .{driver.name()});
}
}
@@ -313,7 +305,7 @@ fn onDriverExit(driver: *Driver) void {
const reason = runtime.process.exitReason(driver.process_id) orelse .fault;
if (reason == .exited) {
driver.state = .stopped;
writeLine("/system/services/device-manager: {s} exited cleanly; not restarting\n", .{driver.name()});
std.log.info("{s} exited cleanly; not restarting", .{driver.name()});
return;
}
const now = system.clock();
@@ -321,13 +313,13 @@ fn onDriverExit(driver: *Driver) void {
driver.restarts = if (alive_ns < fast_death_ns) driver.restarts + 1 else 1;
if (driver.restarts >= crash_loop_cap) {
driver.state = .failed;
writeLine("/system/services/device-manager: {s} is failing repeatedly (crash loop); giving up\n", .{driver.name()});
std.log.info("{s} is failing repeatedly (crash loop); giving up", .{driver.name()});
return;
}
const delay_ms = backoff_base_ms << @intCast(driver.restarts - 1);
driver.state = .restarting;
driver.restart_due_ns = now + delay_ms * 1_000_000;
writeLine("/system/services/device-manager: restarting {s} in {d} ms (died: {s})\n", .{ driver.name(), delay_ms, @tagName(reason) });
std.log.info("restarting {s} in {d} ms (died: {s})", .{ driver.name(), delay_ms, @tagName(reason) });
_ = system.timerOnce(manager_endpoint, delay_ms + 50);
}
@@ -338,7 +330,7 @@ fn onDriverExit(driver: *Driver) void {
fn sweepDeadlines() void {
const now = system.clock();
if (test_kill_pid != 0 and now >= test_kill_due_ns) {
writeLine("/system/services/device-manager: test mode: killing the reporter\n", .{});
std.log.info("test mode: killing the reporter", .{});
_ = system.kill(test_kill_pid);
test_kill_pid = 0;
}
@@ -346,7 +338,7 @@ fn sweepDeadlines() void {
if (!driver.used) continue;
switch (driver.state) {
.awaiting_hello => if (now >= driver.hello_deadline_ns) {
writeLine("/system/services/device-manager: {s} missed its hello deadline\n", .{driver.name()});
std.log.info("{s} missed its hello deadline", .{driver.name()});
_ = system.kill(driver.process_id);
// The exit notification finishes the job via onDriverExit.
},
@@ -423,10 +415,10 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
var status: i32 = 0;
if (hello.version != protocol.version) {
status = -1;
writeLine("/system/services/device-manager: refused hello (version {d}) from process {d}\n", .{ hello.version, sender });
std.log.info("refused hello (version {d}) from process {d}", .{ hello.version, sender });
} else if (driverByProcess(sender)) |driver| {
driver.state = .running;
writeLine("/system/services/device-manager: hello from {s} (device {d})\n", .{ driver.name(), hello.device_id });
std.log.info("hello from {s} (device {d})", .{ driver.name(), hello.device_id });
// Resilience drill (V6): once, kill the virtio-gpu driver a moment after it hellos, so
// the normal restart policy respawns it — the compositor must survive and re-attach.
if (test_scanout_restart_mode and !test_scanout_killed and std.mem.eql(u8, driver.name(), "/system/drivers/virtio-gpu")) {
@@ -437,7 +429,7 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
}
} else {
status = -1;
writeLine("/system/services/device-manager: hello from unknown process {d}\n", .{sender});
std.log.info("hello from unknown process {d}", .{sender});
}
const hello_reply = protocol.HelloReply{ .status = status };
@memcpy(reply[0..protocol.reply_size], std.mem.asBytes(&hello_reply));
@@ -453,7 +445,7 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
var status: i32 = 0;
if (driverByProcess(sender)) |driver| {
if (!addChild(report.parent, report.bus_address, report.identity, report.device_id, sender)) status = -1;
writeLine("/system/services/device-manager: child added (device {d} port {d}, identity {d}) by {s}\n", .{ report.parent, report.bus_address, report.identity, driver.name() });
std.log.info("child added (device {d} port {d}, identity {d}) by {s}", .{ report.parent, report.bus_address, report.identity, driver.name() });
if (status == 0) publishEvent(message[0..protocol.child_added_size]);
// Matching from reports (M19.3): a registered child whose identity
// names a driver gets one, once — re-reports after a bus restart
@@ -519,7 +511,7 @@ fn onChildRemoved(message: []const u8, reply: []u8, sender: u32) usize {
var status: i32 = -1;
for (&children) |*child| {
if (child.used and child.parent == report.parent and child.bus_address == report.bus_address and child.reporter == sender) {
writeLine("/system/services/device-manager: child removed (device {d} port {d})\n", .{ child.parent, child.bus_address });
std.log.info("child removed (device {d} port {d})", .{ child.parent, child.bus_address });
child.used = false;
status = 0;
}
+2 -7
View File
@@ -16,11 +16,6 @@ const on_disk = @import("on-disk.zig");
const protocol = runtime.vfs_protocol;
const dma = runtime.dma;
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [96]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
const mount_point = "/mnt/usb";
// The engine's BlockDevice, backed by the `.block` driver plus a DMA bounce
@@ -104,14 +99,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
_ = runtime.system.write("/system/services/fat: not a FAT filesystem\n");
return false;
};
writeLine("/system/services/fat: mounted FAT ({s}, {d} clusters, partition lba {d})\n", .{ @tagName(filesystem.geometry.fat_type), filesystem.geometry.cluster_count, filesystem.base_lba });
std.log.info("mounted FAT ({s}, {d} clusters, partition lba {d})", .{ @tagName(filesystem.geometry.fat_type), filesystem.geometry.cluster_count, filesystem.base_lba });
// Mount ourselves into the VFS namespace at /mnt/usb (retry while the VFS
// comes up). From here the VFS routes /mnt/usb/... to this server.
var tries: u32 = 0;
while (tries < 100) : (tries += 1) {
if (runtime.fs.mount(mount_point, endpoint)) {
writeLine("/system/services/fat: mounted {s}\n", .{mount_point});
std.log.info("mounted {s}", .{mount_point});
return true;
}
runtime.system.sleep(50);
+3 -8
View File
@@ -145,26 +145,21 @@ fn restartChild(id: u32) void {
// An unknown reason (the record aged out) is treated as a crash worth restarting.
const reason = runtime.process.exitReason(id) orelse .fault;
if (reason == .exited) {
logLine("/system/services/init: {s} exited cleanly; not restarting\n", .{service});
std.log.info("{s} exited cleanly; not restarting", .{service});
return;
}
restart_counts[i] += 1;
if (restart_counts[i] > maximum_restarts) {
logLine("/system/services/init: {s} keeps crashing; giving up after {d} restarts\n", .{ service, maximum_restarts });
std.log.info("{s} keeps crashing; giving up after {d} restarts", .{ service, maximum_restarts });
return;
}
logLine("/system/services/init: {s} died ({s}); restarting ({d}/{d})\n", .{ service, @tagName(reason), restart_counts[i], maximum_restarts });
std.log.info("{s} died ({s}); restarting ({d}/{d})", .{ service, @tagName(reason), restart_counts[i], maximum_restarts });
if (runtime.system.spawnSupervised(service, &.{}, supervision_endpoint)) |new_id| child_ids[i] = new_id;
return;
}
// An untracked child (e.g. the log-flush one-shot): nothing to restart.
}
fn logLine(comptime fmt: []const u8, args: anytype) void {
var line: [128]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, args) catch return);
}
/// Look up the power service and subscribe our endpoint (handed over as the
/// call's capability) so events arrive as buffered messages here.
fn subscribePower() void {
+4 -11
View File
@@ -113,13 +113,6 @@ fn fail(out: []u8) usize {
return writeReply(out, .{ .status = -1 }, &.{});
}
/// Format one whole log line and emit it in a single `debug_write`, so lines from
/// concurrent processes can never land in the middle of it.
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [96]u8 = undefined;
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
// --- mount routing ----------------------------------------------------------
/// Forward an open under a mount to its backend and, on success, allocate a local
@@ -211,7 +204,7 @@ fn doMount(out: []u8, prefix: []const u8, backend: ipc.Handle) usize {
for (&mounts) |*m| {
if (m.used and std.mem.eql(u8, m.prefix[0..m.prefix_len], prefix)) {
m.backend = backend;
writeLine("/system/services/vfs: remounted {s}\n", .{prefix});
std.log.info("remounted {s}", .{prefix});
return writeReply(out, .{ .status = 0 }, &.{});
}
}
@@ -222,7 +215,7 @@ fn doMount(out: []u8, prefix: []const u8, backend: ipc.Handle) usize {
@memcpy(m.prefix[0..l], prefix[0..l]);
m.prefix_len = l;
m.backend = backend;
writeLine("/system/services/vfs: mounted {s}\n", .{prefix[0..l]});
std.log.info("mounted {s}", .{prefix[0..l]});
return writeReply(out, .{ .status = 0 }, &.{});
}
}
@@ -233,7 +226,7 @@ fn doUnmount(out: []u8, prefix: []const u8) usize {
for (&mounts) |*m| {
if (m.used and std.mem.eql(u8, m.prefix[0..m.prefix_len], prefix)) {
m.used = false;
writeLine("/system/services/vfs: unmounted {s}\n", .{prefix});
std.log.info("unmounted {s}", .{prefix});
return writeReply(out, .{ .status = 0 }, &.{});
}
}
@@ -252,7 +245,7 @@ fn releaseClientHandles(client: u32) void {
released += 1;
}
}
if (released != 0) writeLine("/system/services/vfs: released {d} handle(s) for dead client {d}\n", .{ released, client });
if (released != 0) std.log.info("released {d} handle(s) for dead client {d}", .{ released, client });
}
/// Handle one request from `sender`; write the reply into `out`, return its length.
+5 -5
View File
@@ -458,7 +458,7 @@ CASES = [
"smp": 4,
"timeout": 150,
# usb-kbd/usb-mouse ride the default boot xHCI bus (see qemu_args).
"expect": r"(?=[\s\S]*usb-hid/keyboard: ok)(?=[\s\S]*usb-hid/mouse: ok)",
"expect": r"(?=[\s\S]*usb-hid-keyboard: ok)(?=[\s\S]*usb-hid-mouse: ok)",
"fail": r"DANOS-TEST-RESULT: FAIL"},
# USB mass storage end to end: the boot usb-storage device (the FAT32 image,
# which has a real 0x55AA boot sector) is enough — the manager spawns
@@ -526,7 +526,7 @@ CASES = [
{"name": "acpi-ps2",
"smp": 4,
"timeout": 150,
"expect": r"acpi: reported PNP0303[\s\S]*"
"expect": r"discovery: reported PNP0303[\s\S]*"
r"device-manager: spawned \S*ps2-bus[\s\S]*"
r"ps2-bus: keyboard driver attached",
"fail": r"DANOS-TEST-RESULT: FAIL"},
@@ -572,8 +572,8 @@ CASES = [
{"name": "acpi-report",
"smp": 4,
"timeout": 150,
"expect": r"acpi: reported PNP0303 \(device \d+, 3 resources\)[\s\S]*"
r"acpi: reported PNP0F13 \(device \d+, 1 resources\)",
"expect": r"discovery: reported PNP0303 \(device \d+, 3 resources\)[\s\S]*"
r"discovery: reported PNP0F13 \(device \d+, 1 resources\)",
"fail": r"DANOS-TEST-RESULT: FAIL"},
# M19.1/M19.3: the ring-3 PCI scan. pci-bus walks the ECAM through its mmio_map
# grant and registers every function it finds; the kernel's own walk retired, so
@@ -589,7 +589,7 @@ CASES = [
"timeout": 60,
"expect": r"pci-bus: (\d+) functions found[\s\S]*"
r"device-manager: test mode: killing the reporter[\s\S]*"
r"device-manager: restarting pci-bus[\s\S]*"
r"device-manager: restarting \S*pci-bus[\s\S]*"
r"pci-bus: \1 functions found[\s\S]*"
r"DANOS-TEST-RESULT: PASS",
"fail": r"DANOS-TEST-RESULT: FAIL"},