//! /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 }