enumerate usable CPU cores from the MADT
Keep each Local APIC's id and expose platform.cpus().
This commit is contained in:
+4
-1
@@ -114,7 +114,10 @@ active reconsideration in favour of resilience — see [vision.md](vision.md).)
|
||||
Whatever the top goal, the *sequence* is the same and seL4 validates starting simple:
|
||||
|
||||
1. **Enumerate cores** — needs [device discovery](discovery.md) (ACPI MADT on x86,
|
||||
device tree on ARM). SMP is a concrete consumer of that work.
|
||||
device tree on ARM). SMP is a concrete consumer of that work. **Done on x86:** the
|
||||
MADT parse records every usable Local APIC — with the `apic_id` an AP wake targets —
|
||||
and `platform.cpus()` returns the list (see [discovery.md](discovery.md)). The boot
|
||||
log reports the count; the ARM (device-tree) path still needs it.
|
||||
2. **Wake the APs** — INIT–SIPI–SIPI on x86; PSCI/spin-tables on ARM. Each core brings
|
||||
up its own tables, timer, and idle task.
|
||||
3. **Start with a big kernel lock.** It's a legitimate first design, not a shortcut —
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user