refactor kernel to use device platform discovery
This commit is contained in:
@@ -56,6 +56,41 @@ pub const PowerInfo = struct {
|
||||
/// Filled in by `discover`; the power service reads it to reboot/shutdown.
|
||||
pub var power_info: PowerInfo = .{};
|
||||
|
||||
/// A legacy ISA IRQ remapped to a different global system interrupt (GSI), from a
|
||||
/// MADT Interrupt Source Override. `flags` are the MPS INTI polarity/trigger bits.
|
||||
pub const IsoEntry = struct {
|
||||
source: u8,
|
||||
gsi: u32,
|
||||
flags: u16,
|
||||
};
|
||||
|
||||
/// Firmware facts the arch layer needs to avoid legacy assumptions (so danos boots
|
||||
/// on legacy-free UEFI Class 3 machines). MMIO device *addresses* (HPET, IOAPIC)
|
||||
/// come from the device tree instead; this holds the scalar facts that have no
|
||||
/// natural device node.
|
||||
pub const PlatformInfo = struct {
|
||||
/// Whether the legacy 8259 PIC is present (MADT flags bit 0, PCAT_COMPAT). When
|
||||
/// false, the PIC must not be programmed (it may not exist).
|
||||
pic_present: bool = false,
|
||||
/// Local APIC MMIO base (MADT, honouring a type-5 address override).
|
||||
lapic_base: u64 = 0xFEE00000,
|
||||
/// The ACPI power-management timer — a fixed 3.579545 MHz counter usable as a
|
||||
/// calibration reference when no HPET is present.
|
||||
pm_timer: RegAccess = .{},
|
||||
/// true = 32-bit PM timer counter, false = 24-bit (FADT flag TMR_VAL_EXT).
|
||||
pm_timer_32bit: bool = false,
|
||||
/// The console UART the firmware points at (SPCR), if any — MMIO or I/O port.
|
||||
spcr_uart: ?RegAccess = null,
|
||||
/// SPCR interface type (0/1 = 16550/16450, …).
|
||||
spcr_kind: u8 = 0,
|
||||
/// ISA-IRQ-to-GSI remappings from the MADT (for future IOAPIC routing).
|
||||
overrides: [16]IsoEntry = undefined,
|
||||
override_count: usize = 0,
|
||||
};
|
||||
|
||||
/// Filled in by `discover`; the arch layer reads it during bring-up.
|
||||
pub var platform_info: PlatformInfo = .{};
|
||||
|
||||
/// Integrity/diagnostics for the AML parse. `consumed == total` means the parser
|
||||
/// walked every byte of the DSDT/SSDTs without desyncing.
|
||||
pub const AmlStats = struct {
|
||||
@@ -167,6 +202,8 @@ const SLIT: [4]u8 = "SLIT".*;
|
||||
const SRAT: [4]u8 = "SRAT".*;
|
||||
/// Secondary System Description Table (SSDT)
|
||||
const SSDT: [4]u8 = "SSDT".*;
|
||||
/// Serial Port Console Redirection table (SPCR) — the firmware's console UART.
|
||||
const SPCR: [4]u8 = "SPCR".*;
|
||||
/// Extended System Description Table (XSDT; 64-bit version of the RSDT)
|
||||
const XSDT: [4]u8 = "XSDT".*;
|
||||
|
||||
@@ -222,6 +259,22 @@ const MadtIoApic = extern struct {
|
||||
gsi_base: u32 align(1),
|
||||
};
|
||||
|
||||
/// MADT record type 2: an Interrupt Source Override (ISA IRQ -> GSI remap).
|
||||
const MadtIso = extern struct {
|
||||
record: MadtRecordHeader,
|
||||
bus: u8,
|
||||
source: u8,
|
||||
gsi: u32 align(1),
|
||||
flags: u16 align(1),
|
||||
};
|
||||
|
||||
/// MADT record type 5: Local APIC Address Override (64-bit MMIO base).
|
||||
const MadtLapicOverride = extern struct {
|
||||
record: MadtRecordHeader,
|
||||
reserved: u16 align(1),
|
||||
address: u64 align(1),
|
||||
};
|
||||
|
||||
// --- MCFG: PCIe ECAM configuration space (signature "MCFG") -----------------
|
||||
|
||||
const Mcfg = extern struct {
|
||||
@@ -286,6 +339,7 @@ pub fn discover(rsdp_phys: u64, dt: *DeviceTree, hal: Hal) !void {
|
||||
|
||||
// Start clean so a re-run doesn't accumulate stale state.
|
||||
power_info = .{};
|
||||
platform_info = .{};
|
||||
aml_stats = .{};
|
||||
namespace = null;
|
||||
dsdt_phys = 0;
|
||||
@@ -352,6 +406,8 @@ fn handleTable(dt: *DeviceTree, hal: Hal, sdt_phys: u64) !void {
|
||||
try parseHpet(dt, header);
|
||||
} else if (std.mem.eql(u8, &sig, &FACP)) {
|
||||
parseFadt(header);
|
||||
} else if (std.mem.eql(u8, &sig, &SPCR)) {
|
||||
parseSpcr(header);
|
||||
} else if (std.mem.eql(u8, &sig, &SSDT)) {
|
||||
// Secondary namespace bytecode — collect for the sleep-state (`_Sx`) scan.
|
||||
addAmlBlock(sdt_phys);
|
||||
@@ -361,10 +417,15 @@ fn handleTable(dt: *DeviceTree, hal: Hal, sdt_phys: u64) !void {
|
||||
|
||||
/// MADT -> one processor node per Local APIC, one interrupt_controller per I/O APIC.
|
||||
fn parseMadt(dt: *DeviceTree, header: *const SystemDescriptorTableHeader) !void {
|
||||
const madt: *const Madt = @ptrCast(header);
|
||||
const total: usize = header.length;
|
||||
const base: [*]const u8 = @ptrCast(header);
|
||||
var ioapic_index: usize = 0;
|
||||
|
||||
// MADT header: local APIC base + flags (bit 0 = 8259 PIC present).
|
||||
platform_info.lapic_base = madt.local_apic_address;
|
||||
platform_info.pic_present = madt.flags & 1 != 0;
|
||||
|
||||
var off: usize = @sizeOf(Madt);
|
||||
while (off + @sizeOf(MadtRecordHeader) <= total) {
|
||||
const rec: *const MadtRecordHeader = @ptrCast(base + off);
|
||||
@@ -389,6 +450,21 @@ fn parseMadt(dt: *DeviceTree, header: *const SystemDescriptorTableHeader) !void
|
||||
// The GSI range this I/O APIC handles, starting at gsi_base.
|
||||
_ = d.addResource(.irq, io.gsi_base, 0);
|
||||
},
|
||||
2 => {
|
||||
const iso: *const MadtIso = @ptrCast(base + off);
|
||||
if (platform_info.override_count < platform_info.overrides.len) {
|
||||
platform_info.overrides[platform_info.override_count] = .{
|
||||
.source = iso.source,
|
||||
.gsi = iso.gsi,
|
||||
.flags = iso.flags,
|
||||
};
|
||||
platform_info.override_count += 1;
|
||||
}
|
||||
},
|
||||
5 => {
|
||||
const ovr: *const MadtLapicOverride = @ptrCast(base + off);
|
||||
platform_info.lapic_base = ovr.address;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
off += rec.length;
|
||||
@@ -523,6 +599,7 @@ const fadt_acpi_enable = 52; // u8
|
||||
const fadt_acpi_disable = 53; // u8
|
||||
const fadt_pm1a_cnt_blk = 64; // u32 (I/O port)
|
||||
const fadt_pm1b_cnt_blk = 68; // u32 (I/O port)
|
||||
const fadt_pm_tmr_blk = 76; // u32 (I/O port) — the PM timer counter
|
||||
const fadt_pm1_cnt_len = 89; // u8 (bytes)
|
||||
const fadt_flags = 112; // u32
|
||||
const fadt_reset_reg = 116; // GAS (12 bytes)
|
||||
@@ -530,7 +607,9 @@ const fadt_reset_value = 128; // u8
|
||||
const fadt_x_dsdt = 140; // u64
|
||||
const fadt_x_pm1a_cnt_blk = 172; // GAS
|
||||
const fadt_x_pm1b_cnt_blk = 184; // GAS
|
||||
const fadt_x_pm_tmr_blk = 208; // GAS
|
||||
const flag_reset_reg_supported = 1 << 10;
|
||||
const flag_tmr_val_ext = 1 << 8; // PM timer counter is 32-bit (else 24-bit)
|
||||
|
||||
/// FADT -> the power register map (into `power_info`) and the DSDT address, which
|
||||
/// is queued for the AML sleep-state (`_Sx`) scan. No AML interpretation happens here.
|
||||
@@ -552,6 +631,11 @@ fn parseFadt(header: *const SystemDescriptorTableHeader) void {
|
||||
pi.reset = readGas(base, len, fadt_reset_reg) orelse .{};
|
||||
pi.reset_value = fadt(u8, base, len, fadt_reset_value) orelse 0;
|
||||
|
||||
// The PM timer — a fixed-rate counter used as a calibration reference when no
|
||||
// HPET is present. Prefer the 64-bit-capable X_ GAS, fall back to the port.
|
||||
platform_info.pm_timer = readCntReg(base, len, fadt_x_pm_tmr_blk, fadt_pm_tmr_blk, 4);
|
||||
platform_info.pm_timer_32bit = flags & flag_tmr_val_ext != 0;
|
||||
|
||||
var dsdt: u64 = fadt(u32, base, len, fadt_dsdt) orelse 0;
|
||||
if (fadt(u64, base, len, fadt_x_dsdt)) |x| {
|
||||
if (x != 0) dsdt = x;
|
||||
@@ -560,6 +644,21 @@ fn parseFadt(header: *const SystemDescriptorTableHeader) void {
|
||||
addAmlBlock(dsdt);
|
||||
}
|
||||
|
||||
// SPCR field offsets (bytes from the table start).
|
||||
const spcr_interface_type = 36; // u8
|
||||
const spcr_base_address = 40; // GAS (12 bytes)
|
||||
|
||||
/// SPCR -> the console UART's address + interface type, so serial can target the
|
||||
/// firmware's actual debug port instead of assuming legacy COM1.
|
||||
fn parseSpcr(header: *const SystemDescriptorTableHeader) void {
|
||||
const base: [*]align(1) const u8 = @ptrCast(header);
|
||||
const len: usize = header.length;
|
||||
const gas = readGas(base, len, spcr_base_address) orelse return;
|
||||
if (gas.address == 0) return;
|
||||
platform_info.spcr_uart = gas;
|
||||
platform_info.spcr_kind = fadt(u8, base, len, spcr_interface_type) orelse 0;
|
||||
}
|
||||
|
||||
// --- AML namespace -> generic device tree -----------------------------------
|
||||
|
||||
/// The PCI bus context while descending the ACPI namespace: the generic host
|
||||
|
||||
@@ -131,6 +131,14 @@ pub const Device = struct {
|
||||
self.resource_count += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// The device's first resource of `kind`, or null — e.g. a timer's MMIO base.
|
||||
pub fn firstResource(self: *const Device, kind: ResourceKind) ?Resource {
|
||||
for (self.resources[0..self.resource_count]) |r| {
|
||||
if (r.kind == kind) return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/// Owns the discovered device tree and the allocator its nodes came from.
|
||||
@@ -146,6 +154,12 @@ pub const DeviceTree = struct {
|
||||
return .{ .allocator = allocator, .root = root };
|
||||
}
|
||||
|
||||
/// The first device of `class` anywhere in the tree (depth-first), or null —
|
||||
/// how the kernel pulls e.g. the HPET or IOAPIC MMIO base out of discovery.
|
||||
pub fn firstOfClass(self: *const DeviceTree, class: DeviceClass) ?*Device {
|
||||
return firstOfClassIn(self.root, class);
|
||||
}
|
||||
|
||||
/// Allocate a device and append it under `parent`, returning it so the caller
|
||||
/// can attach resources/ids. Appended at the tail so a dump reads in the order
|
||||
/// devices were discovered.
|
||||
@@ -176,6 +190,15 @@ pub const DeviceTree = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn firstOfClassIn(node: *Device, class: DeviceClass) ?*Device {
|
||||
var child = node.first_child;
|
||||
while (child) |c| : (child = c.next_sibling) {
|
||||
if (c.class == class) return c;
|
||||
if (firstOfClassIn(c, class)) |found| return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn dumpNode(dev: *const Device, depth: usize, emit: *const fn ([]const u8) void) void {
|
||||
const indent = @min(depth * 2, 40);
|
||||
|
||||
|
||||
@@ -21,12 +21,21 @@ pub const DeviceClass = device.DeviceClass;
|
||||
pub const Hal = device.Hal;
|
||||
pub const PowerInfo = acpi.PowerInfo;
|
||||
pub const AmlStats = acpi.AmlStats;
|
||||
pub const PlatformInfo = acpi.PlatformInfo;
|
||||
pub const RegAccess = acpi.RegAccess;
|
||||
pub const IsoEntry = acpi.IsoEntry;
|
||||
|
||||
/// The register map + sleep types discovery extracted, for logging/diagnostics.
|
||||
pub fn powerInfo() PowerInfo {
|
||||
return acpi.power_info;
|
||||
}
|
||||
|
||||
/// The scalar firmware facts the arch layer needs to avoid legacy assumptions
|
||||
/// (8259 presence, LAPIC base, PM timer, SPCR UART, IRQ overrides).
|
||||
pub fn platformInfo() PlatformInfo {
|
||||
return acpi.platform_info;
|
||||
}
|
||||
|
||||
/// AML parse integrity/diagnostics (namespace node count, bytes consumed).
|
||||
pub fn amlStats() AmlStats {
|
||||
return acpi.aml_stats;
|
||||
|
||||
Reference in New Issue
Block a user