1use crate::arch::xshim::VirtAddr;
2use core::{
3 panic::PanicInfo,
4 sync::atomic::{AtomicBool, Ordering},
5};
6use spin::Mutex;
7type PanicHook = fn(&PanicInfo);
8const MAX_PANIC_HOOKS: usize = 8;
9
10static PANIC_HOOKS: Mutex<[Option<PanicHook>; MAX_PANIC_HOOKS]> =
11 Mutex::new([None; MAX_PANIC_HOOKS]);
12static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
13
14pub fn panic_in_progress() -> bool {
16 PANIC_IN_PROGRESS.load(Ordering::SeqCst)
17}
18
19pub fn register_panic_hook(hook: PanicHook) -> bool {
21 crate::e9_mark!(b'P');
22 let mut hooks = PANIC_HOOKS.lock();
23 crate::e9_mark!(b'p');
24 for slot in hooks.iter_mut() {
25 if slot.is_none() {
26 *slot = Some(hook);
27 crate::e9_mark!(b'q');
28 return true;
29 }
30 }
31 crate::e9_mark!(b'r');
32 false
33}
34
35fn run_panic_hooks(info: &PanicInfo) {
37 match PANIC_HOOKS.try_lock() {
38 Some(hooks) => {
39 for hook in hooks.iter().flatten() {
40 hook(info);
41 }
42 }
43 None => {
44 crate::serial_force_println!(
45 "[panic] WARNING: panic hooks skipped (lock held by panicking context)"
46 );
47 }
48 }
49}
50
51fn panic_hook_dump_context(_info: &PanicInfo) {
53 let cpu = crate::arch::percpu::current_cpu_index();
54 let ticks = crate::process::scheduler::ticks();
55 let cr3 = crate::memory::paging::active_page_table().as_u64();
56 crate::serial_println!("panic-hook: cpu={} ticks={} cr3=0x{:x}", cpu, ticks, cr3);
57 if let Some(task) = crate::process::scheduler::current_task_clone_try() {
58 crate::serial_println!(
59 "panic-hook: current_task id={} name={}",
60 task.id.as_u64(),
61 task.name
62 );
63 } else {
64 crate::serial_println!("panic-hook: current_task none (scheduler locked or idle)");
65 }
66 let sched = crate::process::scheduler::state_snapshot();
67 if cpu < sched.cpu_count {
68 crate::serial_println!(
69 "panic-hook: sched cpu={} current_tid={} need_resched={} rq(rt/fair/idle)={}/{}/{} blocked={} init={} phase={}",
70 cpu,
71 sched.current_task[cpu],
72 sched.need_resched[cpu],
73 sched.rq_rt[cpu],
74 sched.rq_fair[cpu],
75 sched.rq_idle[cpu],
76 sched.blocked_tasks,
77 sched.initialized,
78 sched.boot_phase
79 );
80 }
81}
82
83#[inline(always)]
88fn read_rbp() -> u64 {
89 let rbp: u64;
90 unsafe {
91 core::arch::asm!("mov {}, rbp", out(reg) rbp, options(nomem, nostack, preserves_flags));
92 }
93 rbp
94}
95
96#[inline(always)]
97fn read_rsp() -> u64 {
98 let rsp: u64;
99 unsafe {
100 core::arch::asm!("mov {}, rsp", out(reg) rsp, options(nomem, nostack, preserves_flags));
101 }
102 rsp
103}
104
105fn addr_readable(addr: u64) -> bool {
106 crate::memory::paging::translate(VirtAddr::new(addr)).is_some()
107}
108
109fn read_cr_regs() -> (u64, u64, u64, u64) {
111 let cr0: u64;
112 let cr2: u64;
113 let cr3: u64;
114 let cr4: u64;
115 unsafe {
116 core::arch::asm!("mov {}, cr0", out(reg) cr0, options(nomem, nostack, preserves_flags));
117 core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nomem, nostack, preserves_flags));
118 core::arch::asm!("mov {}, cr3", out(reg) cr3, options(nomem, nostack, preserves_flags));
119 core::arch::asm!("mov {}, cr4", out(reg) cr4, options(nomem, nostack, preserves_flags));
120 }
121 (cr0, cr2, cr3, cr4)
122}
123
124fn dump_backtrace() {
126 crate::serial_println!("RSP=0x{:016X} RBP=0x{:016X}", read_rsp(), read_rbp());
127 crate::serial_println!("Backtrace (frame-pointer):");
128 let mut rbp = read_rbp();
129 for i in 0..16 {
130 if rbp == 0 || (rbp & 0x7) != 0 {
131 crate::serial_println!(" #{:02}: stop (invalid rbp)", i);
132 break;
133 }
134 if !addr_readable(rbp) || !addr_readable(rbp.saturating_add(8)) {
135 crate::serial_println!(" #{:02}: stop (unmapped)", i);
136 break;
137 }
138 let prev = unsafe { *(rbp as *const u64) };
139 let ret = unsafe { *((rbp + 8) as *const u64) };
140
141 if let Some((name, offset)) = super::symbols::lookup(ret) {
143 if offset == 0 {
144 crate::serial_println!(" #{:02}: RIP=0x{:016X} {}", i, ret, name);
145 } else {
146 crate::serial_println!(" #{:02}: RIP=0x{:016X} {}+0x{:x}", i, ret, name, offset);
147 }
148 } else {
149 crate::serial_println!(" #{:02}: RIP=0x{:016X}", i, ret);
150 }
151
152 if prev <= rbp || prev.saturating_sub(rbp) > 1024 * 1024 {
153 break;
154 }
155 rbp = prev;
156 }
157}
158
159fn dump_panic_info(info: &PanicInfo) {
161 crate::serial_force_println!("\n\x1b[31;1m!!! KERNEL PANIC !!!\x1b[0m");
162 crate::serial_force_println!("=== GURU MEDiTATiON :: KERNEL PANiK ===");
163 crate::serial_force_println!("");
164
165 if let Some(loc) = info.location() {
167 crate::serial_force_println!("File: {}:{}:{}", loc.file(), loc.line(), loc.column());
168 } else {
169 crate::serial_force_println!("File: (unknown)");
170 }
171 crate::serial_force_println!("Message: {}", info.message());
172 crate::serial_force_println!("");
173
174 let cpu = crate::arch::percpu::current_cpu_index();
176 let (cr0, cr2, cr3, cr4) = read_cr_regs();
177 let rsp = read_rsp();
178 let rbp = read_rbp();
179 crate::serial_force_println!("CPU={} CR0={:#X} CR2={:#X}", cpu, cr0, cr2);
180 crate::serial_force_println!("CR3={:#X} CR4={:#X}", cr3, cr4);
181 crate::serial_force_println!("RSP={:#018X} RBP={:#018X}", rsp, rbp);
182 crate::serial_force_println!("");
183
184 dump_backtrace();
186 crate::serial_force_println!("");
187
188 let ticks = crate::process::scheduler::ticks();
190 crate::serial_force_println!("Ticks={}", ticks);
191 if let Some(task) = crate::process::scheduler::current_task_clone_try() {
192 crate::serial_force_println!("Task: id={} name={}", task.id.as_u64(), task.name);
193 } else {
194 crate::serial_force_println!("Task: (scheduler locked / idle)");
195 }
196
197 let sched = crate::process::scheduler::state_snapshot();
198 if cpu < sched.cpu_count {
199 crate::serial_force_println!(
200 "Sched: tid={} rt={} fair={} idle={} blocked={} init={}",
201 sched.current_task[cpu],
202 sched.rq_rt[cpu],
203 sched.rq_fair[cpu],
204 sched.rq_idle[cpu],
205 sched.blocked_tasks,
206 sched.initialized,
207 );
208 }
209 crate::serial_force_println!("");
210
211 let ts = crate::arch::boot_timestamp::elapsed_ms();
213 crate::serial_force_println!("Uptime: {} ms", ts);
214}
215
216pub fn install_default_panic_hooks() {
222 crate::e9_mark!(b'O');
223 let _ = register_panic_hook(panic_hook_dump_context);
224 crate::e9_mark!(b'o');
225}
226
227pub fn panic_handler(info: &PanicInfo) -> ! {
236 crate::e9_println!("[panic] {}", info.message());
238 crate::e9_println!("[panic] at {:?}", info.location());
239 crate::arch::serial::enter_emergency_mode();
241
242 if PANIC_IN_PROGRESS.swap(true, Ordering::SeqCst) {
244 crate::arch::cli();
246 crate::serial_force_println!("\n\x1b[31;1m!!! DOUBLE PANIC !!!\x1b[0m");
247 if let Some(loc) = info.location() {
248 crate::serial_force_println!("panic at {}:{}", loc.file(), loc.line());
249 }
250 crate::serial_force_println!("Message: {}", info.message());
251 let cpu = crate::arch::percpu::current_cpu_index();
252 let (cr0, cr2, cr3, cr4) = read_cr_regs();
253 crate::serial_force_println!(
254 "CPU={} CR0={:#X} CR2={:#X} CR3={:#X} CR4={:#X}",
255 cpu,
256 cr0,
257 cr2,
258 cr3,
259 cr4
260 );
261 crate::serial_force_println!("RSP={:#018X} RBP={:#018X}", read_rsp(), read_rbp());
262 dump_backtrace();
263 loop {
264 crate::arch::hlt();
265 }
266 }
267
268 crate::arch::cli();
270
271 crate::arch::speaker::beep_panic();
273
274 dump_panic_info(info);
276
277 run_panic_hooks(info);
279
280 crate::arch::smp::broadcast_panic_halt();
282 for _ in 0..100_000 {
285 core::hint::spin_loop();
286 }
287
288 crate::arch::vgabuf::vgabuf_flush_all();
290
291 if crate::arch::vga::is_available() {
293 if let Some(mut writer) = crate::arch::vga::VGA_WRITER.try_lock() {
294 use core::fmt::Write;
295 writer.set_rgb_color(
296 crate::arch::vga::RgbColor::new(0xFF, 0xE7, 0xA0),
297 crate::arch::vga::RgbColor::new(0x3A, 0x1F, 0x00),
298 );
299 writer.clear();
300 let _ = writeln!(writer, "=== GURU MEDiTATiON :: KERNEL PANiK ===");
301 if let Some(loc) = info.location() {
302 let _ = writeln!(
303 writer,
304 "File: {}:{}:{}",
305 loc.file(),
306 loc.line(),
307 loc.column()
308 );
309 }
310 let _ = writeln!(writer, "Message: {}", info.message());
311 let _ = writeln!(writer, "");
312 let _ = writeln!(writer, "See serial output for full backtrace.");
313 } else {
314 let title = "=== GURU MEDiTATiON :: KERNEL PANiK ===";
316 let serial_hint = "See serial output for full backtrace.";
317 let str_lines: [&str; 2] = [title, serial_hint];
318 crate::arch::vga::panic_draw_direct(&str_lines);
319 }
320 }
321
322 loop {
324 crate::arch::hlt();
325 }
326}