add device platform module with ACPI support
This commit is contained in:
@@ -11,6 +11,7 @@ const idt = @import("idt.zig");
|
||||
const paging = @import("paging.zig");
|
||||
const serial = @import("serial.zig");
|
||||
const apic = @import("apic.zig");
|
||||
const io = @import("io.zig");
|
||||
|
||||
/// The saved register/trap frame passed to a fault handler.
|
||||
pub const CpuState = idt.CpuState;
|
||||
@@ -177,6 +178,28 @@ pub fn vectorName(vector: u64) []const u8 {
|
||||
return idt.vectorName(vector);
|
||||
}
|
||||
|
||||
/// Read `width` bytes (1/2/4) from an I/O port. The generic device layer drives
|
||||
/// ACPI registers through this rather than naming x86 port instructions; on an
|
||||
/// MMIO-only architecture this would be implemented differently.
|
||||
pub fn pioRead(width: u8, port: u16) u32 {
|
||||
return switch (width) {
|
||||
1 => io.inb(port),
|
||||
2 => io.inw(port),
|
||||
4 => io.inl(port),
|
||||
else => 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Write `width` bytes (1/2/4) to an I/O port.
|
||||
pub fn pioWrite(width: u8, port: u16, value: u32) void {
|
||||
switch (width) {
|
||||
1 => io.outb(port, @truncate(value)),
|
||||
2 => io.outw(port, @truncate(value)),
|
||||
4 => io.outl(port, value),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// CR2 holds the faulting linear address after a page fault (#PF, vector 14).
|
||||
pub fn readCr2() u64 {
|
||||
return asm volatile ("mov %%cr2, %[out]"
|
||||
|
||||
@@ -16,6 +16,36 @@ pub fn inb(port: u16) u8 {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn outw(port: u16, value: u16) void {
|
||||
asm volatile ("outw %[value], %[port]"
|
||||
:
|
||||
: [value] "{ax}" (value),
|
||||
[port] "{dx}" (port),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn inw(port: u16) u16 {
|
||||
return asm volatile ("inw %[port], %[value]"
|
||||
: [value] "={ax}" (-> u16),
|
||||
: [port] "{dx}" (port),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn outl(port: u16, value: u32) void {
|
||||
asm volatile ("outl %[value], %[port]"
|
||||
:
|
||||
: [value] "{eax}" (value),
|
||||
[port] "{dx}" (port),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn inl(port: u16) u32 {
|
||||
return asm volatile ("inl %[port], %[value]"
|
||||
: [value] "={eax}" (-> u32),
|
||||
: [port] "{dx}" (port),
|
||||
);
|
||||
}
|
||||
|
||||
/// Read a model-specific register (returns edx:eax combined).
|
||||
pub fn rdmsr(msr: u32) u64 {
|
||||
var low: u32 = undefined;
|
||||
|
||||
@@ -5,6 +5,7 @@ const console = @import("console.zig");
|
||||
const pmm = @import("pmm.zig");
|
||||
const heap = @import("heap.zig");
|
||||
const scheduler = @import("scheduler.zig");
|
||||
const platform = @import("platform");
|
||||
const tests = @import("tests.zig");
|
||||
const build_options = @import("build_options");
|
||||
const BootInfo = danos.BootInfo;
|
||||
@@ -102,6 +103,38 @@ fn kmain(boot_info: *const BootInfo) noreturn {
|
||||
const s2 = pmm.stats();
|
||||
serial0.debugPrint(" Kernel footprint: {d} KiB\n", .{kib(s1.free_frames - s2.free_frames)});
|
||||
|
||||
// Enumerate hardware from the firmware tables (ACPI here) into a generic
|
||||
// device tree, then list it. Discovery walks ACPI memory directly (identity-
|
||||
// mapped) and maps PCIe config space on demand via the VMM. A failure here is
|
||||
// not fatal yet — log it and carry on.
|
||||
const hal = platform.Hal{
|
||||
.mapMmio = arch.mapPage,
|
||||
.pioRead = arch.pioRead,
|
||||
.pioWrite = arch.pioWrite,
|
||||
};
|
||||
if (platform.discover(boot_info, heap.allocator(), hal)) |devtree| {
|
||||
var dt = devtree;
|
||||
serial0.debugWrite("\ndanos: device discovery online\n");
|
||||
dt.dump(console.SerialConsole.debugWrite);
|
||||
|
||||
// Power register map extracted from the FADT + AML, for confidence it parsed.
|
||||
const pw = platform.powerInfo();
|
||||
serial0.debugWrite("danos: power\n");
|
||||
serial0.debugPrint(" 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| {
|
||||
serial0.debugPrint(" S5 slp_typ : a={d} b={d}\n", .{ s.slp_typ_a, s.slp_typ_b });
|
||||
} else {
|
||||
serial0.debugWrite(" S5 slp_typ : (not found)\n");
|
||||
}
|
||||
serial0.debugPrint(" 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();
|
||||
serial0.debugPrint(" aml : {d} namespace nodes, parsed {d}/{d} bytes\n", .{ am.nodes, am.consumed, am.total });
|
||||
} else |err| {
|
||||
serial0.debugPrint("\ndanos: device discovery failed: {s}\n", .{@errorName(err)});
|
||||
}
|
||||
|
||||
// Register the current context as the first task before enabling preemption.
|
||||
scheduler.init(4);
|
||||
serial0.debugWrite("\ndanos: scheduler online\n");
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
const std = @import("std");
|
||||
const danos = @import("danos");
|
||||
const arch = @import("arch");
|
||||
const platform = @import("platform");
|
||||
const pmm = @import("pmm.zig");
|
||||
const heap = @import("heap.zig");
|
||||
const sched = @import("scheduler.zig");
|
||||
@@ -77,11 +78,39 @@ pub fn run(case: []const u8, boot_info: *const BootInfo) void {
|
||||
faultNoExecute();
|
||||
} else if (eql(case, "fault-null")) {
|
||||
faultNull();
|
||||
} else if (eql(case, "poweroff")) {
|
||||
powerTest(.off);
|
||||
} else if (eql(case, "reboot")) {
|
||||
powerTest(.reboot);
|
||||
} else {
|
||||
log("DANOS-TEST-RESULT: FAIL (unknown case '{s}')\n", .{case});
|
||||
}
|
||||
}
|
||||
|
||||
fn platformHal() platform.Hal {
|
||||
return .{
|
||||
.mapMmio = arch.mapPage,
|
||||
.pioRead = arch.pioRead,
|
||||
.pioWrite = arch.pioWrite,
|
||||
};
|
||||
}
|
||||
|
||||
/// 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});
|
||||
const hal = platformHal();
|
||||
log("DANOS-POWER: attempting {s}\n", .{name});
|
||||
switch (action) {
|
||||
.off => platform.shutdown(hal),
|
||||
.reboot => platform.reboot(hal),
|
||||
}
|
||||
check("power transition took effect", false);
|
||||
result();
|
||||
}
|
||||
|
||||
const BootInfo = danos.BootInfo;
|
||||
|
||||
fn eql(a: []const u8, b: []const u8) bool {
|
||||
|
||||
Reference in New Issue
Block a user