Files
danos/system/devices/platform.zig
Daniel Samson a299363b59 The AML interpreter runs in ring 3: the acpi service parses (M20.1)
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.
2026-07-13 03:07:11 +01:00

104 lines
4.3 KiB
Zig

//! The firmware-agnostic discovery facade.
//!
//! The kernel calls `platform.discover()` and gets back a generic `DeviceTree`
//! without ever naming ACPI or device-tree — the same way it imports `architecture`
//! without naming x86_64. Which backend runs is decided *at runtime* from what
//! the bootloader handed us (an ACPI RSDP today, a device-tree blob later),
//! because a single image — a future ARM kernel especially — may boot under
//! either firmware. That's a deliberate divergence from `architecture`, which is a
//! compile-time choice.
const std = @import("std");
const boot_handoff = @import("boot-handoff");
const device_model = @import("device-model.zig");
const acpi = @import("acpi.zig");
const power = @import("power.zig");
const devicetree = @import("device-tree.zig");
pub const DeviceTree = device_model.DeviceTree;
pub const Device = device_model.Device;
pub const DeviceClass = device_model.DeviceClass;
pub const Resource = device_model.Resource;
pub const ResourceKind = device_model.ResourceKind;
pub const Hal = device_model.Hal;
pub const PowerInformation = acpi.PowerInformation;
pub const AmlStats = acpi.AmlStats;
pub const PlatformInformation = acpi.PlatformInformation;
pub const RegisterAccess = acpi.RegisterAccess;
pub const IsoEntry = acpi.IsoEntry;
pub const Cpu = acpi.Cpu;
/// The register map + sleep types discovery extracted, for logging/diagnostics.
pub fn powerInformation() PowerInformation {
return acpi.power_information;
}
/// The scalar firmware facts the architecture layer needs to avoid legacy assumptions
/// (8259 presence, LAPIC base, PM timer, SPCR UART, IRQ overrides).
pub fn platformInformation() PlatformInformation {
return acpi.platform_information;
}
/// AML parse integrity/diagnostics (namespace node count, bytes consumed).
/// The number of Device objects in the kernel's own AML namespace, or 0 if the
/// parse produced none — the `acpi-parse` test compares the ring-3 service's
/// count against this.
pub fn amlDeviceCount() usize {
return acpi.amlDeviceCount();
}
pub fn amlStats() AmlStats {
return acpi.aml_stats;
}
/// The usable logical processors discovered during enumeration — one entry per
/// core danos may schedule on, each carrying the Local APIC ID an SMP wake targets.
/// `len` is the hardware's degree of parallelism: how many tasks *could* run at the
/// same instant once the application processors are started. Today only the
/// bootstrap processor is actually running, so starting the rest is the pending SMP
/// step (see docs/smp.md). Borrowed from static storage populated by `discover`.
pub fn cpus() []const Cpu {
return acpi.cpu_information.cpus[0..acpi.cpu_information.count];
}
/// Non-zero only if enumeration found more processors than the static pool holds
/// (the surplus were dropped from `cpus()`); surfaced so the cap is never silent.
pub fn cpusDropped() usize {
return acpi.cpu_information.dropped;
}
/// Enumerate hardware into a fresh device tree. `hal` supplies the hardware
/// primitives the backend needs (MMIO mapping for PCIe configuration space, port I/O for
/// ACPI registers); pass the architecture implementation. Errors leave nothing to clean up
/// beyond the tree's own allocations.
pub fn discover(
boot_information: *const boot_handoff.BootInformation,
allocator: std.mem.Allocator,
hal: Hal,
) !DeviceTree {
var device_tree = try DeviceTree.init(allocator);
if (boot_information.acpi_rsdp != 0) {
const memory_regions = @as([*]const boot_handoff.MemoryRegion, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.memory_map.regions)))[0..boot_information.memory_map.len];
try acpi.discover(boot_information.acpi_rsdp, memory_regions, &device_tree, hal);
} else {
// No ACPI RSDP. A device-tree boot would parse its blob here; today that
// path is a stub, so this reports the machine described itself no way we
// understand yet.
try devicetree.discover(&device_tree);
}
return device_tree;
}
/// Restart the machine. Never returns on success; returns only if no reset method
/// worked (extremely unlikely). Backend-agnostic entry the kernel calls.
pub fn reboot(hal: Hal) void {
power.reboot(hal);
}
/// Power the machine off (ACPI S5). Never returns on success.
pub fn shutdown(hal: Hal) void {
power.shutdown(hal);
}