From f480c5d7905c1fc4cb0efac675fad25325952cd5 Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Tue, 21 Jul 2026 15:39:51 +0100 Subject: [PATCH] =?UTF-8?q?runtime:=20std.log=20for=20every=20user=20binar?= =?UTF-8?q?y=20=E2=80=94=20kernel-stamped=20attribution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ': ' 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. --- library/runtime/log.zig | 51 +++++++++++++ library/runtime/root.zig | 6 ++ library/runtime/runtime.zig | 1 + system/drivers/pci-bus/pci-bus.zig | 17 ++--- system/drivers/ps2-bus/keyboard.zig | 11 +-- system/drivers/ps2-bus/mouse.zig | 9 +-- system/drivers/ps2-bus/ps2-bus.zig | 17 ++--- system/drivers/usb-hid/keyboard.zig | 11 +-- system/drivers/usb-hid/mouse.zig | 11 +-- system/drivers/usb-storage/usb-storage.zig | 13 +--- system/drivers/usb-xhci-bus/usb-xhci-bus.zig | 35 ++++----- system/drivers/virtio-gpu/virtio-gpu.zig | 73 +++++++++---------- system/kernel/log.zig | 8 +- system/services/acpi/acpi.zig | 19 ++--- .../device-manager/device-manager.zig | 38 ++++------ system/services/fat/fat.zig | 9 +-- system/services/init/init.zig | 11 +-- system/services/vfs/vfs.zig | 15 +--- test/qemu_test.py | 10 +-- 19 files changed, 172 insertions(+), 193 deletions(-) create mode 100644 library/runtime/log.zig diff --git a/library/runtime/log.zig b/library/runtime/log.zig new file mode 100644 index 0000000..5ae921d --- /dev/null +++ b/library/runtime/log.zig @@ -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 `: 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, +}; diff --git a/library/runtime/root.zig b/library/runtime/root.zig index 1fa2d3a..e91d038 100644 --- a/library/runtime/root.zig +++ b/library/runtime/root.zig @@ -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 } diff --git a/library/runtime/runtime.zig b/library/runtime/runtime.zig index 0533d7c..c3eefbc 100644 --- a/library/runtime/runtime.zig +++ b/library/runtime/runtime.zig @@ -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"); diff --git a/system/drivers/pci-bus/pci-bus.zig b/system/drivers/pci-bus/pci-bus.zig index 9b415d0..4b2d797 100644 --- a/system/drivers/pci-bus/pci-bus.zig +++ b/system/drivers/pci-bus/pci-bus.zig @@ -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, .{ diff --git a/system/drivers/ps2-bus/keyboard.zig b/system/drivers/ps2-bus/keyboard.zig index fa337a5..a486a7a 100644 --- a/system/drivers/ps2-bus/keyboard.zig +++ b/system/drivers/ps2-bus/keyboard.zig @@ -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). diff --git a/system/drivers/ps2-bus/mouse.zig b/system/drivers/ps2-bus/mouse.zig index fa12d35..98043da 100644 --- a/system/drivers/ps2-bus/mouse.zig +++ b/system/drivers/ps2-bus/mouse.zig @@ -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; } diff --git a/system/drivers/ps2-bus/ps2-bus.zig b/system/drivers/ps2-bus/ps2-bus.zig index 1ed6ee1..05a7209 100644 --- a/system/drivers/ps2-bus/ps2-bus.zig +++ b/system/drivers/ps2-bus/ps2-bus.zig @@ -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); diff --git a/system/drivers/usb-hid/keyboard.zig b/system/drivers/usb-hid/keyboard.zig index ab1528a..cbb95cb 100644 --- a/system/drivers/usb-hid/keyboard.zig +++ b/system/drivers/usb-hid/keyboard.zig @@ -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; diff --git a/system/drivers/usb-hid/mouse.zig b/system/drivers/usb-hid/mouse.zig index 2e90360..8d76d78 100644 --- a/system/drivers/usb-hid/mouse.zig +++ b/system/drivers/usb-hid/mouse.zig @@ -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; diff --git a/system/drivers/usb-storage/usb-storage.zig b/system/drivers/usb-storage/usb-storage.zig index d7fb272..26942c0 100644 --- a/system/drivers/usb-storage/usb-storage.zig +++ b/system/drivers/usb-storage/usb-storage.zig @@ -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, .{ diff --git a/system/drivers/usb-xhci-bus/usb-xhci-bus.zig b/system/drivers/usb-xhci-bus/usb-xhci-bus.zig index 0666863..6e8f1f0 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-bus.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-bus.zig @@ -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, .{ diff --git a/system/drivers/virtio-gpu/virtio-gpu.zig b/system/drivers/virtio-gpu/virtio-gpu.zig index 273702e..1a8af1d 100644 --- a/system/drivers/virtio-gpu/virtio-gpu.zig +++ b/system/drivers/virtio-gpu/virtio-gpu.zig @@ -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, .{ diff --git a/system/kernel/log.zig b/system/kernel/log.zig index 7355e6e..e939859 100644 --- a/system/kernel/log.zig +++ b/system/kernel/log.zig @@ -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); diff --git a/system/services/acpi/acpi.zig b/system/services/acpi/acpi.zig index 348ddd4..8f3dcff 100644 --- a/system/services/acpi/acpi.zig +++ b/system/services/acpi/acpi.zig @@ -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 }; diff --git a/system/services/device-manager/device-manager.zig b/system/services/device-manager/device-manager.zig index 1d3164d..0623b99 100644 --- a/system/services/device-manager/device-manager.zig +++ b/system/services/device-manager/device-manager.zig @@ -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; } diff --git a/system/services/fat/fat.zig b/system/services/fat/fat.zig index 9a3bea9..426c292 100644 --- a/system/services/fat/fat.zig +++ b/system/services/fat/fat.zig @@ -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); diff --git a/system/services/init/init.zig b/system/services/init/init.zig index c6209ff..525e674 100644 --- a/system/services/init/init.zig +++ b/system/services/init/init.zig @@ -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 { diff --git a/system/services/vfs/vfs.zig b/system/services/vfs/vfs.zig index 8ec25ed..3b2223c 100644 --- a/system/services/vfs/vfs.zig +++ b/system/services/vfs/vfs.zig @@ -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. diff --git a/test/qemu_test.py b/test/qemu_test.py index a34a1fc..e285ee4 100644 --- a/test/qemu_test.py +++ b/test/qemu_test.py @@ -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"},