From cf58ad369e0701643f6cbfb1d9a8210dc99988e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gr=C3=A9goire?= Date: Wed, 2 Sep 2026 22:17:54 -0400 Subject: [PATCH] ke: freeze CPUs on panic --- src/kern/amd64/impl.zig | 4 ++++ src/kern/ipi.zig | 39 ++++++++++++++++++++++++++++++++++++++- src/kern/panic.zig | 36 +++++++++++++++++++++++++++++++----- src/kern/private.zig | 3 +++ 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/kern/amd64/impl.zig b/src/kern/amd64/impl.zig index 302d3b4..159a333 100644 --- a/src/kern/amd64/impl.zig +++ b/src/kern/amd64/impl.zig @@ -158,6 +158,10 @@ pub inline fn percpu_ptr_other(variable: anytype, id: u32) @TypeOf(variable) { return percpu_ptr_for(variable, id); } +pub inline fn halt() void { + asm volatile ("hlt"); +} + pub inline fn percpu_ptr(variable: anytype) @TypeOf(variable) { const offset = asm volatile ("mov %%gs:(%[self]), %[out]" : [out] "=r" (-> u64), diff --git a/src/kern/ipi.zig b/src/kern/ipi.zig index 97b8b70..285b947 100644 --- a/src/kern/ipi.zig +++ b/src/kern/ipi.zig @@ -4,7 +4,7 @@ const r = @import("root"); const std = @import("std"); const ke = r.ke; -const ki = r.ki; +const ki = ke.private; const pl = r.pl; const State = enum(u8) { @@ -31,6 +31,8 @@ const PerCpu = struct { const percpu = ke.CpuLocal(PerCpu, undefined); +var frozen_cpus: std.atomic.Value(u32) = .init(0); + /// Call a function on another CPU. /// The function will be executed synchronously, i.e this will block until /// the function finishes running. The callback is called with the sender cpu @@ -63,6 +65,10 @@ pub fn unicast( ke.ipl.lower(ipl); } +/// Call a function on all other CPUs. +/// The function will be executed synchronously, i.e this will block until +/// the function finishes running. The callback is called with the sender cpu +/// and provided argument as arguments. pub fn broadcast(func: *const fn (u32, ?*anyopaque) void, arg: ?*anyopaque) void { const ipl = ke.ipl.raise(.Dispatch); const curcpu = percpu.local(); @@ -91,6 +97,28 @@ pub fn broadcast(func: *const fn (u32, ?*anyopaque) void, arg: ?*anyopaque) void ke.ipl.lower(ipl); } +/// Freeze all other CPUs. +/// Called at IPL >= Dispatch. +pub fn freeze_cpus() void { + frozen_cpus.store(1, .monotonic); + + for (0..ke.ncpus) |i| { + if (i == ke.cpu.current()) continue; + const remote = percpu.remote(@intCast(i)); + remote.pending.set(ke.cpu.current(), .monotonic); + + const state = remote.state.swap(.Pending, .release); + + if (state == .Idle) { + pl.send_ipi(@intCast(i)); + } + } + + while (frozen_cpus.load(.monotonic) != ke.ncpus) { + std.atomic.spinLoopHint(); + } +} + pub fn ipi_handler() void { const curcpu = percpu.local(); @@ -105,6 +133,15 @@ pub fn ipi_handler() void { .monotonic, ); + if (frozen_cpus.load(.monotonic) != 0) { + // We have been requested to freeze, ack and do it now. + _ = frozen_cpus.fetchAdd(1, .monotonic); + + while (true) { + ki.impl.halt(); + } + } + if (val != null) { // CAS failed. return; diff --git a/src/kern/panic.zig b/src/kern/panic.zig index b26526d..2c3c6db 100644 --- a/src/kern/panic.zig +++ b/src/kern/panic.zig @@ -71,24 +71,50 @@ fn walk_stack_frame(base: usize) void { } pub var panic_lock: ke.SpinLock = .init(); +var crash_count: std.atomic.Value(u8) = .init(0); pub fn panic_with_frame( msg: []const u8, frame: usize, ) noreturn { - std.log.err("KERNEL PANIC: {s} on CPU {}, curthread is {*}", .{ msg, ke.cpu.current(), ki.sched.percpu.local().current_thread.? }); + _ = ki.impl.disable_interrupts(); + + std.log.info("Crash count is {} on cpu {}", .{ crash_count.load(.monotonic), ke.cpu.current() }); + + if (crash_count.fetchAdd(1, .monotonic) != 0) { + std.log.err("Recursive panic from CPU {}", .{ke.cpu.current()}); + // Another CPU has already crashed, just freeze manually right now. + + while (true) { + ki.impl.halt(); + } + } + + // We're the first one who actually crashed, that means we get to handle + // things like dumping the system info and freezing other CPUs. Start by + // doing the freeze. + ki.ipi.freeze_cpus(); + + _ = ke.ipl.raise(.High); + + std.log.err("KERNEL PANIC: {s} on CPU {}, curthread is {*}", .{ + msg, + ke.cpu.current(), + ki.sched.percpu.local().current_thread.?, + }); + std.log.err("Stack trace:", .{}); + walk_stack_frame(frame); - panic_lock.release_no_ipl(); - - while (true) {} + while (true) { + ki.impl.halt(); + } } pub fn panic( msg: []const u8, _: ?usize, ) noreturn { - panic_lock.acquire_no_ipl(); panic_with_frame(msg, @frameAddress()); } diff --git a/src/kern/private.zig b/src/kern/private.zig index c53f672..bb33425 100644 --- a/src/kern/private.zig +++ b/src/kern/private.zig @@ -46,6 +46,9 @@ const ImplSchema = struct { return false; } + /// Halt the CPU. This is called on crashes. + pub fn halt() void {} + /// Restore the state of interrupts. pub fn restore_interrupts(state: bool) void { _ = state;