From 2ab0d129a23f6dc498a93ade12598def665fa630 Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Fri, 10 Jul 2026 21:44:44 +0100 Subject: [PATCH] Decode ACPI _HID names in device discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flat analog of pci-class for acpi_device nodes. ACPI has no class/subclass/prog-IF taxonomy — a device's identity is its _HID string itself (PNP0303 *is* "PS/2 keyboard") — so this is a plain id -> name registry, not a hierarchical decoder. New system/devices/acpi-ids.zig (module `acpi-ids`): the common standard PnP/ACPI hardware IDs; vendor-specific ids (QEMU0002, etc.) have no registry name and print the raw HID. Shared reference data like pci-class. The dump now names each _HID: KBD_ [acpi_device] hid=PNP0303 (PS/2 Keyboard) COM1 [acpi_device] hid=PNP0501 (16550A-compatible Serial Port) RTC_ [acpi_device] hid=PNP0B00 (Real-Time Clock (RTC)) LNKA [acpi_device] hid=PNP0C0F (PCI Interrupt Link Device) FWCF [acpi_device] hid=QEMU0002 (vendor-specific: raw HID) Host test covers known ids and the unknown/empty fallthrough. Suite 41/41 plus host tests. --- build.zig | 7 ++++ system/devices/acpi-ids.zig | 68 +++++++++++++++++++++++++++++++++ system/devices/device-model.zig | 12 ++++-- 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 system/devices/acpi-ids.zig diff --git a/build.zig b/build.zig index 5f34b34..6515810 100644 --- a/build.zig +++ b/build.zig @@ -121,6 +121,11 @@ pub fn build(b: *std.Build) void { const pci_class_module = b.addModule("pci-class", .{ .root_source_file = b.path("system/devices/pci-class.zig"), }); + // ACPI/PnP hardware-ID (_HID) names — the flat analog of pci-class for acpi_device + // nodes. Also shared reference data. + const acpi_ids_module = b.addModule("acpi-ids", .{ + .root_source_file = b.path("system/devices/acpi-ids.zig"), + }); // Kernel tunables (maximum_cpus, stack sizes, tick rate). A dependency-free module of // compile-time constants, imported wherever a knob is read; keeps the trade-offs @@ -159,6 +164,7 @@ pub fn build(b: *std.Build) void { .{ .name = "abi", .module = abi_module }, // acpi.zig works in page_size units .{ .name = "device-abi", .module = device_abi_module }, // device-model's DeviceClass/ResourceKind live here .{ .name = "pci-class", .module = pci_class_module }, // decode PCI class codes in the device dump + .{ .name = "acpi-ids", .module = acpi_ids_module }, // decode ACPI _HID names in the device dump .{ .name = "parameters", .module = parameters_module }, // maximum_cpus (the discovery pool) }, }); @@ -446,6 +452,7 @@ pub fn build(b: *std.Build) void { "system/abi.zig", "system/devices/device-abi.zig", "system/devices/pci-class.zig", // class/subclass/prog-IF name decoding + "system/devices/acpi-ids.zig", // _HID name decoding "library/mmio/mmio.zig", // barriers assemble + registers round-trip }) |root| { const mod_tests = b.addTest(.{ diff --git a/system/devices/acpi-ids.zig b/system/devices/acpi-ids.zig new file mode 100644 index 0000000..7d22965 --- /dev/null +++ b/system/devices/acpi-ids.zig @@ -0,0 +1,68 @@ +//! ACPI / PnP hardware-ID (`_HID`) names: the flat analog of pci-class.zig for +//! `acpi_device` nodes. Unlike PCI, ACPI has no class/subclass/prog-IF taxonomy — a +//! device's identity *is* its `_HID` string (`PNP0303` simply means "PS/2 keyboard"), +//! so this is a plain id -> description registry rather than a hierarchical decoder. +//! The well-known PnP/ACPI IDs; vendor-specific ids (e.g. `QEMU0002`, `INTC1234`) have +//! no standard name and return "". Pure reference data, so it is shared by kernel +//! discovery (the device-tree dump) and any user-space tool. + +const std = @import("std"); + +const Entry = struct { hid: []const u8, name: []const u8 }; + +/// The common standard PnP/ACPI hardware IDs. Prefix ranges hint at the grouping +/// (PNP03xx keyboards, PNP0Fxx pointing devices, PNP0Cxx ACPI power/thermal, +/// PNP0Axx buses), but there is no formal hierarchy — hence a flat table. +const table = [_]Entry{ + .{ .hid = "PNP0000", .name = "Programmable Interrupt Controller (PIC)" }, + .{ .hid = "PNP0100", .name = "System Timer (PIT)" }, + .{ .hid = "PNP0103", .name = "High Precision Event Timer (HPET)" }, + .{ .hid = "PNP0200", .name = "DMA Controller" }, + .{ .hid = "PNP0303", .name = "PS/2 Keyboard" }, + .{ .hid = "PNP0400", .name = "Standard LPT Parallel Port" }, + .{ .hid = "PNP0401", .name = "ECP Parallel Port" }, + .{ .hid = "PNP0501", .name = "16550A-compatible Serial Port" }, + .{ .hid = "PNP0700", .name = "PC Floppy Disk Controller" }, + .{ .hid = "PNP0800", .name = "System Speaker" }, + .{ .hid = "PNP0A03", .name = "PCI Bus" }, + .{ .hid = "PNP0A05", .name = "Generic Container Device" }, + .{ .hid = "PNP0A06", .name = "Generic Container Device" }, + .{ .hid = "PNP0A08", .name = "PCI Express Root Bridge" }, + .{ .hid = "PNP0B00", .name = "Real-Time Clock (RTC)" }, + .{ .hid = "PNP0C01", .name = "System Board" }, + .{ .hid = "PNP0C02", .name = "Motherboard Reserved Resources" }, + .{ .hid = "PNP0C04", .name = "Math Coprocessor" }, + .{ .hid = "PNP0C08", .name = "ACPI System Board" }, + .{ .hid = "PNP0C09", .name = "ACPI Embedded Controller" }, + .{ .hid = "PNP0C0A", .name = "ACPI Control Method Battery" }, + .{ .hid = "PNP0C0B", .name = "ACPI Fan" }, + .{ .hid = "PNP0C0C", .name = "ACPI Power Button" }, + .{ .hid = "PNP0C0D", .name = "ACPI Lid" }, + .{ .hid = "PNP0C0E", .name = "ACPI Sleep Button" }, + .{ .hid = "PNP0C0F", .name = "PCI Interrupt Link Device" }, + .{ .hid = "PNP0F03", .name = "Microsoft PS/2 Mouse" }, + .{ .hid = "PNP0F13", .name = "PS/2 Mouse" }, + .{ .hid = "ACPI0003", .name = "AC Adapter" }, + .{ .hid = "ACPI0007", .name = "Processor Device" }, + .{ .hid = "ACPI000C", .name = "Processor Aggregator" }, + .{ .hid = "ACPI0010", .name = "Processor Container" }, +}; + +/// The human-readable name for a `_HID`, or "" if it is not a known standard id +/// (vendor-specific ids have no registry name — callers just print the raw HID). +pub fn description(hid: []const u8) []const u8 { + for (table) |entry| { + if (std.mem.eql(u8, entry.hid, hid)) return entry.name; + } + return ""; +} + +test "decodes standard PnP/ACPI ids and leaves the rest alone" { + const eq = std.testing.expectEqualStrings; + try eq("PS/2 Keyboard", description("PNP0303")); + try eq("PS/2 Mouse", description("PNP0F13")); + try eq("PCI Express Root Bridge", description("PNP0A08")); + try eq("Real-Time Clock (RTC)", description("PNP0B00")); + try eq("", description("QEMU0002")); // vendor-specific: no standard name + try eq("", description("")); // no HID at all +} diff --git a/system/devices/device-model.zig b/system/devices/device-model.zig index a47697e..e9eb0d6 100644 --- a/system/devices/device-model.zig +++ b/system/devices/device-model.zig @@ -14,6 +14,7 @@ const std = @import("std"); const device_abi = @import("device-abi"); const pci_class = @import("pci-class"); +const acpi_ids = @import("acpi-ids"); /// The hardware primitives a discovery backend needs but can't express portably. /// The kernel injects an implementation (the architecture VMM + port I/O), so the device @@ -190,9 +191,14 @@ fn dumpNode(device: *const Device, depth: usize, emit: *const fn ([]const u8) vo var buffer: [200]u8 = undefined; @memset(buffer[0..indent], ' '); - const body = if (device.hid_len != 0) - std.fmt.bufPrint(buffer[indent..], "{s} [{s}] hid={s}\n", .{ device.name(), @tagName(device.class), device.hid() }) catch return - else + const body = if (device.hid_len != 0) blk: { + // Decode the _HID to a human name when it's a known standard PnP/ACPI id. + const desc = acpi_ids.description(device.hid()); + break :blk if (desc.len != 0) + std.fmt.bufPrint(buffer[indent..], "{s} [{s}] hid={s} ({s})\n", .{ device.name(), @tagName(device.class), device.hid(), desc }) catch return + else + std.fmt.bufPrint(buffer[indent..], "{s} [{s}] hid={s}\n", .{ device.name(), @tagName(device.class), device.hid() }) catch return; + } else std.fmt.bufPrint(buffer[indent..], "{s} [{s}]\n", .{ device.name(), @tagName(device.class) }) catch return; emit(buffer[0 .. indent + body.len]);