Files
danos/test/system/services/device-list/device-list.zig
Daniel Samson f023f1cfd6 reorg: move test fixtures to test/system/services (source + boot volume)
The 11 QEMU-suite fixtures lived mixed into system/services/ with their
binaries bundled at /system/tests/<name>. Now the repo path is the boot
path, like every real service: test/system/services/<name>. fat-test
moves out of the fat server's directory into its own; display-demo
stays a boot service.

- kernel VFS: setInitialRamdisk derives one read-only initrd mount per
  top-level tree named by the ramdisk entry paths (/system, /test),
  registers ancestors generically with self-parented roots, and refuses
  backend shadowing of any initrd tree
- EFI loader: the fallback walk also enumerates \test (optional — a
  volume without fixtures still boots); manifest and capsule unchanged
- path literals: vfs-test self-open + create probe, process-test
  process_enumerate matches, the args-echo argv[0] expectation; the
  kvfs case now covers the /test root end to end
- docs: DFHS /test rows, tree diagrams, loader prose, and the location
  convention gain the third home; fixed the input-source link

100/100 QEMU cases pass.
2026-07-23 00:11:07 +01:00

86 lines
4.1 KiB
Zig

//! device-list — the `ps` analog for the device tree (docs/device-manager.md
//! M18.3): asks the device manager for the tree over IPC, prints it, then
//! subscribes and prints every published add/remove event. The manager is the
//! one answer to "what devices exist" for user space; nothing here touches a
//! device_* system call.
const std = @import("std");
const ipc = @import("ipc");
const time = @import("time");
const logging = @import("logging");
const device_manager_protocol = @import("device-manager-protocol");
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
var line: [96]u8 = undefined;
_ = logging.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
}
pub fn main() void {
var manager: ?ipc.Handle = null;
var tries: u32 = 0;
while (manager == null and tries < 200) : (tries += 1) {
manager = ipc.lookup(.device_manager);
if (manager == null) time.sleepMillis(20);
}
const h = manager orelse {
_ = logging.write("device-list: no device manager\n");
return;
};
// The snapshot — polled briefly, because at boot the bus drivers may still
// be scanning: an empty first answer usually just means "too early".
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
var count: u32 = 0;
var length: usize = 0;
tries = 0;
while (tries < 20) : (tries += 1) {
const request = device_manager_protocol.Enumerate{};
length = ipc.call(h, std.mem.asBytes(&request), &reply) catch 0;
if (length >= @sizeOf(device_manager_protocol.EnumerateReply)) {
count = std.mem.bytesToValue(device_manager_protocol.EnumerateReply, reply[0..@sizeOf(device_manager_protocol.EnumerateReply)]).count;
if (count != 0) break;
}
time.sleepMillis(100);
}
writeLine("device-list: {d} devices\n", .{count});
var offset: usize = @sizeOf(device_manager_protocol.EnumerateReply);
var index: u32 = 0;
while (index < count and offset + @sizeOf(device_manager_protocol.ChildEntry) <= length) : (index += 1) {
const entry = std.mem.bytesToValue(device_manager_protocol.ChildEntry, reply[offset..][0..@sizeOf(device_manager_protocol.ChildEntry)]);
writeLine("device-list: device {d} port {d} identity {d}\n", .{ entry.parent, entry.bus_address, entry.identity });
offset += @sizeOf(device_manager_protocol.ChildEntry);
}
// The subscription: our endpoint rides as the call's capability; events
// arrive as buffered messages carrying the same structs the bus sends.
const endpoint = ipc.createIpcEndpoint() orelse {
_ = logging.write("device-list: no endpoint\n");
return;
};
const subscribe = device_manager_protocol.Subscribe{};
_ = ipc.callCap(h, std.mem.asBytes(&subscribe), &reply, endpoint) catch {
_ = logging.write("device-list: subscribe failed\n");
return;
};
_ = logging.write("device-list: subscribed\n");
var receive: [device_manager_protocol.message_maximum]u8 = undefined;
while (true) {
const got = ipc.replyWait(endpoint, &.{}, &receive, null);
if (!got.isMessage() or got.len < 1) continue;
switch (receive[0]) {
@intFromEnum(device_manager_protocol.Operation.child_added) => {
if (got.len < device_manager_protocol.child_added_size) continue;
const event = std.mem.bytesToValue(device_manager_protocol.ChildAdded, receive[0..device_manager_protocol.child_added_size]);
writeLine("device-list: added (device {d} port {d})\n", .{ event.parent, event.bus_address });
},
@intFromEnum(device_manager_protocol.Operation.child_removed) => {
if (got.len < device_manager_protocol.child_removed_size) continue;
const event = std.mem.bytesToValue(device_manager_protocol.ChildRemoved, receive[0..device_manager_protocol.child_removed_size]);
writeLine("device-list: removed (device {d} port {d})\n", .{ event.parent, event.bus_address });
},
else => {},
}
}
}