Skip to main content

strat9_kernel/boot/
panic.rs

1use core::{
2    panic::PanicInfo,
3    sync::atomic::{AtomicBool, Ordering},
4};
5use spin::Mutex;
6use x86_64::VirtAddr;
7
8type PanicHook = fn(&PanicInfo);
9const MAX_PANIC_HOOKS: usize = 8;
10
11static PANIC_HOOKS: Mutex<[Option<PanicHook>; MAX_PANIC_HOOKS]> =
12    Mutex::new([None; MAX_PANIC_HOOKS]);
13static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
14
15pub fn panic_in_progress() -> bool {
16    PANIC_IN_PROGRESS.load(Ordering::SeqCst)
17}
18
19/// Performs the register panic hook operation.
20pub fn register_panic_hook(hook: PanicHook) -> bool {
21    let mut hooks = PANIC_HOOKS.lock();
22    for slot in hooks.iter_mut() {
23        if slot.is_none() {
24            *slot = Some(hook);
25            return true;
26        }
27    }
28    false
29}
30
31/// Performs the run panic hooks operation.
32fn run_panic_hooks(info: &PanicInfo) {
33    // In case of panic, we try to lock hooks but if it fails (deadlock during panic)
34    // we might skip them or use a more aggressive approach.
35    // For now, we try_lock to be safe.
36    if let Some(hooks) = PANIC_HOOKS.try_lock() {
37        for hook in hooks.iter().flatten() {
38            hook(info);
39        }
40    }
41}
42
43/// Performs the panic hook dump context operation.
44fn panic_hook_dump_context(_info: &PanicInfo) {
45    let cpu = crate::arch::x86_64::percpu::current_cpu_index();
46    let ticks = crate::process::scheduler::ticks();
47    let cr3 = crate::memory::paging::active_page_table().as_u64();
48    crate::serial_println!("panic-hook: cpu={} ticks={} cr3=0x{:x}", cpu, ticks, cr3);
49    // Use try_lock variant to avoid deadlocking when the panic occurs while
50    // the scheduler lock is already held (e.g. during a context switch).
51    if let Some(task) = crate::process::scheduler::current_task_clone_try() {
52        crate::serial_println!(
53            "panic-hook: current_task id={} name={}",
54            task.id.as_u64(),
55            task.name
56        );
57    } else {
58        crate::serial_println!("panic-hook: current_task none (scheduler locked or idle)");
59    }
60    let sched = crate::process::scheduler::state_snapshot();
61    if cpu < sched.cpu_count {
62        crate::serial_println!(
63            "panic-hook: sched cpu={} current_tid={} need_resched={} rq(rt/fair/idle)={}/{}/{} blocked={} init={} phase={}",
64            cpu,
65            sched.current_task[cpu],
66            sched.need_resched[cpu],
67            sched.rq_rt[cpu],
68            sched.rq_fair[cpu],
69            sched.rq_idle[cpu],
70            sched.blocked_tasks,
71            sched.initialized,
72            sched.boot_phase
73        );
74    }
75    let fb = crate::arch::x86_64::vga::framebuffer_info();
76    crate::serial_println!(
77        "panic-hook: fb={}x{} {}bpp pitch={} text={}x{}",
78        fb.width,
79        fb.height,
80        fb.bpp,
81        fb.pitch,
82        fb.text_cols,
83        fb.text_rows
84    );
85}
86
87/// Reads rbp.
88#[inline(always)]
89fn read_rbp() -> u64 {
90    let rbp: u64;
91    unsafe {
92        core::arch::asm!("mov {}, rbp", out(reg) rbp, options(nomem, nostack, preserves_flags));
93    }
94    rbp
95}
96
97/// Reads rsp.
98#[inline(always)]
99fn read_rsp() -> u64 {
100    let rsp: u64;
101    unsafe {
102        core::arch::asm!("mov {}, rsp", out(reg) rsp, options(nomem, nostack, preserves_flags));
103    }
104    rsp
105}
106
107/// Performs the addr readable operation.
108fn addr_readable(addr: u64) -> bool {
109    crate::memory::paging::translate(VirtAddr::new(addr)).is_some()
110}
111
112/// Performs the panic hook backtrace operation.
113fn panic_hook_backtrace(_info: &PanicInfo) {
114    let mut rbp = read_rbp();
115    let rsp = read_rsp();
116    crate::serial_println!("panic-hook: stack rsp=0x{:x} rbp=0x{:x}", rsp, rbp);
117    crate::serial_println!("panic-hook: backtrace (frame-pointer)");
118
119    // [rbp + 0] = previous rbp, [rbp + 8] = return address
120    for i in 0..16 {
121        if rbp == 0 || (rbp & 0x7) != 0 {
122            crate::serial_println!("  #{:02}: stop (invalid rbp=0x{:x})", i, rbp);
123            break;
124        }
125        if !addr_readable(rbp) || !addr_readable(rbp.saturating_add(8)) {
126            crate::serial_println!("  #{:02}: stop (unmapped rbp=0x{:x})", i, rbp);
127            break;
128        }
129
130        let prev = unsafe { *(rbp as *const u64) };
131        let ret = unsafe { *((rbp + 8) as *const u64) };
132        crate::serial_println!("  #{:02}: rip=0x{:x} rbp=0x{:x}", i, ret, rbp);
133
134        // Stop on corrupted/non-progressing chains.
135        if prev <= rbp || prev.saturating_sub(rbp) > 1024 * 1024 {
136            break;
137        }
138        rbp = prev;
139    }
140}
141
142/// Performs the install default panic hooks operation.
143pub fn install_default_panic_hooks() {
144    let _ = register_panic_hook(panic_hook_dump_context);
145    let _ = register_panic_hook(panic_hook_backtrace);
146}
147
148/// Panic handler for the kernel
149pub fn panic_handler(info: &PanicInfo) -> ! {
150    // 1. Enter emergency mode for serial output immediately.
151    // This allows serial_println! to bypass locks.
152    crate::arch::x86_64::serial::enter_emergency_mode();
153
154    // 2. Prevent recursive panics.
155    if PANIC_IN_PROGRESS.swap(true, Ordering::SeqCst) {
156        loop {
157            crate::arch::x86_64::hlt();
158        }
159    }
160
161    // 3. Disable interrupts on the current CPU first.
162    crate::arch::x86_64::cli();
163
164    // 4. Print the panic location/message before halting sibling CPUs so that
165    // NMI-based panic-stop traffic cannot obscure the root cause.
166    crate::serial_println!("\n\x1b[31;1m!!! KERNEL PANIC !!!\x1b[0m");
167    crate::serial_println!("=== GURU MEDIATiON :: KERNEL PANiK ===");
168    if let Some(location) = info.location() {
169        crate::serial_println!(
170            "not kalm :: panik at {}:{}:{}",
171            location.file(),
172            location.line(),
173            location.column()
174        );
175    }
176    crate::serial_println!("Message: {}", info.message());
177    crate::serial_println!("====================");
178
179    // 5. Stop all other CPUs only after the root-cause lines are emitted.
180    crate::arch::x86_64::smp::broadcast_panic_halt();
181
182    // Run custom panic hooks before trying complex rendering.
183    run_panic_hooks(info);
184
185    // Best-effort panic display on framebuffer console (no legacy 0xB8000 access).
186    if crate::arch::x86_64::vga::is_available() {
187        if let Some(mut writer) = crate::arch::x86_64::vga::VGA_WRITER.try_lock() {
188            use core::fmt::Write;
189            writer.set_rgb_color(
190                crate::arch::x86_64::vga::RgbColor::new(0xFF, 0xE7, 0xA0),
191                crate::arch::x86_64::vga::RgbColor::new(0x3A, 0x1F, 0x00),
192            );
193            writer.clear();
194            let _ = writeln!(writer, "=== GURU MEDIATiON :: KERNEL PANiK ===");
195            if let Some(location) = info.location() {
196                let _ = writeln!(
197                    writer,
198                    "not kalm :: panik at {}:{}:{}",
199                    location.file(),
200                    location.line(),
201                    location.column()
202                );
203            }
204            let _ = writeln!(writer, "Message: {}", info.message());
205        }
206    }
207
208    // Halt the CPU
209    loop {
210        crate::arch::x86_64::hlt();
211    }
212}