1use alloc::string::String;
2use core::{
3 panic::PanicInfo,
4 sync::atomic::{AtomicBool, Ordering},
5};
6use spin::Mutex;
7use x86_64::VirtAddr;
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 {
17 PANIC_IN_PROGRESS.load(Ordering::SeqCst)
18}
19
20pub fn register_panic_hook(hook: PanicHook) -> bool {
22 let mut hooks = PANIC_HOOKS.lock();
23 for slot in hooks.iter_mut() {
24 if slot.is_none() {
25 *slot = Some(hook);
26 return true;
27 }
28 }
29 false
30}
31
32fn run_panic_hooks(info: &PanicInfo) {
34 if let Some(hooks) = PANIC_HOOKS.try_lock() {
35 for hook in hooks.iter().flatten() {
36 hook(info);
37 }
38 }
39}
40
41fn panic_hook_dump_context(_info: &PanicInfo) {
43 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
44 let ticks = crate::process::scheduler::ticks();
45 let cr3 = crate::memory::paging::active_page_table().as_u64();
46 crate::serial_println!("panic-hook: cpu={} ticks={} cr3=0x{:x}", cpu, ticks, cr3);
47 if let Some(task) = crate::process::scheduler::current_task_clone_try() {
48 crate::serial_println!(
49 "panic-hook: current_task id={} name={}",
50 task.id.as_u64(),
51 task.name
52 );
53 } else {
54 crate::serial_println!("panic-hook: current_task none (scheduler locked or idle)");
55 }
56 let sched = crate::process::scheduler::state_snapshot();
57 if cpu < sched.cpu_count {
58 crate::serial_println!(
59 "panic-hook: sched cpu={} current_tid={} need_resched={} rq(rt/fair/idle)={}/{}/{} blocked={} init={} phase={}",
60 cpu,
61 sched.current_task[cpu],
62 sched.need_resched[cpu],
63 sched.rq_rt[cpu],
64 sched.rq_fair[cpu],
65 sched.rq_idle[cpu],
66 sched.blocked_tasks,
67 sched.initialized,
68 sched.boot_phase
69 );
70 }
71}
72
73#[inline(always)]
78fn read_rbp() -> u64 {
79 let rbp: u64;
80 unsafe {
81 core::arch::asm!("mov {}, rbp", out(reg) rbp, options(nomem, nostack, preserves_flags));
82 }
83 rbp
84}
85
86#[inline(always)]
87fn read_rsp() -> u64 {
88 let rsp: u64;
89 unsafe {
90 core::arch::asm!("mov {}, rsp", out(reg) rsp, options(nomem, nostack, preserves_flags));
91 }
92 rsp
93}
94
95fn addr_readable(addr: u64) -> bool {
96 crate::memory::paging::translate(VirtAddr::new(addr)).is_some()
97}
98
99fn read_cr_regs() -> (u64, u64, u64, u64) {
101 let cr0: u64;
102 let cr2: u64;
103 let cr3: u64;
104 let cr4: u64;
105 unsafe {
106 core::arch::asm!("mov {}, cr0", out(reg) cr0, options(nomem, nostack, preserves_flags));
107 core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nomem, nostack, preserves_flags));
108 core::arch::asm!("mov {}, cr3", out(reg) cr3, options(nomem, nostack, preserves_flags));
109 core::arch::asm!("mov {}, cr4", out(reg) cr4, options(nomem, nostack, preserves_flags));
110 }
111 (cr0, cr2, cr3, cr4)
112}
113
114fn collect_backtrace() -> alloc::vec::Vec<alloc::string::String> {
116 use alloc::format;
117 let mut lines = alloc::vec::Vec::new();
118 let mut rbp = read_rbp();
119 let rsp = read_rsp();
120 lines.push(format!("RSP=0x{:016X} RBP=0x{:016X}", rsp, rbp));
121 lines.push("Backtrace (frame-pointer):".into());
122
123 for i in 0..16 {
124 if rbp == 0 || (rbp & 0x7) != 0 {
125 lines.push(format!(" #{:02}: stop (invalid rbp)", i));
126 break;
127 }
128 if !addr_readable(rbp) || !addr_readable(rbp.saturating_add(8)) {
129 lines.push(format!(" #{:02}: stop (unmapped)", i));
130 break;
131 }
132
133 let prev = unsafe { *(rbp as *const u64) };
134 let ret = unsafe { *((rbp + 8) as *const u64) };
135 lines.push(format!(" #{:02}: RIP=0x{:016X}", i, ret));
136
137 if prev <= rbp || prev.saturating_sub(rbp) > 1024 * 1024 {
138 break;
139 }
140 rbp = prev;
141 }
142 lines
143}
144
145fn collect_panic_lines(info: &PanicInfo) -> alloc::vec::Vec<alloc::string::String> {
147 use alloc::format;
148 let mut lines = alloc::vec::Vec::new();
149
150 lines.push("=== GURU MEDITATION :: KERNEL PANIC ===".into());
152 lines.push(String::new());
153
154 if let Some(loc) = info.location() {
156 lines.push(format!(
157 "File: {}:{}:{}",
158 loc.file(),
159 loc.line(),
160 loc.column()
161 ));
162 } else {
163 lines.push("File: (unknown)".into());
164 }
165 lines.push(format!("Message: {}", info.message()));
166 lines.push(String::new());
167
168 let cpu = crate::arch::x86_64::percpu::current_cpu_index();
170 let (cr0, cr2, cr3, cr4) = read_cr_regs();
171 let rsp = read_rsp();
172 let rbp = read_rbp();
173
174 lines.push(format!("CPU={} CR0={:#X} CR2={:#X}", cpu, cr0, cr2));
175 lines.push(format!("CR3={:#X} CR4={:#X}", cr3, cr4));
176 lines.push(format!("RSP={:#018X} RBP={:#018X}", rsp, rbp));
177 lines.push(String::new());
178
179 let bt = collect_backtrace();
181 lines.extend(bt);
182 lines.push(String::new());
183
184 let ticks = crate::process::scheduler::ticks();
186 lines.push(format!("Ticks={}", ticks));
187 if let Some(task) = crate::process::scheduler::current_task_clone_try() {
188 lines.push(format!("Task: id={} name={}", task.id.as_u64(), task.name));
189 } else {
190 lines.push("Task: (scheduler locked / idle)".into());
191 }
192
193 let sched = crate::process::scheduler::state_snapshot();
194 if cpu < sched.cpu_count {
195 lines.push(format!(
196 "Sched: tid={} rt={} fair={} idle={} blocked={} init={}",
197 sched.current_task[cpu],
198 sched.rq_rt[cpu],
199 sched.rq_fair[cpu],
200 sched.rq_idle[cpu],
201 sched.blocked_tasks,
202 sched.initialized,
203 ));
204 }
205 lines.push(String::new());
206
207 let ts = crate::arch::x86_64::boot_timestamp::elapsed_ms();
209 lines.push(format!("Uptime: {} ms", ts));
210
211 lines
212}
213
214fn panic_serial_dump(lines: &[alloc::string::String]) {
216 crate::serial_println!("\n\x1b[31;1m!!! KERNEL PANIC !!!\x1b[0m");
217 for line in lines {
218 crate::serial_println!("{}", line);
219 }
220}
221
222pub fn install_default_panic_hooks() {
228 let _ = register_panic_hook(panic_hook_dump_context);
229}
230
231pub fn panic_handler(info: &PanicInfo) -> ! {
240 crate::arch::x86_64::serial::enter_emergency_mode();
242
243 if PANIC_IN_PROGRESS.swap(true, Ordering::SeqCst) {
245 loop {
246 crate::arch::x86_64::hlt();
247 }
248 }
249
250 crate::arch::x86_64::cli();
252
253 crate::arch::x86_64::speaker::beep_panic();
255
256 let lines = collect_panic_lines(info);
258
259 panic_serial_dump(&lines);
261
262 run_panic_hooks(info);
264
265 crate::arch::x86_64::smp::broadcast_panic_halt();
267
268 crate::arch::x86_64::vgabuf::vgabuf_flush_all();
270
271 if crate::arch::x86_64::vga::is_available() {
273 let writer_locked = {
275 if let Some(mut writer) = crate::arch::x86_64::vga::VGA_WRITER.try_lock() {
276 use core::fmt::Write;
277 writer.set_rgb_color(
278 crate::arch::x86_64::vga::RgbColor::new(0xFF, 0xE7, 0xA0),
279 crate::arch::x86_64::vga::RgbColor::new(0x3A, 0x1F, 0x00),
280 );
281 writer.clear();
282 for line in &lines {
283 let _ = writeln!(writer, "{}", line);
284 }
285 true
286 } else {
287 false
288 }
289 };
290
291 if !writer_locked {
292 let str_lines: alloc::vec::Vec<&str> = lines.iter().map(|s| s.as_str()).collect();
295 crate::arch::x86_64::vga::panic_draw_direct(&str_lines);
296 }
297 }
298
299 loop {
301 crate::arch::x86_64::hlt();
302 }
303}