147 lines
7.5 KiB
Zig
147 lines
7.5 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/kernel/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/discovery.md): 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,
|
|
/// One interface of a USB device, registered by the xHCI bus driver. It owns
|
|
/// no MMIO — it is reached through its controller — so it carries no
|
|
/// resources; the (class, subclass, protocol) triple that says what it is
|
|
/// travels in the bus report's identity, not here.
|
|
usb_device,
|
|
/// A scanout framebuffer: a linear region of pixel memory the display service
|
|
/// claims and maps. Unlike the other classes this one is not firmware-discovered
|
|
/// — the kernel seeds it from the loader's [[boot-handoff]] framebuffer
|
|
/// (`devices_broker.seedDisplay`). Its one `memory` resource is the framebuffer,
|
|
/// flagged write-combining; the geometry to interpret it travels in
|
|
/// `DeviceDescriptor.display`.
|
|
display,
|
|
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,
|
|
/// A bitmask of `resource_flag_*` hints. Zero for a plain register/RAM window;
|
|
/// the kernel reads it when it maps the resource. Defaulted so every existing
|
|
/// literal (which never set flags) keeps compiling and lays out identically.
|
|
flags: u64 = 0,
|
|
};
|
|
|
|
/// `ResourceDescriptor.flags`: map this `memory` resource **write-combining** rather
|
|
/// than strong-uncacheable — for a framebuffer, where batched bursts to pixel memory
|
|
/// are the whole point (an uncacheable framebuffer blit is glacial). See
|
|
/// `mmio_map` (system/kernel/process.zig) and `setupPat` (…/x86_64/paging.zig).
|
|
pub const resource_flag_write_combining: u64 = 1 << 0;
|
|
|
|
pub const maximum_device_resources = 8;
|
|
|
|
/// The byte order of a display's pixels — mirrors the loader's `PixelFormat`
|
|
/// ([[boot-handoff]]) with the same numeric values, but lives here so user space
|
|
/// (which must never import the loader↔kernel handoff) can name it. Only the two
|
|
/// linear 32-bpp layouts a console can paint into exist; see docs/gop.md.
|
|
pub const DisplayFormat = enum(u32) {
|
|
rgbx = 0, // byte 0 = Red, 1 = Green, 2 = Blue, 3 = reserved
|
|
bgrx = 1, // byte 0 = Blue, 1 = Green, 2 = Red, 3 = reserved
|
|
};
|
|
|
|
/// The geometry of a `display` device's framebuffer, carried in its descriptor so a
|
|
/// claiming driver knows how to interpret the pixel bytes its `memory` resource maps.
|
|
/// `pitch` is bytes per row (may exceed `width * 4`; see docs/framebuffer.md).
|
|
pub const DisplayInfo = extern struct {
|
|
width: u32 = 0, // visible pixels per row
|
|
height: u32 = 0, // visible rows
|
|
pitch: u32 = 0, // bytes from one row's start to the next
|
|
format: u32 = 0, // a DisplayFormat value
|
|
refresh_hz: u32 = 0, // panel refresh rate from EDID (0 = unknown); see boot-handoff
|
|
};
|
|
|
|
/// `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,
|
|
// Numeric identity beyond the class triple, mirrored in the bus report's
|
|
// ChildAdded so /system/configuration/devices.csv can bind on it: `vendor`/`device` are the PCI
|
|
// vendor/device (or USB idVendor/idProduct), `subsystem` is the PCI subsystem id
|
|
// packed `(subsystem_vendor << 16) | subsystem_device`. Zero where the bus has no
|
|
// such concept. Defaulted so existing descriptor literals keep compiling and lay
|
|
// out identically until they choose to set them.
|
|
vendor: u16 = 0,
|
|
device: u16 = 0,
|
|
subsystem: u32 = 0,
|
|
hid_len: u64,
|
|
resource_count: u64,
|
|
hid: [8]u8,
|
|
resources: [maximum_device_resources]ResourceDescriptor,
|
|
// Framebuffer geometry, meaningful only when `class` is `DeviceClass.display`
|
|
// (zeroed otherwise). Kept here — a class-specific field on the shared descriptor —
|
|
// the same way `pci_class` is meaningful only for `pci_device` and `hid` only for
|
|
// `acpi_device`.
|
|
display: DisplayInfo = .{},
|
|
};
|