acpi: discovery is handed its node like every other driver

The last claimant. The kernel seeds the acpi-tables node, so it sits in the
same boot snapshot the manager already scans to find the PCI host bridge —
there was never a bootstrap problem, only a lookup nobody had written. The
manager claims it and names it in the spawn; the service stops claiming.

Every driver in the system now receives its hardware rather than taking it.

Two failures on the way, both mine. addDriver puts the device id in argv[1],
and the acpi service read argv[1] as a self-verify device-count floor — so
handed device 7 it decided it was in test mode, printed "acpi-parse: ok",
and never reported a device. The test argument is now floor:N, which a bare
id cannot be mistaken for.

And acpi-parse spawns the service directly rather than through the manager,
so nothing handed it the node. That test now claims and transfers it exactly
as the manager does, which is the right shape: the test plays the manager's
role instead of the service reaching for hardware.

device_claim now has two callers left: the manager, which is the acquirer
and should have it, and the display service's GOP path. That is recorded as
question 10 — the framebuffer is not a device, so the answer is likely that
it leaves the device table rather than being exempted from its rules.

Suite 118/118.
This commit is contained in:
Daniel Samson
2026-08-08 21:54:45 +01:00
parent 6b3a381626
commit b7d97ebb5d
4 changed files with 67 additions and 39 deletions
+11 -1
View File
@@ -3098,7 +3098,17 @@ fn acpiParseTest(boot_information: *const BootInformation) void {
while (i < rd.count) : (i += 1) {
const item = rd.entry(i) orelse continue;
if (!eql(initial_ramdisk.basename(item.name), "discovery")) continue;
_ = process.spawnProcessSupervised(item.blob, 4, &.{ item.name, "1" }, scheduler.currentId(), null) catch 0;
// Hand it the acpi-tables node the way the manager would: claim it here, then
// move it to the child. Discovery no longer claims for itself.
var scratch: [64]device_abi.DeviceDescriptor = undefined;
const seen_devices = devices_broker.enumerate(&scratch);
const tables: ?u64 = for (scratch[0..@min(seen_devices, scratch.len)]) |d| {
if (d.class == @intFromEnum(device_abi.DeviceClass.acpi_tables)) break d.id;
} else null;
const me_parse = scheduler.currentId();
if (tables) |node| _ = claimOk(node, me_parse);
const child = process.spawnProcessSupervised(item.blob, 4, &.{ item.name, "floor:1" }, me_parse, null) catch 0;
if (tables) |node| devices_broker.transfer(node, me_parse, child) catch {};
spawned = true;
break;
}
+17 -9
View File
@@ -104,11 +104,19 @@ fn findTablesNode(buffer: []device.DeviceDescriptor) ?device.DeviceDescriptor {
}
pub fn main(init: process.Init) void {
// When the acpi-parse scenario spawns this directly, argv[1] is a device-count
// *floor* to self-verify against. The kernel no longer parses AML, so there is
// no exact count to match — proving the ring-3 parse found at least a floor of
// devices is the check. Deterministic, no log-scraping.
const floor: ?usize = if (init.arguments.get(1)) |a| (std.fmt.parseInt(usize, a, 10) catch null) else null;
// When the acpi-parse scenario spawns this directly, it passes `floor:N` — a
// device-count floor to self-verify against. The kernel no longer parses AML, so
// there is no exact count to match; proving the ring-3 parse found at least N
// Device objects is the check. Deterministic, no log-scraping.
//
// The `floor:` prefix matters. argv[1] is the assigned device id for every driver
// the manager spawns, so a bare number here would be read as a floor — which is
// exactly what happened when discovery started being given its node: it saw
// argv[1] = "7", decided it was in self-verify mode, and never reported a device.
const floor: ?usize = if (init.arguments.get(1)) |a| blk: {
if (!std.mem.startsWith(u8, a, "floor:")) break :blk null;
break :blk std.fmt.parseInt(usize, a["floor:".len..], 10) catch null;
} else null;
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = logging.write("/system/services/acpi: out of memory\n");
@@ -118,11 +126,11 @@ pub fn main(init: process.Init) void {
_ = logging.write("/system/services/acpi: no acpi-tables node to claim\n");
return;
};
// The node arrived with the spawn: the manager holds it and names it in the call
// that creates this process. Discovery was the last thing in the system that
// acquired hardware by naming it rather than being given it
// (docs/os-development/device-authority.md).
node_id = node.id;
device.claim(node_id) catch |e| {
std.log.warn("unable to claim acpi-tables: {s}", .{@errorName(e)});
return;
};
// Map the node's resources: the AML blobs (bytecode), the FADT (intact
// "FACP" header — decision 3), the io_port grant, and the SCI irq.
@@ -257,6 +257,7 @@ const delegated_drivers = [_][]const u8{
"pci-bus",
"virtio-gpu",
"ps2-bus",
"discovery",
};
/// Matched on the **last path component**, because a driver reaches this table under
@@ -420,6 +421,18 @@ fn initialise(endpoint: ipc.Handle) bool {
const total = device.enumerate(buffer);
const count = @min(total, buffer.len);
// The node discovery needs: the kernel seeds it, so it is in this same snapshot
// and can be handed over like any other assignment. Discovery used to find and
// claim it itself — the last driver that acquired hardware by naming it rather
// than being given it (docs/os-development/device-authority.md).
var tables_node: u64 = device_manager_protocol.no_device;
for (buffer[0..count]) |descriptor| {
if (descriptor.class == @intFromEnum(device.DeviceClass.acpi_tables)) {
tables_node = descriptor.id;
break;
}
}
var matched: usize = 0;
for (buffer[0..count]) |descriptor| {
if (descriptor.class == @intFromEnum(device.DeviceClass.pci_host_bridge)) {
@@ -441,7 +454,7 @@ fn initialise(endpoint: ipc.Handle) bool {
// under the neutral name "discovery", spawned once at startup. It finds and
// claims the acpi-tables (or devicetree-blob) node itself. Not a per-device
// match — it is the discoverer, not a driver bound to one device.
addDriver("discovery", device_manager_protocol.no_device, false);
addDriver("discovery", tables_node, false);
if (test_restart_mode) {
// The driver-restart scenario's fixture: claims device 0 (the tree