close audited test gaps: discovery, W^X, power

Add a discovery case (asserts stable ACPI/MADT/FADT/AML facts) and a wx case (audits R+X code vs NX data/rodata/heap/stack via arch.pageExecutable). Wire the existing poweroff/reboot cases into the harness. Suite: 18 -> 22.
This commit is contained in:
Daniel Samson
2026-07-08 14:17:54 +01:00
parent 5940864958
commit f02259cae0
2 changed files with 66 additions and 0 deletions
+52
View File
@@ -50,6 +50,10 @@ fn result() void {
pub fn run(case: []const u8, boot_info: *const BootInfo) void {
if (eql(case, "smoke")) {
smoke(boot_info);
} else if (eql(case, "discovery")) {
discoveryTest();
} else if (eql(case, "wx")) {
wxTest();
} else if (eql(case, "timer")) {
timer();
} else if (eql(case, "clock")) {
@@ -171,6 +175,54 @@ fn timer() void {
result();
}
/// 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).
fn discoveryTest() void {
log("DANOS-TEST-BEGIN: discovery\n", .{});
const pinfo = platform.platformInfo();
const pw = platform.powerInfo();
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);
result();
}
/// Audit the W^X invariant across the memory classes: kernel code must be
/// executable, everything else must not be. `arch.pageExecutable` reads the leaf
/// page-table entry's NX bit, so this guards the permission overlay in paging.zig —
/// a broader check than `fault-nx`, which only exercises one data page.
fn wxTest() void {
log("DANOS-TEST-BEGIN: wx\n", .{});
check("kernel code is executable (R+X)", arch.pageExecutable(@intFromPtr(&wxTest)));
const ro = "danos-wx-probe"; // string literal -> .rodata
check("rodata is non-executable (NX)", !arch.pageExecutable(@intFromPtr(ro.ptr)));
check("kernel data is non-executable (NX)", !arch.pageExecutable(@intFromPtr(&passed)));
if (heap.allocator().alloc(u8, 64) catch null) |h| {
check("heap is non-executable (NX)", !arch.pageExecutable(@intFromPtr(h.ptr)));
heap.allocator().free(h);
}
var local: u64 = 0;
_ = &local;
check("stack is non-executable (NX)", !arch.pageExecutable(@intFromPtr(&local)));
result();
}
/// Verify the on-demand VMM: map a fresh frame at an unused virtual address, and
/// check it's writable and reads back.
fn vmm() void {