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