acpi: stop parsing AML in the kernel; power management is userspace's
Kernel discovery interpreted the whole DSDT/SSDTs (~0.5 MB -> ~9700 nodes) solely to extract the _S5 sleep type for a kernel-side soft-off — ~1-2s of work on the single-core, pre-scheduler critical path, with nothing to overlap it. The ring-3 acpi service already parses the same blobs and owns S5 end-to-end (reads pm1a_cnt from the FADT, writes SLP_TYP itself; orderly-shutdown tests it). So drop the kernel parse entirely. - all *static*-table parsing stays (MADT/HPET/FADT/MCFG): CPUs, timers, PCIe, and the power register map are still detected from ACPI, not legacy/compat addresses (timer still calibrates via HPET/PM-timer/CPUID, PIT only as last resort) - kernel keeps reboot (FADT reset register + legacy fallbacks — no AML); soft-off is userspace-only now - removed PowerInformation.s5/s3, the kernel AML namespace, aml_stats, amlDeviceCount, and the aml import; power.shutdown/enable/sleepS3 gone - tests: poweroff case retired (S5 covered by orderly-shutdown); discovery drops its S5/AML checks; acpi-parse self-verifies against a device-count floor since there's no kernel count to match
This commit is contained in:
@@ -199,21 +199,13 @@ fn kmain(boot_information: *const BootInformation) noreturn {
|
||||
// land on. Every line stays masked until something binds it (ioapic.init).
|
||||
irq.init();
|
||||
|
||||
// Power register map extracted from the FADT + AML, for confidence it parsed.
|
||||
// Power register map, from the FADT (the SLP_TYP sleep values live in AML,
|
||||
// which the kernel doesn't parse — the ring-3 acpi service owns soft-off).
|
||||
const pw = platform.powerInformation();
|
||||
log.write("/system/kernel: power\n");
|
||||
log.print(" pm1a_cnt : {s} 0x{x} (width {d})\n", .{ if (pw.pm1a_cnt.mmio) "mmio" else "io", pw.pm1a_cnt.address, pw.pm1a_cnt.width });
|
||||
if (pw.s5) |s| {
|
||||
log.print(" S5 slp_typ : a={d} b={d}\n", .{ s.slp_typ_a, s.slp_typ_b });
|
||||
} else {
|
||||
log.write(" S5 slp_typ : (not found)\n");
|
||||
}
|
||||
log.print(" reset : supported={} {s} 0x{x} val 0x{x}\n", .{ pw.reset_supported, if (pw.reset.mmio) "mmio" else "io", pw.reset.address, pw.reset_value });
|
||||
|
||||
// AML namespace parse integrity: consumed should equal total.
|
||||
const am = platform.amlStats();
|
||||
log.print(" aml : {d} namespace nodes, parsed {d}/{d} bytes\n", .{ am.nodes, am.consumed, am.total });
|
||||
|
||||
// Feed the architecture layer the discovered addresses/facts so it makes no legacy
|
||||
// assumptions — the point of all this on UEFI Class 3 firmware. MMIO bases
|
||||
// (HPET, I/O APIC) come from the device tree; scalar facts from ACPI.
|
||||
|
||||
+23
-34
@@ -181,10 +181,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
containmentTest();
|
||||
} else if (eql(case, "device-manager")) {
|
||||
deviceManagerTest(boot_information);
|
||||
} else if (eql(case, "poweroff")) {
|
||||
powerTest(.off);
|
||||
} else if (eql(case, "reboot")) {
|
||||
powerTest(.reboot);
|
||||
rebootTest();
|
||||
} else {
|
||||
log("DANOS-TEST-RESULT: FAIL (unknown case '{s}')\n", .{case});
|
||||
}
|
||||
@@ -201,15 +199,14 @@ fn platformHal() platform.Hal {
|
||||
/// Drive an ACPI power transition. On success the machine powers off or resets,
|
||||
/// so QEMU exits — the harness observes the process exit. If control returns, the
|
||||
/// transition failed and we emit a FAIL result.
|
||||
fn powerTest(comptime action: enum { off, reboot }) void {
|
||||
const name = if (action == .off) "poweroff" else "reboot";
|
||||
log("DANOS-TEST-BEGIN: {s}\n", .{name});
|
||||
// Soft-off (S5) is no longer a kernel operation — the ring-3 acpi service owns it
|
||||
// (exercised end-to-end by `orderly-shutdown`). Reboot stays in the kernel (FADT
|
||||
// reset register, no AML), so it keeps its own case.
|
||||
fn rebootTest() void {
|
||||
log("DANOS-TEST-BEGIN: reboot\n", .{});
|
||||
const hal = platformHal();
|
||||
log("DANOS-POWER: attempting {s}\n", .{name});
|
||||
switch (action) {
|
||||
.off => platform.shutdown(hal),
|
||||
.reboot => platform.reboot(hal),
|
||||
}
|
||||
log("DANOS-POWER: attempting reboot\n", .{});
|
||||
platform.reboot(hal);
|
||||
check("power transition took effect", false);
|
||||
result();
|
||||
}
|
||||
@@ -276,22 +273,19 @@ fn timer() void {
|
||||
}
|
||||
|
||||
/// Verify device discovery populated the platform facts the rest of the kernel
|
||||
/// depends on — the results ACPI parsing stashed in globals at boot. These are
|
||||
/// stable for the QEMU q35 + OVMF machine the harness runs, and span the tables:
|
||||
/// MADT (LAPIC base, CPU count), FADT (PM/reset registers), and the AML parse
|
||||
/// (the sleep type, plus the integrity check that every byte was consumed).
|
||||
/// depends on — the results the static ACPI tables stashed in globals at boot.
|
||||
/// These are stable for the QEMU q35 + OVMF machine the harness runs, and span the
|
||||
/// tables: MADT (LAPIC base, CPU count) and FADT (PM/reset registers). The kernel
|
||||
/// no longer interprets AML — sleep types are the ring-3 acpi service's concern.
|
||||
fn discoveryTest() void {
|
||||
log("DANOS-TEST-BEGIN: discovery\n", .{});
|
||||
const pinfo = platform.platformInformation();
|
||||
const pw = platform.powerInformation();
|
||||
const am = platform.amlStats();
|
||||
|
||||
check("LAPIC base discovered (MADT)", pinfo.lapic_base == 0xFEE00000);
|
||||
check("ACPI PM timer found (FADT)", pinfo.pm_timer.present());
|
||||
check("PM1a control register found (FADT)", pw.pm1a_cnt.present());
|
||||
check("reset register supported (FADT)", pw.reset_supported);
|
||||
check("S5 sleep type found (AML)", pw.s5 != null);
|
||||
check("AML parsed completely (consumed == total)", am.total > 0 and am.consumed == am.total);
|
||||
check("at least one CPU enumerated (MADT)", platform.cpus().len >= 1);
|
||||
|
||||
// M15: every PCI function now carries its own 4 KiB ECAM configuration space as
|
||||
@@ -2095,11 +2089,11 @@ fn acpiReportTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
}
|
||||
|
||||
/// M20.1: the ring-3 AML parse agrees with the kernel's. The manager spawns
|
||||
/// the discovery service (the acpi build variant); it claims the acpi-tables
|
||||
/// node, maps the blobs, parses them, and logs its Device count — which must
|
||||
/// equal what the kernel's own parse produced (the equivalence that licenses
|
||||
/// retiring the kernel's device build in M20.3).
|
||||
/// M20.1: the ring-3 AML parse works. The manager spawns the discovery service
|
||||
/// (the acpi build variant); it claims the acpi-tables node, maps the blobs,
|
||||
/// parses them, and self-verifies it found at least a floor of Device objects,
|
||||
/// printing "acpi-parse: ok". The kernel no longer parses AML, so there is no
|
||||
/// kernel count to compare against — the ring-3 parse is now the only one.
|
||||
fn acpiParseTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: acpi-parse\n", .{});
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
@@ -2114,23 +2108,18 @@ fn acpiParseTest(boot_information: *const BootInformation) void {
|
||||
return;
|
||||
};
|
||||
|
||||
// The kernel's own count, from the namespace it already built for \_S5.
|
||||
const kernel_devices = platform.amlDeviceCount();
|
||||
check("the kernel namespace has devices to compare against", kernel_devices >= 1);
|
||||
|
||||
// Spawn the discovery service directly with that count as argv: it parses
|
||||
// the same blobs in ring 3 and self-verifies, printing "acpi-parse: ok" iff
|
||||
// the counts match. The harness's expect regex is that marker — deterministic,
|
||||
// no racing the shared serial buffer.
|
||||
// Spawn the discovery service directly with a device-count *floor* as argv:
|
||||
// it parses the blobs in ring 3 and self-verifies it found at least that many
|
||||
// Device objects, printing "acpi-parse: ok". A floor of 1 just proves the
|
||||
// parser ran and produced a namespace (the QEMU q35 DSDT has dozens). The
|
||||
// marker is deterministic — no racing the shared serial buffer.
|
||||
process.setInitialRamdisk(image);
|
||||
var count_text: [16]u8 = undefined;
|
||||
const count_arg = std.fmt.bufPrint(&count_text, "{d}", .{kernel_devices}) catch "0";
|
||||
var spawned = false;
|
||||
var i: u32 = 0;
|
||||
while (i < rd.count) : (i += 1) {
|
||||
const item = rd.entry(i) orelse continue;
|
||||
if (!eql(item.name, "discovery")) continue;
|
||||
_ = process.spawnProcessSupervised(item.blob, 4, &.{ "discovery", count_arg }, scheduler.currentId(), null) catch 0;
|
||||
_ = process.spawnProcessSupervised(item.blob, 4, &.{ "discovery", "1" }, scheduler.currentId(), null) catch 0;
|
||||
spawned = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user