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:
@@ -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,
|
||||||
|
};
|
||||||
@@ -14,6 +14,12 @@ pub const main = program.main;
|
|||||||
/// The panic handler for every safety check in the image (runtime.start.panic).
|
/// The panic handler for every safety check in the image (runtime.start.panic).
|
||||||
pub const panic = runtime.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 {
|
comptime {
|
||||||
_ = &runtime.start._start; // pull the runtime entry shim into the image
|
_ = &runtime.start._start; // pull the runtime entry shim into the image
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
//! nothing to declare per source file.
|
//! nothing to declare per source file.
|
||||||
|
|
||||||
pub const system = @import("system.zig");
|
pub const system = @import("system.zig");
|
||||||
|
pub const log = @import("log.zig");
|
||||||
/// Monotonic time, delays, and deadlines over the kernel clock/sleep/timer syscalls
|
/// Monotonic time, delays, and deadlines over the kernel clock/sleep/timer syscalls
|
||||||
/// — an `Instant`/`Duration` front door, no time service (docs/timers.md).
|
/// — an `Instant`/`Duration` front door, no time service (docs/timers.md).
|
||||||
pub const time = @import("time.zig");
|
pub const time = @import("time.zig");
|
||||||
|
|||||||
@@ -16,11 +16,6 @@ const protocol = runtime.device_manager_protocol;
|
|||||||
const device = runtime.device;
|
const device = runtime.device;
|
||||||
const pci_class = @import("pci-class");
|
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
|
/// 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
|
/// to human names — the boot-log breadcrumb that says *what* the hardware is, so
|
||||||
/// "class 0x01 (Mass Storage Controller) subclass 0x06 (Serial ATA Controller)
|
/// "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 {
|
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||||
_ = endpoint;
|
_ = endpoint;
|
||||||
if (!device.claim(bridge_id)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
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| {
|
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
|
||||||
if (d.id == bridge_id) break d;
|
if (d.id == bridge_id) break d;
|
||||||
} else {
|
} 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;
|
return false;
|
||||||
};
|
};
|
||||||
// Resource 0 is the ECAM window (1 MiB of config space per bus); the bus
|
// 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
|
/// 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 {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
const report = protocol.ChildAdded{
|
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;
|
var reply: [protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
|
_ = 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 {
|
pub fn main(init: runtime.process.Init) void {
|
||||||
const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent
|
const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent
|
||||||
bridge_id = std.fmt.parseInt(u64, argument, 10) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(protocol.message_maximum, .{
|
runtime.service.run(protocol.message_maximum, .{
|
||||||
|
|||||||
@@ -23,11 +23,6 @@ const device = runtime.device;
|
|||||||
const ipc = runtime.ipc;
|
const ipc = runtime.ipc;
|
||||||
const protocol = runtime.input_protocol;
|
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
|
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
||||||
/// registering) is still coming up.
|
/// registering) is still coming up.
|
||||||
fn lookupBus() ?ipc.Handle {
|
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");
|
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: no HID argument\n");
|
||||||
return;
|
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 {
|
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
|
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (device.findDeviceDescriptorByHid(buffer, hid) == null) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +85,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
// absent (as today) it defaults to us.
|
// absent (as today) it defaults to us.
|
||||||
const layout_name = init.arguments.get(2) orelse "us";
|
const layout_name = init.arguments.get(2) orelse "us";
|
||||||
const layout = xkb.byName(layout_name) orelse xkb.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
|
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
||||||
// keyboard sends (it owns the controller; we own the decoding).
|
// keyboard sends (it owns the controller; we own the decoding).
|
||||||
|
|||||||
@@ -22,11 +22,6 @@ const device = runtime.device;
|
|||||||
const ipc = runtime.ipc;
|
const ipc = runtime.ipc;
|
||||||
const protocol = runtime.input_protocol;
|
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
|
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
||||||
/// registering) is still coming up.
|
/// registering) is still coming up.
|
||||||
fn lookupBus() ?ipc.Handle {
|
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");
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
||||||
return;
|
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 {
|
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (ps2.findMouseDescriptor(buffer) == null) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,6 @@ const ps2 = @import("ps2-library.zig");
|
|||||||
const device = runtime.device;
|
const device = runtime.device;
|
||||||
const ipc = runtime.ipc;
|
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
|
/// 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
|
/// 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
|
/// 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.
|
/// attaches, or null if nothing was spawned.
|
||||||
fn spawnIdentifiedDriver(controller: ps2.Controller, port: ps2.Port) ?ps2.DeviceType {
|
fn spawnIdentifiedDriver(controller: ps2.Controller, port: ps2.Port) ?ps2.DeviceType {
|
||||||
const device_type = controller.identifyDevice(port) orelse {
|
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;
|
return null;
|
||||||
};
|
};
|
||||||
const driver_name = device_type.driverName() orelse {
|
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;
|
return null;
|
||||||
};
|
};
|
||||||
const hid = device_type.hid() orelse "";
|
const hid = device_type.hid() orelse "";
|
||||||
if (runtime.system.spawnWithArguments(driver_name, &.{hid}) != null) {
|
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;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +76,7 @@ fn handleAttach(message: []const u8, got: ipc.Received, out: []u8) usize {
|
|||||||
const device_type = maybe_type orelse continue;
|
const device_type = maybe_type orelse continue;
|
||||||
if (@intFromEnum(device_type) != request.device_type) continue;
|
if (@intFromEnum(device_type) != request.device_type) continue;
|
||||||
port_endpoints[port_index] = endpoint;
|
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, .ok);
|
||||||
}
|
}
|
||||||
return reply.write(out, .no_such_device);
|
return reply.write(out, .no_such_device);
|
||||||
|
|||||||
@@ -23,11 +23,6 @@ const ipc = runtime.ipc;
|
|||||||
const process = runtime.process;
|
const process = runtime.process;
|
||||||
const input_protocol = runtime.input_protocol;
|
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
|
// The modifier state a character lookup needs — derived from the report's
|
||||||
// modifier byte, plus the driver-tracked caps-lock toggle.
|
// modifier byte, plus the driver-tracked caps-lock toggle.
|
||||||
const ModifierSnapshot = struct {
|
const ModifierSnapshot = struct {
|
||||||
@@ -71,7 +66,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
const layout = xkb.byName(init.arguments.get(2) orelse "us") orelse xkb.us;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
var device = runtime.usb.open(device_id) orelse {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
const endpoint = device.findEndpoint(runtime.usb.transfer_type_interrupt, true) orelse {
|
const endpoint = device.findEndpoint(runtime.usb.transfer_type_interrupt, true) orelse {
|
||||||
@@ -104,7 +99,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = process.bindSignals(device.endpoint);
|
_ = 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 decoder = hid.KeyboardDecoder{};
|
||||||
var caps_lock = false;
|
var caps_lock = false;
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ const ipc = runtime.ipc;
|
|||||||
const process = runtime.process;
|
const process = runtime.process;
|
||||||
const input_protocol = runtime.input_protocol;
|
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.
|
// The current pressed-button bitmask in input-protocol terms.
|
||||||
fn buttonMask(buttons: u8) u32 {
|
fn buttonMask(buttons: u8) u32 {
|
||||||
var mask: u32 = 0;
|
var mask: u32 = 0;
|
||||||
@@ -38,7 +33,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +42,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var device = runtime.usb.open(device_id) orelse {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
const endpoint = device.findEndpoint(runtime.usb.transfer_type_interrupt, true) orelse {
|
const endpoint = device.findEndpoint(runtime.usb.transfer_type_interrupt, true) orelse {
|
||||||
@@ -67,7 +62,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
_ = process.bindSignals(device.endpoint);
|
_ = 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 previous_buttons: u8 = 0;
|
||||||
var receive: [64]u8 = undefined;
|
var receive: [64]u8 = undefined;
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ const bot = @import("bulk-only-transport.zig");
|
|||||||
const block_protocol = @import("block-protocol");
|
const block_protocol = @import("block-protocol");
|
||||||
const dma = runtime.dma;
|
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_id: u64 = 0;
|
||||||
var device: runtime.usb.Device = undefined;
|
var device: runtime.usb.Device = undefined;
|
||||||
var bulk_in: runtime.usb.Endpoint = undefined;
|
var bulk_in: runtime.usb.Endpoint = undefined;
|
||||||
@@ -73,7 +68,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
device = runtime.usb.open(device_id) orelse {
|
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;
|
return false;
|
||||||
};
|
};
|
||||||
bulk_in = device.findEndpoint(runtime.usb.transfer_type_bulk, true) orelse {
|
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);
|
const capacity = scsi.parseCapacity(capacity_bytes);
|
||||||
block_size = capacity.block_size;
|
block_size = capacity.block_size;
|
||||||
block_count = @as(u64, capacity.last_lba) + 1;
|
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
|
// 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.
|
// sector) — proof READ(10) works end to end over the bulk path.
|
||||||
const read0 = scsi.read10(0, 1);
|
const read0 = scsi.read10(0, 1);
|
||||||
if (block_size <= 4096 and transact(&read0, true, command_data.physical, block_size)) {
|
if (block_size <= 4096 and transact(&read0, true, command_data.physical, block_size)) {
|
||||||
const sector: [*]const u8 = @ptrFromInt(command_data.virtual);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -171,7 +166,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(block_protocol.message_maximum, .{
|
runtime.service.run(block_protocol.message_maximum, .{
|
||||||
|
|||||||
@@ -64,13 +64,6 @@ fn reportEndpointFor(device_token: u64) ?usize {
|
|||||||
return null;
|
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;
|
var controller_id: u64 = protocol.no_device;
|
||||||
|
|
||||||
/// Claim the assigned controller, find its register window, and hello the
|
/// 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 {
|
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||||
service_endpoint = endpoint;
|
service_endpoint = endpoint;
|
||||||
if (!device.claim(controller_id)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +85,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
|
const descriptor = for (buffer[0..@min(total, buffer.len)]) |d| {
|
||||||
if (d.id == controller_id) break d;
|
if (d.id == controller_id) break d;
|
||||||
} else {
|
} 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;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,10 +98,10 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
|||||||
break resource;
|
break resource;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
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,
|
controller_id,
|
||||||
register_window.start,
|
register_window.start,
|
||||||
register_window.len,
|
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");
|
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
|
||||||
return false;
|
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.?.max_slots,
|
||||||
controller.?.context_size,
|
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");
|
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
|
||||||
return;
|
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 port: u32 = 1;
|
||||||
var connected: u32 = 0;
|
var connected: u32 = 0;
|
||||||
@@ -207,17 +200,17 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
|
|||||||
if (port_status & 1 == 0) continue; // CCS: nothing connected
|
if (port_status & 1 == 0) continue; // CCS: nothing connected
|
||||||
connected += 1;
|
connected += 1;
|
||||||
const speed = (port_status >> 10) & 0xF; // the PORTSC port-speed class
|
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 {
|
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;
|
continue;
|
||||||
};
|
};
|
||||||
if (!engine.enumerate(usb_device)) {
|
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;
|
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,
|
port,
|
||||||
usb_device.device_descriptor.vendor_id,
|
usb_device.device_descriptor.vendor_id,
|
||||||
usb_device.device_descriptor.product_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;
|
descriptor.hid_len = hid_text.len;
|
||||||
@memcpy(descriptor.hid[0..hid_text.len], hid_text);
|
@memcpy(descriptor.hid[0..hid_text.len], hid_text);
|
||||||
const registered = device.register(controller_id, &descriptor) orelse {
|
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;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -271,10 +264,10 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
|
|||||||
};
|
};
|
||||||
var reply: [protocol.message_maximum]u8 = undefined;
|
var reply: [protocol.message_maximum]u8 = undefined;
|
||||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
|
_ = 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;
|
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,
|
port,
|
||||||
interface.number,
|
interface.number,
|
||||||
interface.class,
|
interface.class,
|
||||||
@@ -407,7 +400,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(transfer.message_maximum, .{
|
runtime.service.run(transfer.message_maximum, .{
|
||||||
|
|||||||
@@ -103,13 +103,6 @@ var surface: shm.Region = undefined;
|
|||||||
var avail_shadow: u16 = 0;
|
var avail_shadow: u16 = 0;
|
||||||
var used_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`) ---------------
|
// --- common-config register access (little-endian MMIO at `common_base`) ---------------
|
||||||
|
|
||||||
fn cfgRead(comptime T: type, comptime field: []const u8) T {
|
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;
|
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;
|
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.
|
/// notify structures (the only two V3 needs). Returns false if either is missing.
|
||||||
fn walkCapabilities(config: usize, descriptor: *const device.DeviceDescriptor) bool {
|
fn walkCapabilities(config: usize, descriptor: *const device.DeviceDescriptor) bool {
|
||||||
if (mmio.read(u16, config + 0x06) & 0x10 == 0) { // Status bit 4: capabilities list present
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
var cap: u8 = @as(u8, @truncate(mmio.read(u8, config + 0x34))) & 0xFC;
|
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;
|
cap = next;
|
||||||
}
|
}
|
||||||
if (common_base == 0 or notify_base == 0) {
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -275,7 +268,7 @@ fn testPixel(index: u32) u32 {
|
|||||||
fn initialise(endpoint: ipc.Handle) bool {
|
fn initialise(endpoint: ipc.Handle) bool {
|
||||||
_ = endpoint;
|
_ = endpoint;
|
||||||
if (!device.claim(device_id)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +277,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
const descriptor = for (descriptors[0..@min(total, descriptors.len)]) |*d| {
|
const descriptor = for (descriptors[0..@min(total, descriptors.len)]) |*d| {
|
||||||
if (d.id == device_id) break d;
|
if (d.id == device_id) break d;
|
||||||
} else {
|
} 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;
|
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
|
// 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.
|
// preserves whatever the firmware left, and a secondary display is often left disabled.
|
||||||
const config = device.mmioMap(device_id, 0) orelse {
|
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;
|
return false;
|
||||||
};
|
};
|
||||||
const vendor = mmio.read(u16, config + 0x00);
|
const vendor = mmio.read(u16, config + 0x00);
|
||||||
const dev = mmio.read(u16, config + 0x02);
|
const dev = mmio.read(u16, config + 0x02);
|
||||||
if (vendor != virtio_vendor or dev != virtio_gpu_device) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
mmio.write(u16, config + 0x04, mmio.read(u16, config + 0x04) | 0x06); // MEM + bus master
|
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.
|
// High feature word: VERSION_1 (bit 32) is required for a modern device.
|
||||||
cfgWrite(u32, "device_feature_select", vp.feature_version_1_word);
|
cfgWrite(u32, "device_feature_select", vp.feature_version_1_word);
|
||||||
if (cfgRead(u32, "device_feature") & vp.feature_version_1_bit == 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
// Accept exactly VERSION_1, plus EDID when the device offered it (never a feature it didn't).
|
// 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);
|
cfgWrite(u32, "driver_feature", vp.feature_version_1_bit);
|
||||||
orStatus(vp.status_features_ok);
|
orStatus(vp.status_features_ok);
|
||||||
if (cfgRead(u8, "device_status") & vp.status_features_ok == 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,15 +328,15 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
cfgWrite(u16, "queue_select", 0);
|
cfgWrite(u16, "queue_select", 0);
|
||||||
const device_qsize = cfgRead(u16, "queue_size");
|
const device_qsize = cfgRead(u16, "queue_size");
|
||||||
if (device_qsize < 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;
|
return false;
|
||||||
}
|
}
|
||||||
ring = dma.alloc(4096, dma.coherent) orelse {
|
ring = dma.alloc(4096, dma.coherent) orelse {
|
||||||
log("virtio-gpu: virtqueue allocation failed\n", .{});
|
std.log.info("virtqueue allocation failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
command = dma.alloc(4096, dma.coherent) orelse {
|
command = dma.alloc(4096, dma.coherent) orelse {
|
||||||
log("virtio-gpu: command-buffer allocation failed\n", .{});
|
std.log.info("command-buffer allocation failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
mmio.write(u16, ring.virtual + avail_offset, 1); // VIRTQ_AVAIL_F_NO_INTERRUPT: we poll
|
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,
|
.height = max_height,
|
||||||
};
|
};
|
||||||
if (command_nodata(@sizeOf(vg.ResourceCreate2d)) != ok_nodata) {
|
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;
|
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
|
// 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.
|
// the same physical pages. The device needs the guest-physical base for attach_backing.
|
||||||
surface = shm.create(scanout_bytes) orelse {
|
surface = shm.create(scanout_bytes) orelse {
|
||||||
log("virtio-gpu: scanout surface allocation failed\n", .{});
|
std.log.info("scanout surface allocation failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const surface_physical = shm.physical(surface.handle) orelse {
|
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;
|
return false;
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
@@ -396,15 +389,15 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
const entry: *vg.MemEntry = @ptrFromInt(command.virtual + request_offset + @sizeOf(vg.ResourceAttachBacking));
|
const entry: *vg.MemEntry = @ptrFromInt(command.virtual + request_offset + @sizeOf(vg.ResourceAttachBacking));
|
||||||
entry.* = .{ .addr = surface_physical, .length = @intCast(scanout_bytes) };
|
entry.* = .{ .addr = surface_physical, .length = @intCast(scanout_bytes) };
|
||||||
if (command_nodata(@sizeOf(vg.ResourceAttachBacking) + @sizeOf(vg.MemEntry)) != ok_nodata) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!setScanoutRect()) {
|
if (!setScanoutRect()) {
|
||||||
log("virtio-gpu: set_scanout failed\n", .{});
|
std.log.info("set_scanout failed", .{});
|
||||||
return false;
|
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
|
// 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
|
// 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));
|
for (0..pixel_count) |i| pixels[i] = testPixel(@intCast(i));
|
||||||
|
|
||||||
if (!presentFull()) {
|
if (!presentFull()) {
|
||||||
log("virtio-gpu: initial present failed\n", .{});
|
std.log.info("initial present failed", .{});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// The scanout surface is CPU-visible RAM: read the pattern back to prove the mapping,
|
// 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".
|
// which together with the flush ack above is the automated stand-in for "it's on screen".
|
||||||
mmio.rmb();
|
mmio.rmb();
|
||||||
if (pixels[0] != testPixel(0) or pixels[pixel_count / 2] != testPixel(@intCast(pixel_count / 2))) {
|
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;
|
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).
|
// Offer the shared surface to the compositor so it upgrades off the GOP floor (V4).
|
||||||
announce();
|
announce();
|
||||||
@@ -456,18 +449,18 @@ fn setScanoutRect() bool {
|
|||||||
/// a device that doesn't offer EDID, or a missing/short block, is logged and ignored.
|
/// a device that doesn't offer EDID, or a missing/short block, is logged and ignored.
|
||||||
fn readEdid() void {
|
fn readEdid() void {
|
||||||
if (!edid_available) {
|
if (!edid_available) {
|
||||||
log("virtio-gpu: EDID not offered by device\n", .{});
|
std.log.info("EDID not offered by device", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const request = requestAt(vg.GetEdid);
|
const request = requestAt(vg.GetEdid);
|
||||||
request.* = .{ .hdr = .{ .type = @intFromEnum(vg.CmdType.get_edid) }, .scanout = 0 };
|
request.* = .{ .hdr = .{ .type = @intFromEnum(vg.CmdType.get_edid) }, .scanout = 0 };
|
||||||
if (!submit(@sizeOf(vg.GetEdid), @sizeOf(vg.RespEdid))) {
|
if (!submit(@sizeOf(vg.GetEdid), @sizeOf(vg.RespEdid))) {
|
||||||
log("virtio-gpu: EDID request not acked\n", .{});
|
std.log.info("EDID request not acked", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const response: *vg.RespEdid = @ptrFromInt(command.virtual + response_offset);
|
const response: *vg.RespEdid = @ptrFromInt(command.virtual + response_offset);
|
||||||
if (response.hdr.type != @intFromEnum(vg.CmdType.resp_ok_edid) or response.size < 64) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
// The first detailed timing descriptor (EDID base-block offset 54) is the preferred mode:
|
// 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 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);
|
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);
|
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
|
/// 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;
|
if (ipc.lookup(.device_manager)) |h| break h;
|
||||||
system.sleep(20);
|
system.sleep(20);
|
||||||
} else {
|
} else {
|
||||||
log("virtio-gpu: no device manager to hello\n", .{});
|
std.log.info("no device manager to hello", .{});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const hello = dm.Hello{ .role = @intFromEnum(dm.Role.bus), .device_id = device_id };
|
const hello = dm.Hello{ .role = @intFromEnum(dm.Role.bus), .device_id = device_id };
|
||||||
var reply: [dm.reply_size]u8 = undefined;
|
var reply: [dm.reply_size]u8 = undefined;
|
||||||
const n = ipc.call(manager, std.mem.asBytes(&hello), &reply) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
if (n < dm.reply_size or std.mem.bytesToValue(dm.HelloReply, reply[0..dm.reply_size]).status != 0) {
|
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;
|
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
|
/// 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;
|
if (ipc.lookup(.display)) |h| break h;
|
||||||
system.sleep(20);
|
system.sleep(20);
|
||||||
} else {
|
} 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;
|
return;
|
||||||
};
|
};
|
||||||
var request = dp.Request{
|
var request = dp.Request{
|
||||||
@@ -569,10 +562,10 @@ fn announce() void {
|
|||||||
};
|
};
|
||||||
var reply: [dp.reply_size]u8 = undefined;
|
var reply: [dp.reply_size]u8 = undefined;
|
||||||
_ = ipc.callCap(display, std.mem.asBytes(&request), &reply, surface.handle) catch {
|
_ = 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;
|
return;
|
||||||
};
|
};
|
||||||
log("virtio-gpu: announced scanout to display\n", .{});
|
std.log.info("announced scanout to display", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `sp.Reply{status}` written into `reply`.
|
/// A `sp.Reply{status}` written into `reply`.
|
||||||
@@ -621,7 +614,7 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
runtime.service.run(256, .{
|
runtime.service.run(256, .{
|
||||||
|
|||||||
@@ -118,10 +118,12 @@ fn appendLocked(pid: u32, name: []const u8, level: abi.KlogLevel, now: u64, byte
|
|||||||
var rest = bytes;
|
var rest = bytes;
|
||||||
while (rest.len != 0) {
|
while (rest.len != 0) {
|
||||||
const newline = std.mem.indexOfScalar(u8, rest, '\n');
|
const newline = std.mem.indexOfScalar(u8, rest, '\n');
|
||||||
// The record payload excludes the newline: a record IS a line (or the
|
// The record payload excludes the newline: a record IS a line. Raw
|
||||||
// open tail of one when the emission didn't end in '\n').
|
// 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 = 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)
|
if (line.len != 0 or line_complete)
|
||||||
_ = ring.append(pid, name, level, now, line, line.len > abi.klog_maximum_message);
|
_ = ring.append(pid, name, level, now, line, line.len > abi.klog_maximum_message);
|
||||||
render(pid, name, level, line, line_complete);
|
render(pid, name, level, line, line_complete);
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ const power = runtime.power_protocol;
|
|||||||
/// integer decode names the opcodes instead of bare 0x0A/0x0B/… (docs/coding-standards.md).
|
/// integer decode names the opcodes instead of bare 0x0A/0x0B/… (docs/coding-standards.md).
|
||||||
const opcodes = aml.opcodes;
|
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
|
// 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.
|
// window — the Hal routes every port access through this one claim.
|
||||||
var node_id: u64 = 0;
|
var node_id: u64 = 0;
|
||||||
@@ -163,12 +158,12 @@ pub fn main(init: runtime.process.Init) void {
|
|||||||
};
|
};
|
||||||
var namespace = result.namespace;
|
var namespace = result.namespace;
|
||||||
const devices = aml.deviceCount(&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 (floor) |minimum| {
|
||||||
if (devices >= minimum) {
|
if (devices >= minimum) {
|
||||||
_ = runtime.system.write("acpi-parse: ok\n");
|
_ = runtime.system.write("acpi-parse: ok\n");
|
||||||
} else {
|
} 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.
|
// Self-verify mode is standalone (no manager); stop before reporting.
|
||||||
while (true) runtime.system.sleep(1000);
|
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 hid = entry.hid[0..entry.hid_len];
|
||||||
const desc = acpi_ids.description(hid);
|
const desc = acpi_ids.description(hid);
|
||||||
if (desc.len != 0)
|
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
|
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| {
|
if (manager) |h| {
|
||||||
var report = protocol.ChildAdded{ .parent = node_id, .bus_address = entry.device_id, .identity = 0, .device_id = entry.device_id };
|
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]);
|
@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 {};
|
_ = 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);
|
armPowerButton(endpoint);
|
||||||
return true;
|
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;
|
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) };
|
var event = power.EventMessage{ .event = @intFromEnum(which), .code = @truncate(code) };
|
||||||
event.hid = hid;
|
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));
|
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);
|
applyCrs(&descriptor, node, interpreter);
|
||||||
|
|
||||||
const id = device.register(node_id, &descriptor) orelse {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
registered[registered_count] = .{ .hid = hid, .hid_len = @intCast(hid_len), .device_id = id, .resource_count = descriptor.resource_count };
|
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 device = runtime.device;
|
||||||
const system = runtime.system;
|
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 —
|
/// 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
|
/// Serial Bus Controller / USB Controller / XHCI — named from pci-class.zig rather
|
||||||
/// than written as the bare 0x0C0330 (docs/coding-standards.md, "Named values").
|
/// 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 {
|
fn pruneChildrenOf(reporter: u32) void {
|
||||||
for (&children) |*child| {
|
for (&children) |*child| {
|
||||||
if (child.used and child.reporter == reporter) {
|
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;
|
child.used = false;
|
||||||
const event = protocol.ChildRemoved{ .parent = child.parent, .bus_address = child.bus_address };
|
const event = protocol.ChildRemoved{ .parent = child.parent, .bus_address = child.bus_address };
|
||||||
publishEvent(std.mem.asBytes(&event));
|
publishEvent(std.mem.asBytes(&event));
|
||||||
@@ -270,7 +262,7 @@ fn addDriver(name: []const u8, device_id: u64, speaks_protocol: bool) void {
|
|||||||
spawnDriver(driver);
|
spawnDriver(driver);
|
||||||
return;
|
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
|
/// (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;
|
argument_count = 1;
|
||||||
}
|
}
|
||||||
const child = system.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
|
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;
|
driver.state = .failed;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -299,9 +291,9 @@ fn spawnDriver(driver: *Driver) void {
|
|||||||
driver.state = .running;
|
driver.state = .running;
|
||||||
}
|
}
|
||||||
if (driver.device_id != protocol.no_device) {
|
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 {
|
} 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;
|
const reason = runtime.process.exitReason(driver.process_id) orelse .fault;
|
||||||
if (reason == .exited) {
|
if (reason == .exited) {
|
||||||
driver.state = .stopped;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const now = system.clock();
|
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;
|
driver.restarts = if (alive_ns < fast_death_ns) driver.restarts + 1 else 1;
|
||||||
if (driver.restarts >= crash_loop_cap) {
|
if (driver.restarts >= crash_loop_cap) {
|
||||||
driver.state = .failed;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const delay_ms = backoff_base_ms << @intCast(driver.restarts - 1);
|
const delay_ms = backoff_base_ms << @intCast(driver.restarts - 1);
|
||||||
driver.state = .restarting;
|
driver.state = .restarting;
|
||||||
driver.restart_due_ns = now + delay_ms * 1_000_000;
|
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);
|
_ = system.timerOnce(manager_endpoint, delay_ms + 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +330,7 @@ fn onDriverExit(driver: *Driver) void {
|
|||||||
fn sweepDeadlines() void {
|
fn sweepDeadlines() void {
|
||||||
const now = system.clock();
|
const now = system.clock();
|
||||||
if (test_kill_pid != 0 and now >= test_kill_due_ns) {
|
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);
|
_ = system.kill(test_kill_pid);
|
||||||
test_kill_pid = 0;
|
test_kill_pid = 0;
|
||||||
}
|
}
|
||||||
@@ -346,7 +338,7 @@ fn sweepDeadlines() void {
|
|||||||
if (!driver.used) continue;
|
if (!driver.used) continue;
|
||||||
switch (driver.state) {
|
switch (driver.state) {
|
||||||
.awaiting_hello => if (now >= driver.hello_deadline_ns) {
|
.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);
|
_ = system.kill(driver.process_id);
|
||||||
// The exit notification finishes the job via onDriverExit.
|
// 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;
|
var status: i32 = 0;
|
||||||
if (hello.version != protocol.version) {
|
if (hello.version != protocol.version) {
|
||||||
status = -1;
|
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| {
|
} else if (driverByProcess(sender)) |driver| {
|
||||||
driver.state = .running;
|
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
|
// 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.
|
// 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")) {
|
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 {
|
} else {
|
||||||
status = -1;
|
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 };
|
const hello_reply = protocol.HelloReply{ .status = status };
|
||||||
@memcpy(reply[0..protocol.reply_size], std.mem.asBytes(&hello_reply));
|
@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;
|
var status: i32 = 0;
|
||||||
if (driverByProcess(sender)) |driver| {
|
if (driverByProcess(sender)) |driver| {
|
||||||
if (!addChild(report.parent, report.bus_address, report.identity, report.device_id, sender)) status = -1;
|
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]);
|
if (status == 0) publishEvent(message[0..protocol.child_added_size]);
|
||||||
// Matching from reports (M19.3): a registered child whose identity
|
// Matching from reports (M19.3): a registered child whose identity
|
||||||
// names a driver gets one, once — re-reports after a bus restart
|
// 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;
|
var status: i32 = -1;
|
||||||
for (&children) |*child| {
|
for (&children) |*child| {
|
||||||
if (child.used and child.parent == report.parent and child.bus_address == report.bus_address and child.reporter == sender) {
|
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;
|
child.used = false;
|
||||||
status = 0;
|
status = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,6 @@ const on_disk = @import("on-disk.zig");
|
|||||||
const protocol = runtime.vfs_protocol;
|
const protocol = runtime.vfs_protocol;
|
||||||
const dma = runtime.dma;
|
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";
|
const mount_point = "/mnt/usb";
|
||||||
|
|
||||||
// The engine's BlockDevice, backed by the `.block` driver plus a DMA bounce
|
// 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");
|
_ = runtime.system.write("/system/services/fat: not a FAT filesystem\n");
|
||||||
return false;
|
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
|
// 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.
|
// comes up). From here the VFS routes /mnt/usb/... to this server.
|
||||||
var tries: u32 = 0;
|
var tries: u32 = 0;
|
||||||
while (tries < 100) : (tries += 1) {
|
while (tries < 100) : (tries += 1) {
|
||||||
if (runtime.fs.mount(mount_point, endpoint)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
runtime.system.sleep(50);
|
runtime.system.sleep(50);
|
||||||
|
|||||||
@@ -145,26 +145,21 @@ fn restartChild(id: u32) void {
|
|||||||
// An unknown reason (the record aged out) is treated as a crash worth restarting.
|
// An unknown reason (the record aged out) is treated as a crash worth restarting.
|
||||||
const reason = runtime.process.exitReason(id) orelse .fault;
|
const reason = runtime.process.exitReason(id) orelse .fault;
|
||||||
if (reason == .exited) {
|
if (reason == .exited) {
|
||||||
logLine("/system/services/init: {s} exited cleanly; not restarting\n", .{service});
|
std.log.info("{s} exited cleanly; not restarting", .{service});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
restart_counts[i] += 1;
|
restart_counts[i] += 1;
|
||||||
if (restart_counts[i] > maximum_restarts) {
|
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;
|
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;
|
if (runtime.system.spawnSupervised(service, &.{}, supervision_endpoint)) |new_id| child_ids[i] = new_id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// An untracked child (e.g. the log-flush one-shot): nothing to restart.
|
// 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
|
/// Look up the power service and subscribe our endpoint (handed over as the
|
||||||
/// call's capability) so events arrive as buffered messages here.
|
/// call's capability) so events arrive as buffered messages here.
|
||||||
fn subscribePower() void {
|
fn subscribePower() void {
|
||||||
|
|||||||
@@ -113,13 +113,6 @@ fn fail(out: []u8) usize {
|
|||||||
return writeReply(out, .{ .status = -1 }, &.{});
|
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 ----------------------------------------------------------
|
// --- mount routing ----------------------------------------------------------
|
||||||
|
|
||||||
/// Forward an open under a mount to its backend and, on success, allocate a local
|
/// 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| {
|
for (&mounts) |*m| {
|
||||||
if (m.used and std.mem.eql(u8, m.prefix[0..m.prefix_len], prefix)) {
|
if (m.used and std.mem.eql(u8, m.prefix[0..m.prefix_len], prefix)) {
|
||||||
m.backend = backend;
|
m.backend = backend;
|
||||||
writeLine("/system/services/vfs: remounted {s}\n", .{prefix});
|
std.log.info("remounted {s}", .{prefix});
|
||||||
return writeReply(out, .{ .status = 0 }, &.{});
|
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]);
|
@memcpy(m.prefix[0..l], prefix[0..l]);
|
||||||
m.prefix_len = l;
|
m.prefix_len = l;
|
||||||
m.backend = backend;
|
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 }, &.{});
|
return writeReply(out, .{ .status = 0 }, &.{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +226,7 @@ fn doUnmount(out: []u8, prefix: []const u8) usize {
|
|||||||
for (&mounts) |*m| {
|
for (&mounts) |*m| {
|
||||||
if (m.used and std.mem.eql(u8, m.prefix[0..m.prefix_len], prefix)) {
|
if (m.used and std.mem.eql(u8, m.prefix[0..m.prefix_len], prefix)) {
|
||||||
m.used = false;
|
m.used = false;
|
||||||
writeLine("/system/services/vfs: unmounted {s}\n", .{prefix});
|
std.log.info("unmounted {s}", .{prefix});
|
||||||
return writeReply(out, .{ .status = 0 }, &.{});
|
return writeReply(out, .{ .status = 0 }, &.{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,7 +245,7 @@ fn releaseClientHandles(client: u32) void {
|
|||||||
released += 1;
|
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.
|
/// Handle one request from `sender`; write the reply into `out`, return its length.
|
||||||
|
|||||||
+5
-5
@@ -458,7 +458,7 @@ CASES = [
|
|||||||
"smp": 4,
|
"smp": 4,
|
||||||
"timeout": 150,
|
"timeout": 150,
|
||||||
# usb-kbd/usb-mouse ride the default boot xHCI bus (see qemu_args).
|
# 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"},
|
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
||||||
# USB mass storage end to end: the boot usb-storage device (the FAT32 image,
|
# 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
|
# which has a real 0x55AA boot sector) is enough — the manager spawns
|
||||||
@@ -526,7 +526,7 @@ CASES = [
|
|||||||
{"name": "acpi-ps2",
|
{"name": "acpi-ps2",
|
||||||
"smp": 4,
|
"smp": 4,
|
||||||
"timeout": 150,
|
"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"device-manager: spawned \S*ps2-bus[\s\S]*"
|
||||||
r"ps2-bus: keyboard driver attached",
|
r"ps2-bus: keyboard driver attached",
|
||||||
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
||||||
@@ -572,8 +572,8 @@ CASES = [
|
|||||||
{"name": "acpi-report",
|
{"name": "acpi-report",
|
||||||
"smp": 4,
|
"smp": 4,
|
||||||
"timeout": 150,
|
"timeout": 150,
|
||||||
"expect": r"acpi: reported PNP0303 \(device \d+, 3 resources\)[\s\S]*"
|
"expect": r"discovery: reported PNP0303 \(device \d+, 3 resources\)[\s\S]*"
|
||||||
r"acpi: reported PNP0F13 \(device \d+, 1 resources\)",
|
r"discovery: reported PNP0F13 \(device \d+, 1 resources\)",
|
||||||
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
||||||
# M19.1/M19.3: the ring-3 PCI scan. pci-bus walks the ECAM through its mmio_map
|
# 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
|
# grant and registers every function it finds; the kernel's own walk retired, so
|
||||||
@@ -589,7 +589,7 @@ CASES = [
|
|||||||
"timeout": 60,
|
"timeout": 60,
|
||||||
"expect": r"pci-bus: (\d+) functions found[\s\S]*"
|
"expect": r"pci-bus: (\d+) functions found[\s\S]*"
|
||||||
r"device-manager: test mode: killing the reporter[\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"pci-bus: \1 functions found[\s\S]*"
|
||||||
r"DANOS-TEST-RESULT: PASS",
|
r"DANOS-TEST-RESULT: PASS",
|
||||||
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
||||||
|
|||||||
Reference in New Issue
Block a user