Files
danos/system/services/acpi/acpi.zig
T
Daniel Samson a299363b59 The AML interpreter runs in ring 3: the acpi service parses (M20.1)
The AML module becomes a build module compiled into both the kernel (for
the \_S5 sleep state it still needs) and the new acpi service — one
source, two builds, no fork. The kernel publishes a single acpi-tables
node: the DSDT/SSDT blobs as memory resources, a broad io_port grant (the
honest trust boundary — firmware AML names whatever ports it chose, known
only after parsing), and the SCI for the M21 event track. The acpi
service claims the node, maps each blob through the ordinary mmio grant
(which preserves the sub-page offset onto the bytecode), and runs the
same parser the kernel does. It self-verifies its namespace Device count
against the kernel's — 34 = 34 — deterministically via an argv the
acpi-parse test passes, so no racing the shared serial buffer. Parse-only
touches no hardware; OperationRegion evaluation waits for _CRS/_STA in
M20.2. The manager spawns 'discovery' (the neutral ramdisk name) at
startup. Suite 56/56.
2026-07-13 03:07:11 +01:00

98 lines
4.1 KiB
Zig

//! /system/services/acpi — the ACPI discovery service: the x86 firmware
//! interpreter, moved out of ring 0 (docs/m19-m20-plan.md, M20). Claims the
//! `acpi-tables` node the kernel publishes (the AML blobs, the broad io_port
//! grant, the SCI), and runs the **shared AML module** in ring 3 — the same
//! parser the kernel uses for `\_S5`, now the sole builder of the device
//! namespace.
//!
//! M20.1 (this increment): claim the node, map each AML blob through the
//! ordinary mmio grant, parse them into a namespace, and log the Device count —
//! which the `acpi-parse` scenario checks equals the kernel's own parse.
//! Parsing touches no hardware (the io_port grant and the interpreter's
//! OperationRegion evaluation come in with `_CRS`/`_STA` at M20.2). Registering
//! and reporting the namespace devices, and retiring the kernel's device build,
//! follow in M20.2 and M20.3.
const std = @import("std");
const runtime = @import("runtime");
const aml = @import("aml");
const device = runtime.device;
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);
}
/// Find the acpi-tables node the kernel published, or null.
fn findTablesNode(buffer: []device.DeviceDescriptor) ?device.DeviceDescriptor {
const total = device.enumerate(buffer);
for (buffer[0..@min(total, buffer.len)]) |d| {
if (d.class == @intFromEnum(device.DeviceClass.acpi_tables)) return d;
}
return null;
}
pub fn main(init: runtime.process.Init) void {
// When the acpi-parse scenario spawns this directly, argv[1] is the kernel's
// own device count to self-verify against — deterministic, no log-scraping.
const expected: ?usize = if (init.arguments.get(1)) |a| (std.fmt.parseInt(usize, a, 10) catch null) else null;
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = runtime.system.write("acpi: out of memory\n");
return;
};
const node = findTablesNode(buffer) orelse {
_ = runtime.system.write("acpi: no acpi-tables node to claim\n");
return;
};
if (!device.claim(node.id)) {
_ = runtime.system.write("acpi: unable to claim acpi-tables\n");
return;
}
// Map each memory resource (an AML blob) and collect the byte slices. The
// grant preserves each blob's sub-page offset, so the mapped pointer lands
// straight on the bytecode.
var blocks: [8][]const u8 = undefined;
var block_count: usize = 0;
for (node.resources[0..@intCast(node.resource_count)], 0..) |resource, index| {
if (resource.kind != @intFromEnum(device.ResourceKind.memory)) continue;
const base = device.mmioMap(node.id, index) orelse {
writeLine("acpi: mmio_map failed for blob {d}\n", .{index});
continue;
};
const pointer: [*]const u8 = @ptrFromInt(base);
blocks[block_count] = pointer[0..@intCast(resource.len)];
block_count += 1;
if (block_count == blocks.len) break;
}
if (block_count == 0) {
_ = runtime.system.write("acpi: no AML blobs on the node\n");
return;
}
const result = aml.parse(runtime.allocator(), blocks[0..block_count]) catch {
_ = runtime.system.write("acpi: AML parse failed\n");
return;
};
var namespace = result.namespace;
const devices = aml.deviceCount(&namespace);
writeLine("acpi: parsed {d} AML blob(s), {d} namespace devices\n", .{ block_count, devices });
if (expected) |want| {
if (devices == want) {
_ = runtime.system.write("acpi-parse: ok\n");
} else {
writeLine("acpi-parse: mismatch (ring-3 {d} vs kernel {d})\n", .{ devices, want });
}
}
// Registration and reports arrive in M20.2; stay resident so the claim
// holds and the service is here to grow into the supervised discoverer.
while (true) runtime.system.sleep(1000);
}
pub const panic = runtime.panic;
comptime {
_ = &runtime.start._start; // pull the runtime entry shim into the image
}