keep the AP trampoline inert between wakes, and retry failed cores

Arm the low frame (copy blob, make executable) only while a core climbs, then zero it and restore RW+NX; the frame stays reserved so cores can be re-woken. startSecondary is one re-runnable attempt; boot retries a non-responding core 3x. Validated the retry path by forcing a first-attempt failure.
This commit is contained in:
Daniel Samson
2026-07-08 13:32:34 +01:00
parent dba3939a0f
commit debe815a5c
4 changed files with 68 additions and 28 deletions
+14 -9
View File
@@ -259,17 +259,18 @@ fn bringUpSecondaries() void {
const cores = platform.cpus();
if (cores.len <= 1) return;
// The trampoline page was reserved below 1 MiB at boot (a real-mode SIPI vector
// addresses it). Make it executable — the blanket RAM mapping is NX (W^X).
// A low (<1 MiB) frame was reserved at boot for the real-mode trampoline (a SIPI
// vector addresses it). It's kept for the system's life — armed only during a
// wake, inert (zeroed, non-executable) otherwise — so cores can be re-woken later.
if (ap_trampoline_page == 0) {
log.write("danos: smp: no low page for the AP trampoline; staying uniprocessor\n");
return;
}
arch.setPageExecutable(ap_trampoline_page);
arch.prepareSecondaries(ap_trampoline_page);
arch.setTrampolinePage(ap_trampoline_page);
arch.setSecondaryEntry(scheduler.secondaryMain); // where a woken core joins the run loop
log.print("\ndanos: bringing up {d} application processor(s)\n", .{cores.len - 1});
const max_wake_attempts = 3; // a core that misses the first INIT-SIPI-SIPI gets retried
for (cores[1..], 1..) |core, index| {
const stack = heap.allocator().alloc(u8, 16 * 1024) catch {
log.print(" cpu apic_id {d}: no stack; skipped\n", .{core.apic_id});
@@ -277,11 +278,15 @@ fn bringUpSecondaries() void {
};
const stack_top = (@intFromPtr(stack.ptr) + stack.len) & ~@as(usize, 15);
const pc = scheduler.prepareSecondary(index, core.apic_id);
if (arch.startSecondary(core.apic_id, stack_top, @intFromPtr(pc), index)) {
pc.online = true;
log.print(" cpu apic_id {d}: online\n", .{core.apic_id});
} else {
log.print(" cpu apic_id {d}: no response (parked)\n", .{core.apic_id});
var attempt: u32 = 1;
while (attempt <= max_wake_attempts) : (attempt += 1) {
if (arch.startSecondary(core.apic_id, stack_top, @intFromPtr(pc), index)) {
pc.online = true;
log.print(" cpu apic_id {d}: online (attempt {d})\n", .{ core.apic_id, attempt });
break;
}
if (attempt == max_wake_attempts)
log.print(" cpu apic_id {d}: no response after {d} attempts (parked)\n", .{ core.apic_id, max_wake_attempts });
}
}
log.print("danos: {d}/{d} cores online\n", .{ scheduler.onlineCount(), cores.len });