enumerate usable CPU cores from the MADT

Keep each Local APIC's id and expose platform.cpus().
This commit is contained in:
Daniel Samson
2026-07-08 12:35:30 +01:00
parent 26d2f5259c
commit cf7c6df41c
4 changed files with 66 additions and 1 deletions
+41
View File
@@ -91,6 +91,35 @@ pub const PlatformInfo = struct {
/// Filled in by `discover`; the arch layer reads it during bring-up.
pub var platform_info: PlatformInfo = .{};
/// One usable logical processor, from a MADT type-0 (Local APIC) record. The
/// `apic_id` is the Local APIC ID that SMP bring-up targets to wake this core
/// (INIT–SIPI–SIPI); `processor_id` is the ACPI namespace handle. Only processors
/// the firmware marks *enabled* are recorded — a disabled one can't be started.
pub const Cpu = struct {
processor_id: u8,
apic_id: u8,
/// MADT flags bit 1: usable but firmware-started offline (hot-plug / deferred
/// bring-up), as opposed to already available. Informational for now.
online_capable: bool,
};
/// The set of usable logical processors the MADT listed — the hardware's degree of
/// parallelism. Includes the bootstrap processor danos already runs on; the rest
/// are the application processors SMP bring-up would start (see docs/smp.md).
pub const CpuInfo = struct {
/// A static pool sized well above any danos target (a desktop, two 4-core Pis).
/// If the MADT ever lists more, the surplus is dropped and counted in `dropped`
/// so the truncation is never silent.
cpus: [max_cpus]Cpu = undefined,
count: usize = 0,
dropped: usize = 0,
};
const max_cpus = 64;
/// Filled in by `discover` (from the MADT); SMP bring-up reads it to wake the APs.
pub var cpu_info: CpuInfo = .{};
/// Integrity/diagnostics for the AML parse. `consumed == total` means the parser
/// walked every byte of the DSDT/SSDTs without desyncing.
pub const AmlStats = struct {
@@ -438,6 +467,18 @@ fn parseMadt(dt: *DeviceTree, header: *const SystemDescriptorTableHeader) !void
var nb: [24]u8 = undefined;
const nm = std.fmt.bufPrint(&nb, "cpu{d}", .{la.processor_id}) catch "cpu";
_ = try dt.addChild(dt.root, .processor, nm);
// Also record it as a schedulable core (with the APIC ID an AP
// wake needs, which the device node name doesn't preserve).
if (cpu_info.count < cpu_info.cpus.len) {
cpu_info.cpus[cpu_info.count] = .{
.processor_id = la.processor_id,
.apic_id = la.apic_id,
.online_capable = la.flags & 2 != 0,
};
cpu_info.count += 1;
} else {
cpu_info.dropped += 1;
}
}
},
1 => {
+17
View File
@@ -24,6 +24,7 @@ pub const AmlStats = acpi.AmlStats;
pub const PlatformInfo = acpi.PlatformInfo;
pub const RegAccess = acpi.RegAccess;
pub const IsoEntry = acpi.IsoEntry;
pub const Cpu = acpi.Cpu;
/// The register map + sleep types discovery extracted, for logging/diagnostics.
pub fn powerInfo() PowerInfo {
@@ -41,6 +42,22 @@ 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_info.cpus[0..acpi.cpu_info.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_info.dropped;
}
/// Enumerate hardware into a fresh device tree. `hal` supplies the hardware
/// primitives the backend needs (MMIO mapping for PCIe config space, port I/O for
/// ACPI registers); pass the arch implementation. Errors leave nothing to clean up
+4
View File
@@ -200,6 +200,10 @@ fn kmain(boot_info: *const BootInfo) noreturn {
log.write(" console UART: none in SPCR -> legacy COM1\n");
}
log.print(" ioapic : base 0x{x}, {d} inputs (masked); entry0 low 0x{x}\n", .{ ioapic_base, arch.ioapicEntryCount(), arch.ioapicEntryLow(0) });
const cores = platform.cpus();
log.print(" cpus : {d} usable core(s); 1 running (BSP), {d} AP(s) parked (SMP bring-up pending)\n", .{ cores.len, if (cores.len > 0) cores.len - 1 else 0 });
if (platform.cpusDropped() > 0)
log.print(" cpus : WARNING {d} core(s) beyond pool cap dropped\n", .{platform.cpusDropped()});
} else |err| {
log.print("\ndanos: device discovery failed: {s}\n", .{@errorName(err)});
}