From 36145e623b2042d61adb643de5006c0ec33e9479 Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Mon, 13 Jul 2026 03:53:04 +0100 Subject: [PATCH] Delete the retired kernel AML device-building path (M20.3 cleanup) The M20.3 flip moved ACPI namespace enumeration to the ring-3 acpi service; the kernel now builds the namespace only for the \_S5 sleep type. That left the kernel's AML-to-device helpers unreferenced. Remove the dead cluster (wireAcpiDevices, mirrorDevices, applyHid, setEisaHid, applyCrs, parseResourceTemplate, parseAddressSpace, devicePresent, matchHostBridge, findPciNode, readAdr, isPciRootNode, isPciRootHid, PciContext) and every AML-decoding helper it alone used (eisaIdToStr, seg4, cstr, hexDigit, rd16, rd32, readN, readLE, readIntObj, packageLength/PkgLen) plus their tests and the now-orphaned acpi-ids import. The static-table path keeps checksumOk, fadt, readGas, readCntRegister, and rd. Also tidies two stale comments. --- system/devices/acpi.zig | 374 +--------------------------------------- 1 file changed, 1 insertion(+), 373 deletions(-) diff --git a/system/devices/acpi.zig b/system/devices/acpi.zig index abf5015..58a028f 100644 --- a/system/devices/acpi.zig +++ b/system/devices/acpi.zig @@ -17,7 +17,6 @@ const std = @import("std"); const boot_handoff = @import("boot-handoff"); const abi = @import("abi"); -const acpi_ids = @import("acpi-ids"); const parameters = @import("parameters"); const device_model = @import("device-model.zig"); const aml = @import("aml/aml.zig"); @@ -409,9 +408,7 @@ pub fn discover(rsdp_physical: u64, memory_regions: []const boot_handoff.MemoryR // tree (M20.3): the ring-3 acpi service claims the acpi-tables node // (published below), re-parses the same blobs, and registers + reports // the _HID devices itself. The kernel keeps the namespace only for the - // \_S5 sleep type above. The device-building helpers below - // (wireAcpiDevices and friends) are retained but unreferenced — a - // focused dead-code sweep follows the migration. + // \_S5 sleep type above. } else |_| { // AML parse failed (e.g. out of memory); power stays best-effort with // whatever the FADT alone provided. @@ -816,342 +813,6 @@ fn parseDmar(hal: Hal, header: *const SystemDescriptorTableHeader) void { } } -// --- AML namespace -> generic device tree ----------------------------------- - -/// The PCI bus context while descending the ACPI namespace: the generic host -/// bridge whose children ACPI address (`_ADR`) devices resolve against, and the bus number. -const PciContext = struct { bridge: *device_model.Device, bus: u8 }; - -/// Mirror the ACPI namespace's Device objects into the generic tree, *merging* -/// them with the PCI-enumerated nodes: a PCI root bridge (`PNP0A03`/`PNP0A08`) -/// folds onto the existing `pci_host_bridge`, and each addressed (`_ADR`) device folds onto -/// the matching PCI function (annotating it with the ACPI hardware ID (`_HID`) and nesting the -/// ACPI-only children — keyboard, RTC, … — beneath it). Namespace devices with no -/// PCI match land under a synthetic `acpi` node. -fn wireAcpiDevices(device_tree: *DeviceTree, aml_namespace: *aml.Namespace, hal: Hal) !void { - var arena = std.heap.ArenaAllocator.init(device_tree.allocator); - defer arena.deinit(); - var interpreter = aml.Interpreter.init(aml_namespace, .{ - .mapMmio = hal.mapMmio, - .pioRead = hal.pioRead, - .pioWrite = hal.pioWrite, - }, arena.allocator()); - - const acpi_root = try device_tree.addChild(device_tree.root, .unknown, "acpi"); - try mirrorDevices(device_tree, aml_namespace.root, acpi_root, null, &interpreter); -} - -fn mirrorDevices(device_tree: *DeviceTree, node: *aml.Node, parent_device: *device_model.Device, context: ?PciContext, interpreter: *aml.Interpreter) (error{OutOfMemory})!void { - var child = node.first_child; - while (child) |c| : (child = c.next_sibling) { - if (c.kind != .device) { - // A scope — the System Bus (\_SB), General Purpose Events (\_GPE), … — - // descend without adding a node. - try mirrorDevices(device_tree, c, parent_device, context, interpreter); - continue; - } - - // Skip devices the firmware reports as not present (via a device-status (`_STA`) method), - // along with their whole subtree — per the ACPI rules. - if (!devicePresent(interpreter, c)) continue; - - var mirrored_device: *device_model.Device = undefined; - var child_context = context; - - if (isPciRootNode(c)) { - // The PCI root bridge folds onto the generic host bridge. - mirrored_device = matchHostBridge(device_tree) orelse - try device_tree.addChild(parent_device, .acpi_device, &c.segment); - child_context = .{ .bridge = mirrored_device, .bus = 0 }; - } else { - // An addressed device folds onto its matching PCI function; anything - // else becomes a fresh node under the current parent. - mirrored_device = pick: { - if (context) |pc| { - if (readAdr(c)) |adr| { - if (findPciNode(pc.bridge, pc.bus, adr)) |pnode| break :pick pnode; - } - } - break :pick try device_tree.addChild(parent_device, .acpi_device, &c.segment); - }; - } - - applyHid(mirrored_device, c, interpreter); - applyCrs(mirrored_device, c, interpreter); - try mirrorDevices(device_tree, c, mirrored_device, child_context, interpreter); - } -} - -/// Evaluate a device's status (`_STA`) to decide if it is present. An absent status -/// (`_STA`) means present by default; an evaluation failure is treated as present too (we'd -/// rather over-report than hide a device we couldn't introspect). -fn devicePresent(interpreter: *aml.Interpreter, node: *aml.Node) bool { - const sta = aml.Namespace.childOf(node, seg4("_STA")) orelse return true; - const obj = interpreter.evaluate(sta, &.{}) catch return true; - const status = obj.asInteger() catch return true; - return (status & 0x01) != 0; // bit 0 = present -} - -/// The first PCI host bridge in the generic tree (segment 0). -fn matchHostBridge(device_tree: *DeviceTree) ?*device_model.Device { - var c = device_tree.root.first_child; - while (c) |ch| : (c = ch.next_sibling) { - if (ch.class == .pci_host_bridge) return ch; - } - return null; -} - -/// The PCI function node under `bridge` at the address the device's address object -/// (`_ADR`) names (device/function on -/// `bus`), or null. -fn findPciNode(bridge: *device_model.Device, bus: u8, adr: u32) ?*device_model.Device { - const device: u16 = @truncate((adr >> 16) & 0x1F); - const function: u16 = @truncate(adr & 0x7); - const target: u16 = (@as(u16, bus) << 8) | (device << 3) | function; - var c = bridge.first_child; - while (c) |ch| : (c = ch.next_sibling) { - if (ch.ids.pci_bdf) |bdf| { - if (bdf == target) return ch; - } - } - return null; -} - -/// A device's address (`_ADR`) — a static integer Name — or null. -fn readAdr(node: *aml.Node) ?u32 { - const n = aml.Namespace.childOf(node, seg4("_ADR")) orelse return null; - if (n.kind != .name) return null; - var p: usize = 0; - return @truncate(readIntObj(n.value, &p) orelse return null); -} - -/// Whether a `_HID` string names a PCI(e) host bridge. -fn isPciRootHid(hid: []const u8) bool { - const id = acpi_ids.HardwareId.fromHid(hid) orelse return false; - return id == .pci_bus or id == .pci_express_root_bridge; -} - -/// Whether a namespace device is a PCI(e) host bridge. A packed EISA id is decoded -/// to its string form first, so both encodings answer through the one registry. -fn isPciRootNode(node: *aml.Node) bool { - const hid = aml.Namespace.childOf(node, seg4("_HID")) orelse return false; - if (hid.kind != .name or hid.value.len == 0) return false; - switch (hid.value[0]) { - 0x00, 0x01, 0xFF, 0x0A, 0x0B, 0x0C, 0x0E => { - var p: usize = 0; - const n = readIntObj(hid.value, &p) orelse return false; - var buffer: [8]u8 = undefined; - return isPciRootHid(eisaIdToStr(@truncate(n), &buffer)); - }, - 0x0D => return isPciRootHid(cstr(hid.value[1..])), - else => return false, - } -} - -/// Read a device's hardware ID (`_HID`) into the generic device: an integer decodes as an EISA -/// id ("PNP0A03"), a string is taken verbatim. Handles both the common static -/// Name form and a Method form (evaluated). -fn applyHid(device: *device_model.Device, node: *aml.Node, interpreter: *aml.Interpreter) void { - const hid = aml.Namespace.childOf(node, seg4("_HID")) orelse return; - if (hid.kind == .method) { - const obj = interpreter.evaluate(hid, &.{}) catch return; - switch (obj) { - .integer => |n| setEisaHid(device, @truncate(n)), - .string => |s| device.setHid(s), - else => {}, - } - return; - } - if (hid.kind != .name or hid.value.len == 0) return; - const v = hid.value; - switch (v[0]) { - 0x00, 0x01, 0xFF, 0x0A, 0x0B, 0x0C, 0x0E => { - var p: usize = 0; - const n = readIntObj(v, &p) orelse return; - setEisaHid(device, @truncate(n)); - }, - 0x0D => device.setHid(cstr(v[1..])), // StringPrefix - else => {}, - } -} - -fn setEisaHid(device: *device_model.Device, id: u32) void { - device.ids.acpi_hid = id; - var buffer: [8]u8 = undefined; - device.setHid(eisaIdToStr(id, &buffer)); -} - -/// Parse a device's current resource settings (`_CRS`). The evaluator handles both the static -/// `Buffer` form (a `Name`) and the method form uniformly, yielding the -/// ResourceTemplate bytes we then decode. -fn applyCrs(device: *device_model.Device, node: *aml.Node, interpreter: *aml.Interpreter) void { - const crs = aml.Namespace.childOf(node, seg4("_CRS")) orelse return; - const obj = interpreter.evaluate(crs, &.{}) catch return; - const buffer = switch (obj) { - .buffer => |b| b, - else => return, - }; - parseResourceTemplate(device, buffer); -} - -/// Walk a ResourceTemplate byte list, adding recognised descriptors as resources. -fn parseResourceTemplate(device: *device_model.Device, bytes: []const u8) void { - var i: usize = 0; - while (i < bytes.len) { - const tag = bytes[i]; - if (tag & 0x80 == 0) { - // Small descriptor: length in low 3 bits, type in bits [6:3]. - const len: usize = tag & 0x07; - const body = i + 1; - if (body + len > bytes.len) break; - switch ((tag >> 3) & 0x0F) { - 0x04 => if (len >= 2) { // IRQ: a 16-bit mask, one resource per set bit - const mask = @as(u16, bytes[body]) | (@as(u16, bytes[body + 1]) << 8); - var b: usize = 0; - while (b < 16) : (b += 1) { - if (mask & (@as(u16, 1) << @intCast(b)) != 0) _ = device.addResource(.irq, b, 1); - } - }, - 0x08 => if (len >= 7) { // IO port: minimum at +1, length at +6 - _ = device.addResource(.io_port, rd16(bytes, body + 1), bytes[body + 6]); - }, - 0x09 => if (len >= 3) { // Fixed IO: base at +0, length at +2 - _ = device.addResource(.io_port, rd16(bytes, body), bytes[body + 2]); - }, - 0x0F => break, // EndTag - else => {}, - } - i = body + len; - } else { - // Large descriptor: 16-bit length follows the tag. - if (i + 3 > bytes.len) break; - const len: usize = @intCast(rd16(bytes, i + 1)); - const body = i + 3; - if (body + len > bytes.len) break; - switch (tag) { - 0x85 => if (len >= 17) { // Memory32: minimum at +1, length at +13 - _ = device.addResource(.memory, rd32(bytes, body + 1), rd32(bytes, body + 13)); - }, - 0x86 => if (len >= 9) { // Memory32Fixed: base at +1, length at +5 - _ = device.addResource(.memory, rd32(bytes, body + 1), rd32(bytes, body + 5)); - }, - 0x89 => if (len >= 2) { // Extended IRQ: count at +1, then count u32s - const count = bytes[body + 1]; - var k: usize = 0; - while (k < count and body + 2 + k * 4 + 4 <= body + len) : (k += 1) { - _ = device.addResource(.irq, rd32(bytes, body + 2 + k * 4), 1); - } - }, - 0x87, 0x88, 0x8A => parseAddressSpace(device, tag, bytes[body .. body + len]), - else => {}, - } - i = body + len; - } - } -} - -/// Word/DWord/QWord address-space descriptors: resource type at [0], then -/// granularity/minimum/maximum/translation/length, each of width `w`. -fn parseAddressSpace(device: *device_model.Device, tag: u8, body: []const u8) void { - const w: usize = switch (tag) { - 0x88 => 2, // Word - 0x87 => 4, // DWord - else => 8, // QWord (0x8A) - }; - if (body.len < 3 + 5 * w) return; - const minimum = readN(body, 3 + w, w); - const length = readN(body, 3 + 4 * w, w); - const kind: device_model.ResourceKind = switch (body[0]) { - 0 => .memory, - 1 => .io_port, - else => .bus_range, - }; - _ = device.addResource(kind, minimum, length); -} - -/// Decode a packed EISA id into its 7-char string (e.g. 0x030AD041 -> "PNP0A03"). -fn eisaIdToStr(id: u32, buffer: *[8]u8) []const u8 { - const b0: u16 = @intCast(id & 0xFF); - const b1: u16 = @intCast((id >> 8) & 0xFF); - const b2: u8 = @truncate(id >> 16); - const b3: u8 = @truncate(id >> 24); - const mfg = (b0 << 8) | b1; - buffer[0] = '@' + @as(u8, @intCast((mfg >> 10) & 0x1F)); - buffer[1] = '@' + @as(u8, @intCast((mfg >> 5) & 0x1F)); - buffer[2] = '@' + @as(u8, @intCast(mfg & 0x1F)); - buffer[3] = hexDigit((b2 >> 4) & 0xF); - buffer[4] = hexDigit(b2 & 0xF); - buffer[5] = hexDigit((b3 >> 4) & 0xF); - buffer[6] = hexDigit(b3 & 0xF); - return buffer[0..7]; -} - -fn hexDigit(n: u8) u8 { - return if (n < 10) '0' + n else 'A' + (n - 10); -} - -fn seg4(comptime s: *const [4:0]u8) [4]u8 { - return s[0..4].*; -} - -fn cstr(bytes: []const u8) []const u8 { - const index = std.mem.indexOfScalar(u8, bytes, 0) orelse bytes.len; - return bytes[0..index]; -} - -const PkgLen = struct { value: usize, size: usize }; - -fn packageLength(bytes: []const u8, p: usize) ?PkgLen { - if (p >= bytes.len) return null; - const lead = bytes[p]; - const follow: usize = lead >> 6; - if (p + 1 + follow > bytes.len) return null; - if (follow == 0) return .{ .value = lead & 0x3F, .size = 1 }; - var value: usize = lead & 0x0F; - var i: usize = 0; - while (i < follow) : (i += 1) value |= @as(usize, bytes[p + 1 + i]) << @intCast(4 + i * 8); - return .{ .value = value, .size = 1 + follow }; -} - -/// Read an AML integer object at `p`, advancing `p` past it. -fn readIntObj(bytes: []const u8, p: *usize) ?u64 { - if (p.* >= bytes.len) return null; - const opcode = bytes[p.*]; - p.* += 1; - return switch (opcode) { - 0x00 => 0, - 0x01 => 1, - 0xFF => 0xFF, - 0x0A => readLE(bytes, p, 1), - 0x0B => readLE(bytes, p, 2), - 0x0C => readLE(bytes, p, 4), - 0x0E => readLE(bytes, p, 8), - else => null, - }; -} - -fn readLE(bytes: []const u8, p: *usize, n: usize) ?u64 { - if (p.* + n > bytes.len) return null; - const v = readN(bytes, p.*, n); - p.* += n; - return v; -} - -fn readN(bytes: []const u8, off: usize, n: usize) u64 { - var v: u64 = 0; - var k: usize = 0; - while (k < n and off + k < bytes.len) : (k += 1) v |= @as(u64, bytes[off + k]) << @intCast(k * 8); - return v; -} - -fn rd16(bytes: []const u8, off: usize) u64 { - return readN(bytes, off, 2); -} - -fn rd32(bytes: []const u8, off: usize) u64 { - return readN(bytes, off, 4); -} - // --- helpers ---------------------------------------------------------------- /// Sum `len` bytes; an ACPI table/pointer is valid when the low 8 bits are zero. @@ -1192,42 +853,9 @@ fn readCntRegister(base: [*]align(1) const u8, len: usize, xoff: usize, legacy_o return .{ .mmio = false, .address = port, .width = width }; } -/// The mapped configuration space of one PCI function (its 4 KiB ECAM page). Mapped -/// writable so BAR sizing can probe it; reads and writes both go through here. /// Read a little-endian integer at `off` from a (possibly unaligned) byte pointer. /// x86 is little-endian and native, so an unaligned load suffices. fn rd(comptime T: type, bytes: [*]align(1) const u8, off: usize) T { const p: *align(1) const T = @ptrCast(bytes + off); return p.*; } - -// --- tests ------------------------------------------------------------------ - -test "eisaIdToStr decodes a packed EISA id" { - var buffer: [8]u8 = undefined; - // 0x030AD041 is the well-known encoding of "PNP0A03" (PCI root bridge). - try std.testing.expectEqualStrings("PNP0A03", eisaIdToStr(0x030AD041, &buffer)); -} - -test "parseResourceTemplate extracts IO, IRQ, and fixed memory" { - // ResourceTemplate { IO(minimum 0x60, len 8), IRQ(4), Memory32Fixed(0xFED00000, 0x1000) } - const runtime = [_]u8{ - 0x47, 0x01, 0x60, 0x00, 0x60, 0x00, 0x01, 0x08, // small IO descriptor - 0x22, 0x10, 0x00, // small IRQ descriptor (mask bit 4 -> IRQ 4) - 0x86, 0x09, 0x00, 0x01, 0x00, 0x00, 0xD0, 0xFE, 0x00, 0x10, 0x00, 0x00, // Memory32Fixed - 0x79, 0x00, // EndTag - }; - var device = device_model.Device{}; - parseResourceTemplate(&device, &runtime); - - try std.testing.expectEqual(@as(u8, 3), device.resource_count); - const rs = device.resources[0..device.resource_count]; - try std.testing.expectEqual(device_model.ResourceKind.io_port, rs[0].kind); - try std.testing.expectEqual(@as(u64, 0x60), rs[0].start); - try std.testing.expectEqual(@as(u64, 8), rs[0].len); - try std.testing.expectEqual(device_model.ResourceKind.irq, rs[1].kind); - try std.testing.expectEqual(@as(u64, 4), rs[1].start); - try std.testing.expectEqual(device_model.ResourceKind.memory, rs[2].kind); - try std.testing.expectEqual(@as(u64, 0xFED00000), rs[2].start); - try std.testing.expectEqual(@as(u64, 0x1000), rs[2].len); -}