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.
91 lines
4.2 KiB
Zig
91 lines
4.2 KiB
Zig
//! The **device ABI**: the flat, `extern` device types that cross the system_call
|
|
//! boundary — what `device_enumerate` hands a user-space driver, what
|
|
//! `device_register` takes back. This is the devices sub-project's *public
|
|
//! interface*, exposed as its own `device-abi` module the same way the VFS server
|
|
//! exposes `vfs-protocol` — so both the kernel and user space depend on the contract
|
|
//! by name, and neither reaches into the other's files.
|
|
//!
|
|
//! It is also the **single source of truth** for `DeviceClass` and `ResourceKind`:
|
|
//! the kernel's rich, pointer-based device tree (system/devices/device-model.zig,
|
|
//! which user space must never import) re-exports these, so the enum that a driver
|
|
//! matches on and the enum the kernel classifies with are the *same* type — no
|
|
//! hand-kept "mirror in order" to drift. The core kernel↔user ABI is [[abi]]; the
|
|
//! loader↔kernel handoff is [[boot-handoff]].
|
|
|
|
/// A coarse classification of a device, independent of the describing firmware.
|
|
/// Kept small on purpose; refine as real drivers arrive. `enum(u32)` because the
|
|
/// `@intFromEnum` value crosses the system_call boundary in `DeviceDescriptor.class`.
|
|
pub const DeviceClass = enum(u32) {
|
|
/// The synthetic root every discovered device hangs beneath.
|
|
root,
|
|
processor,
|
|
interrupt_controller,
|
|
timer,
|
|
/// A PCI(e) host bridge — the root of a PCI segment (owns an ECAM window).
|
|
pci_host_bridge,
|
|
/// A single PCI function.
|
|
pci_device,
|
|
/// A device named in the ACPI namespace (from the DSDT/SSDT), carrying a
|
|
/// hardware ID (`_HID`) and, where static, current resource settings (`_CRS`).
|
|
acpi_device,
|
|
/// The ACPI tables themselves, published as one node for the user-space acpi
|
|
/// service (docs/m19-m20-plan.md M20): memory resources over the AML blobs,
|
|
/// a broad io_port grant for OperationRegion access, and the SCI interrupt.
|
|
/// The one node whose claimant is trusted to run firmware bytecode.
|
|
acpi_tables,
|
|
unknown,
|
|
};
|
|
|
|
/// The kind of hardware resource a device occupies. `enum(u32)` for the same
|
|
/// boundary-crossing reason as `DeviceClass` (see `ResourceDescriptor.kind`).
|
|
pub const ResourceKind = enum(u32) {
|
|
/// A memory-mapped I/O window: `start` is the physical base, `len` its size.
|
|
memory,
|
|
/// A legacy I/O-port range: `start` is the first port, `len` the count.
|
|
io_port,
|
|
/// An interrupt: `start` is the global system interrupt (GSI), `len` is 1.
|
|
irq,
|
|
/// A range of bus numbers owned by a bridge: `start`..`start+len`.
|
|
bus_range,
|
|
};
|
|
|
|
/// One device resource, as handed to a user-space driver (flat, extern).
|
|
pub const ResourceDescriptor = extern struct {
|
|
kind: u64, // a ResourceKind value
|
|
start: u64,
|
|
len: u64,
|
|
};
|
|
|
|
pub const maximum_device_resources = 8;
|
|
|
|
/// `DeviceDescriptor.parent` for a device with no parent — a root of the device tree.
|
|
pub const no_parent: u64 = ~@as(u64, 0);
|
|
|
|
/// `DeviceDescriptor.pci_class` for a device that is not a PCI function. (Zero would be
|
|
/// ambiguous: 0x000000 is a real class code, "unclassified device".)
|
|
pub const no_pci_class: u64 = ~@as(u64, 0);
|
|
|
|
/// A device, as snapshotted for user space by `device_enumerate`. A driver scans
|
|
/// these to find the hardware it owns, claims it, and maps its MMIO.
|
|
///
|
|
/// `parent` makes the table a tree rather than a list, which is what a **bus driver**
|
|
/// needs: it claims the bus, finds the devices below it, and publishes any it
|
|
/// discovers itself with `device_register`. A registered child's resources must lie
|
|
/// within its parent's (the kernel enforces this) — that containment is what makes
|
|
/// delegation safe, since a device descriptor is otherwise a licence to map physical
|
|
/// memory.
|
|
pub const DeviceDescriptor = extern struct {
|
|
id: u64,
|
|
parent: u64, // a device id, or `no_parent`
|
|
class: u64, // a DeviceClass value
|
|
// The PCI class/subclass/prog-IF triple packed as 0xCCSSPP when this device is a PCI
|
|
// function, or `no_pci_class` otherwise. This is how a manager tells *what* a
|
|
// `pci_device` is (an xHCI controller, an AHCI controller) — decode the triple into
|
|
// names with the pci-class module.
|
|
pci_class: u64,
|
|
hid_len: u64,
|
|
resource_count: u64,
|
|
hid: [8]u8,
|
|
resources: [maximum_device_resources]ResourceDescriptor,
|
|
};
|