diff --git a/build.zig b/build.zig index 3592363..17c05ba 100644 --- a/build.zig +++ b/build.zig @@ -61,6 +61,7 @@ fn addUserBinary( posix_module: *std.Build.Module, mmio_module: *std.Build.Module, xkeyboard_config_module: *std.Build.Module, + acpi_ids_module: *std.Build.Module, name: []const u8, root: []const u8, ) *std.Build.Step.Compile { @@ -85,6 +86,9 @@ fn addUserBinary( // Keyboard layouts (keycode + modifiers -> keysym/character), available // to any program that wants it. See library/xkeyboard-config/. .{ .name = "xkeyboard-config", .module = xkeyboard_config_module }, + // ACPI/PnP hardware-ID registry, so drivers name devices + // (HardwareId.ps2_keyboard) instead of magic "_HID" strings. + .{ .name = "acpi-ids", .module = acpi_ids_module }, }, }), }); @@ -312,7 +316,7 @@ pub fn build(b: *std.Build) void { // Built by the shared user-binary recipe (see addUserBinary): freestanding, // linked into the kernel's user region against the `runtime` runtime library, and // started in ring 3 by the kernel's user-ELF loader. - const init_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "init", "system/services/init/init.zig"); + const init_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "init", "system/services/init/init.zig"); const init_install = b.addInstallArtifact(init_exe, .{ .dest_dir = .{ .override = .{ .custom = "system/services" } } }); b.getInstallStep().dependOn(&init_install.step); @@ -320,21 +324,21 @@ pub fn build(b: *std.Build) void { // Each is built by the same user-binary recipe, then packed into one image by // the host-side make-initial-ramdisk tool. The bootloader ferries the image to the kernel, // which unpacks it and spawns each program (system/initial-ramdisk.zig). - const vfs_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "vfs", "system/services/vfs/vfs.zig"); - const vfstest_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "vfs-test", "system/services/vfs/vfs-test.zig"); - const hpet_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "hpet", "system/drivers/hpet/hpet.zig"); - const bus_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "bus", "system/drivers/bus/bus.zig"); - const ps2_bus_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "bus", "system/drivers/ps2-bus/ps2-bus.zig"); - const ps2_keyboard_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "ps2-keyboard", "system/drivers/ps2-bus/keyboard.zig"); - const ps2_mouse_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "ps2-mouse", "system/drivers/ps2-bus/mouse.zig"); - const device_manager_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "device-manager", "system/services/device-manager/device-manager.zig"); + const vfs_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "vfs", "system/services/vfs/vfs.zig"); + const vfstest_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "vfs-test", "system/services/vfs/vfs-test.zig"); + const hpet_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "hpet", "system/drivers/hpet/hpet.zig"); + const bus_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "bus", "system/drivers/bus/bus.zig"); + const ps2_bus_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "bus", "system/drivers/ps2-bus/ps2-bus.zig"); + const ps2_keyboard_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-keyboard", "system/drivers/ps2-bus/keyboard.zig"); + const ps2_mouse_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-mouse", "system/drivers/ps2-bus/mouse.zig"); + const device_manager_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "device-manager", "system/services/device-manager/device-manager.zig"); // The input service and its exercisers: the fan-out server, a hardware-free synthetic // source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md. - const input_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "input", "system/services/input/input.zig"); - const input_source_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "input-source", "system/services/input-source/input-source.zig"); - const input_test_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "input-test", "system/services/input-test/input-test.zig"); - const args_echo_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "args-echo", "system/services/args-echo/args-echo.zig"); - const process_test_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, "process-test", "system/services/process-test/process-test.zig"); + const input_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input", "system/services/input/input.zig"); + const input_source_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input-source", "system/services/input-source/input-source.zig"); + const input_test_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input-test", "system/services/input-test/input-test.zig"); + const args_echo_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "args-echo", "system/services/args-echo/args-echo.zig"); + const process_test_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "process-test", "system/services/process-test/process-test.zig"); // Pack the user binaries into the initial_ramdisk image with the host-side Python tool // (the container format is trivial, and Python sidesteps std API churn). Args: diff --git a/system/devices/acpi-ids.zig b/system/devices/acpi-ids.zig index 7d22965..8fd0118 100644 --- a/system/devices/acpi-ids.zig +++ b/system/devices/acpi-ids.zig @@ -1,60 +1,120 @@ //! 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. +//! so this is a plain id <-> name 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. +//! no standard name and decode to nothing. Pure reference data, so it is shared by +//! kernel discovery (the device-tree dump) and any user-space driver or tool. +//! +//! Code that means a specific device names the `HardwareId` variant instead of its +//! `_HID` string — `HardwareId.ps2_keyboard.hid()` reads without a registry lookup, +//! where a bare `"PNP0303"` does not. const std = @import("std"); -const Entry = struct { hid: []const u8, name: []const u8 }; +/// The common standard PnP/ACPI hardware IDs, as named values. 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 +/// enum over a flat registry. +pub const HardwareId = enum { + programmable_interrupt_controller, + system_timer, + high_precision_event_timer, + dma_controller, + ps2_keyboard, + parallel_port, + ecp_parallel_port, + serial_port, + floppy_disk_controller, + system_speaker, + pci_bus, + generic_container, + /// The second id the ACPI spec assigns the same "Generic Container Device" name. + generic_container_extended, + pci_express_root_bridge, + real_time_clock, + system_board, + motherboard_reserved_resources, + math_coprocessor, + acpi_system_board, + embedded_controller, + control_method_battery, + fan, + power_button, + lid, + sleep_button, + pci_interrupt_link, + microsoft_ps2_mouse, + ps2_mouse, + ac_adapter, + processor_device, + processor_aggregator, + processor_container, -/// 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" }, + const Entry = struct { hid: []const u8, name: []const u8 }; + + /// The registry row for this id: its `_HID` string and human-readable name. + fn entry(self: HardwareId) Entry { + return switch (self) { + .programmable_interrupt_controller => .{ .hid = "PNP0000", .name = "Programmable Interrupt Controller (PIC)" }, + .system_timer => .{ .hid = "PNP0100", .name = "System Timer (PIT)" }, + .high_precision_event_timer => .{ .hid = "PNP0103", .name = "High Precision Event Timer (HPET)" }, + .dma_controller => .{ .hid = "PNP0200", .name = "DMA Controller" }, + .ps2_keyboard => .{ .hid = "PNP0303", .name = "PS/2 Keyboard" }, + .parallel_port => .{ .hid = "PNP0400", .name = "Standard LPT Parallel Port" }, + .ecp_parallel_port => .{ .hid = "PNP0401", .name = "ECP Parallel Port" }, + .serial_port => .{ .hid = "PNP0501", .name = "16550A-compatible Serial Port" }, + .floppy_disk_controller => .{ .hid = "PNP0700", .name = "PC Floppy Disk Controller" }, + .system_speaker => .{ .hid = "PNP0800", .name = "System Speaker" }, + .pci_bus => .{ .hid = "PNP0A03", .name = "PCI Bus" }, + .generic_container => .{ .hid = "PNP0A05", .name = "Generic Container Device" }, + .generic_container_extended => .{ .hid = "PNP0A06", .name = "Generic Container Device" }, + .pci_express_root_bridge => .{ .hid = "PNP0A08", .name = "PCI Express Root Bridge" }, + .real_time_clock => .{ .hid = "PNP0B00", .name = "Real-Time Clock (RTC)" }, + .system_board => .{ .hid = "PNP0C01", .name = "System Board" }, + .motherboard_reserved_resources => .{ .hid = "PNP0C02", .name = "Motherboard Reserved Resources" }, + .math_coprocessor => .{ .hid = "PNP0C04", .name = "Math Coprocessor" }, + .acpi_system_board => .{ .hid = "PNP0C08", .name = "ACPI System Board" }, + .embedded_controller => .{ .hid = "PNP0C09", .name = "ACPI Embedded Controller" }, + .control_method_battery => .{ .hid = "PNP0C0A", .name = "ACPI Control Method Battery" }, + .fan => .{ .hid = "PNP0C0B", .name = "ACPI Fan" }, + .power_button => .{ .hid = "PNP0C0C", .name = "ACPI Power Button" }, + .lid => .{ .hid = "PNP0C0D", .name = "ACPI Lid" }, + .sleep_button => .{ .hid = "PNP0C0E", .name = "ACPI Sleep Button" }, + .pci_interrupt_link => .{ .hid = "PNP0C0F", .name = "PCI Interrupt Link Device" }, + .microsoft_ps2_mouse => .{ .hid = "PNP0F03", .name = "Microsoft PS/2 Mouse" }, + .ps2_mouse => .{ .hid = "PNP0F13", .name = "PS/2 Mouse" }, + .ac_adapter => .{ .hid = "ACPI0003", .name = "AC Adapter" }, + .processor_device => .{ .hid = "ACPI0007", .name = "Processor Device" }, + .processor_aggregator => .{ .hid = "ACPI000C", .name = "Processor Aggregator" }, + .processor_container => .{ .hid = "ACPI0010", .name = "Processor Container" }, + }; + } + + /// This id's `_HID` string (e.g. `.ps2_keyboard` -> "PNP0303"). + pub fn hid(self: HardwareId) []const u8 { + return self.entry().hid; + } + + /// This id's human-readable name (e.g. `.ps2_keyboard` -> "PS/2 Keyboard"). + pub fn description(self: HardwareId) []const u8 { + return self.entry().name; + } + + /// The named value for a `_HID` string, or null if it is not a known standard + /// id (vendor-specific ids are not in the registry). + pub fn fromHid(hid_string: []const u8) ?HardwareId { + for (std.enums.values(HardwareId)) |id| { + if (std.mem.eql(u8, id.hid(), hid_string)) return id; + } + return null; + } }; -/// 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). +/// The human-readable name for a `_HID` string, 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 ""; + return (HardwareId.fromHid(hid) orelse return "").description(); } test "decodes standard PnP/ACPI ids and leaves the rest alone" { @@ -66,3 +126,13 @@ test "decodes standard PnP/ACPI ids and leaves the rest alone" { try eq("", description("QEMU0002")); // vendor-specific: no standard name try eq("", description("")); // no HID at all } + +test "named values round-trip through their _HID strings" { + const testing = std.testing; + try testing.expectEqualStrings("PNP0303", HardwareId.ps2_keyboard.hid()); + try testing.expectEqual(@as(?HardwareId, .ps2_mouse), HardwareId.fromHid("PNP0F13")); + try testing.expectEqual(@as(?HardwareId, null), HardwareId.fromHid("QEMU0002")); + for (std.enums.values(HardwareId)) |id| { + try testing.expectEqual(@as(?HardwareId, id), HardwareId.fromHid(id.hid())); + } +} diff --git a/system/devices/acpi.zig b/system/devices/acpi.zig index 7b78146..2f13a49 100644 --- a/system/devices/acpi.zig +++ b/system/devices/acpi.zig @@ -17,6 +17,7 @@ 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"); @@ -914,7 +915,14 @@ fn readAdr(node: *aml.Node) ?u32 { return @truncate(readIntObj(n.value, &p) orelse return null); } -/// Whether a namespace device is a PCI(e) host bridge (`PNP0A03` / `PNP0A08`). +/// 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; @@ -922,12 +930,10 @@ fn isPciRootNode(node: *aml.Node) bool { 0x00, 0x01, 0xFF, 0x0A, 0x0B, 0x0C, 0x0E => { var p: usize = 0; const n = readIntObj(hid.value, &p) orelse return false; - return n == 0x030AD041 or n == 0x080AD041; // PNP0A03 / PNP0A08 - }, - 0x0D => { - const s = cstr(hid.value[1..]); - return std.mem.eql(u8, s, "PNP0A03") or std.mem.eql(u8, s, "PNP0A08"); + var buffer: [8]u8 = undefined; + return isPciRootHid(eisaIdToStr(@truncate(n), &buffer)); }, + 0x0D => return isPciRootHid(cstr(hid.value[1..])), else => return false, } } diff --git a/system/drivers/ps2-bus/ps2-bus.zig b/system/drivers/ps2-bus/ps2-bus.zig index d211de9..dbe1c98 100644 --- a/system/drivers/ps2-bus/ps2-bus.zig +++ b/system/drivers/ps2-bus/ps2-bus.zig @@ -11,6 +11,7 @@ //! - irq 0xc len 0x1 const std = @import("std"); const runtime = @import("runtime"); +const acpi_ids = @import("acpi-ids"); const ps2 = @import("ps2-library.zig"); const device = runtime.device; const ipc = runtime.ipc; @@ -100,7 +101,7 @@ pub fn main() void { // The 8042's IO ports (0x60/0x64) are enumerated under the keyboard ACPI node // (PNP0303), so we init the controller from that descriptor — but which device // is on which port is decided later by identify, not by this HID. - const maybe_controller_device_descriptor = device.findDeviceDescriptorByHid(buffer, "PNP0303"); + const maybe_controller_device_descriptor = device.findDeviceDescriptorByHid(buffer, acpi_ids.HardwareId.ps2_keyboard.hid()); if (maybe_controller_device_descriptor) |controller_device_descriptor| { _ = runtime.system.write("system/drivers/ps2-bus: found PS/2 controller\n"); _ = runtime.system.write("system/drivers/ps2-bus: initializing controller\n"); @@ -243,7 +244,7 @@ pub fn main() void { // to the *port*, whatever device identify found on it. var maybe_auxiliary_interrupt: ?struct { device_id: u64, interrupt_index: u64, gsi: u64 } = null; if (port_device_types[@intFromEnum(ps2.Port.Two)] != null) { - if (device.findDeviceDescriptorByHid(buffer, "PNP0F13")) |descriptor| { + if (device.findDeviceDescriptorByHid(buffer, acpi_ids.HardwareId.ps2_mouse.hid())) |descriptor| { if (findInterruptResourceIndex(descriptor)) |auxiliary_index| { if (device.claim(descriptor.id) and device.irqBind(descriptor.id, auxiliary_index, endpoint)) { maybe_auxiliary_interrupt = .{ diff --git a/system/drivers/ps2-bus/ps2-library.zig b/system/drivers/ps2-bus/ps2-library.zig index 932e826..d5cd630 100644 --- a/system/drivers/ps2-bus/ps2-library.zig +++ b/system/drivers/ps2-bus/ps2-library.zig @@ -1,6 +1,7 @@ //! shared definitions between the different PS/2 drivers const std = @import("std"); const runtime = @import("runtime"); +const acpi_ids = @import("acpi-ids"); const device = runtime.device; const system = runtime.system; @@ -261,8 +262,8 @@ pub const DeviceType = enum(u32) { /// its command-line argument, or null if we could not classify it. pub fn hid(self: DeviceType) ?[]const u8 { return switch (self) { - .keyboard => "PNP0303", - .mouse => "PNP0F13", + .keyboard => acpi_ids.HardwareId.ps2_keyboard.hid(), + .mouse => acpi_ids.HardwareId.ps2_mouse.hid(), .unknown => null, }; } diff --git a/system/services/device-manager/device-manager.zig b/system/services/device-manager/device-manager.zig index 938ed4c..3f92e13 100644 --- a/system/services/device-manager/device-manager.zig +++ b/system/services/device-manager/device-manager.zig @@ -14,6 +14,7 @@ //! that redundancy so the manager is the sole owner of driver spawning.) const std = @import("std"); const runtime = @import("runtime"); +const acpi_ids = @import("acpi-ids"); const device = runtime.device; const system = runtime.system; @@ -34,9 +35,11 @@ fn driverFor(d: device.DeviceDescriptor) ?[]const u8 { if (d.class == @intFromEnum(device.DeviceClass.timer)) return "hpet"; // detect device via hid const hid = d.hid[0..@intCast(d.hid_len)]; - if (std.mem.eql(u8, hid, "PNP0303") or std.mem.eql(u8, hid, "PNP0F13")) return "ps2-bus"; - - return null; + const id = acpi_ids.HardwareId.fromHid(hid) orelse return null; + return switch (id) { + .ps2_keyboard, .ps2_mouse => "ps2-bus", + else => null, + }; }