1use core::{
2 any::Any,
3 ffi::c_void,
4 panic::PanicInfo,
5 sync::atomic::{AtomicI32, Ordering},
6};
7
8use alloc::boxed::Box;
9use alloc::{string::String, vec::Vec};
10use framehop::{
11 x86_64::{CacheX86_64, UnwindRegsX86_64, UnwinderX86_64},
12 ExplicitModuleSectionInfo, Module, Unwinder,
13};
14use unwinding::abi::{UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_GetIP};
15
16use kernel_user_link::process::process_metadata;
17
18use crate::{
19 cpu::{self, idt::InterruptStackFrame64},
20 hw::qemu,
21 memory_management::memory_layout::{
22 eh_frame_end, eh_frame_start, kernel_elf_end, kernel_text_end, KERNEL_LINK,
23 },
24 process::scheduler::with_current_process,
25};
26
27static PANIC_COUNT: AtomicI32 = AtomicI32::new(0);
29
30pub fn print_kernel_stack_trace(rip: u64, rsp: u64, rbp: u64) {
31 cpu::cpu().push_cli();
32
33 let mut cache = CacheX86_64::<_>::new();
34 let mut unwinder = UnwinderX86_64::new();
35
36 let module = Module::new(
37 String::from("kernel"),
38 KERNEL_LINK as u64..kernel_elf_end() as u64,
39 KERNEL_LINK as u64,
40 ExplicitModuleSectionInfo {
41 base_svma: KERNEL_LINK as _,
42 text_svma: Some(KERNEL_LINK as _..kernel_text_end() as _),
43 text: Some(unsafe {
44 core::slice::from_raw_parts(KERNEL_LINK as _, kernel_text_end() - KERNEL_LINK)
45 }),
46 eh_frame_svma: Some(eh_frame_start() as _..eh_frame_end() as _),
47 eh_frame: Some(unsafe {
48 core::slice::from_raw_parts(
49 eh_frame_start() as _,
50 eh_frame_end() - eh_frame_start(),
51 )
52 }),
53 ..Default::default()
54 },
55 );
56 unwinder.add_module(module);
57
58 let mut read_stack = |addr| Ok(unsafe { (addr as *const u64).read_volatile() });
59
60 let mut iter = unwinder.iter_frames(
61 rip,
62 UnwindRegsX86_64::new(rip, rsp, rbp),
63 &mut cache,
64 &mut read_stack,
65 );
66
67 println!("Stack trace:");
68 let mut i = 0;
69 let mut frames = Vec::new();
70 while let Ok(Some(frame)) = iter.next() {
71 println!("{i:4}:{:#19x}", frame.address());
72 frames.push(frame.address());
73 i += 1;
74 }
75
76 print!("You can use this command to get information about the trace (since we don't have debug symbols here):\n$ addr2line -f -C -e ");
77 #[cfg(debug_assertions)]
78 print!("./target/x86-64-os/debug/kernel");
79 #[cfg(not(debug_assertions))]
80 print!("./target/x86-64-os/release/kernel");
81 for frame in frames.iter() {
82 print!(" {:#x}", frame);
83 }
84 println!();
85
86 cpu::cpu().pop_cli();
87}
88
89pub fn print_process_stack_trace(frame: &InterruptStackFrame64, rbp: u64) {
90 cpu::cpu().push_cli();
91
92 assert_eq!(frame.cs & 0x3, 3, "We are in user mode");
93
94 let meta = process_metadata();
95
96 let module = Module::new(
97 String::from("exe"),
98 meta.image_base as _..(meta.image_base + meta.image_size) as _,
99 meta.image_base as _,
100 ExplicitModuleSectionInfo {
101 base_svma: meta.image_base as _,
102 text_svma: Some(meta.text_address as _..(meta.text_address + meta.text_size) as _),
103 text: Some(unsafe {
104 core::slice::from_raw_parts(meta.text_address as _, meta.text_size)
105 }),
106 eh_frame_svma: Some(
107 meta.eh_frame_address as _..(meta.eh_frame_address + meta.eh_frame_size) as _,
108 ),
109 eh_frame: Some(unsafe {
110 core::slice::from_raw_parts(meta.eh_frame_address as _, meta.eh_frame_size)
111 }),
112 ..Default::default()
113 },
114 );
115 let mut cache = CacheX86_64::new();
116 let mut unwinder: UnwinderX86_64<&[u8]> = UnwinderX86_64::new();
117 unwinder.add_module(module);
118
119 let mut read_stack = |addr| Ok(unsafe { (addr as *const u64).read_volatile() });
120
121 let mut iter = unwinder.iter_frames(
122 frame.rip as _,
123 UnwindRegsX86_64::new(frame.rip as _, frame.rsp as _, rbp as _),
124 &mut cache,
125 &mut read_stack,
126 );
127
128 println!("Stack trace:");
129 let mut i = 0;
130 let mut frames = Vec::new();
131 while let Ok(Some(frame)) = iter.next() {
132 println!("{i:4}:{:#19x}", frame.address());
133 frames.push(frame.address());
134 i += 1;
135 }
136
137 with_current_process(|process| {
138 print!("You can use this command to get information about the trace (since we don't have debug symbols here):\n$ addr2line -f -C -e ./filesystem{}", process.file_path().as_str());
139 });
140 for frame in frames.iter() {
141 print!(" {:#x}", frame);
142 }
143 println!();
144
145 cpu::cpu().pop_cli();
146}
147
148pub fn print_originating_stack_trace(frame: &InterruptStackFrame64, rbp: u64) {
149 if frame.cs & 0x3 == 3 {
150 print_process_stack_trace(frame, rbp);
151 } else {
152 print_kernel_stack_trace(frame.rip, frame.rsp, rbp);
153 }
154}
155
156#[allow(dead_code)]
157pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
158 unwinding::panic::catch_unwind(f).inspect_err(|_e| {
159 PANIC_COUNT.store(0, Ordering::Relaxed);
161 })
162}
163
164fn stack_trace() {
165 cpu::cpu().push_cli();
166 struct CallbackData {
167 counter: usize,
168 }
169 extern "C" fn callback(unwind_ctx: &UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode {
170 let data = unsafe { &mut *(arg as *mut CallbackData) };
171 data.counter += 1;
172 println!("{:4}:{:#19x}", data.counter, _Unwind_GetIP(unwind_ctx));
173 UnwindReasonCode::NO_REASON
174 }
175 let mut data = CallbackData { counter: 0 };
176 _Unwind_Backtrace(callback, &mut data as *mut _ as _);
177
178 print!("You can use this command to get information about the trace (since we don't have debug symbols here):\n$ addr2line -f -C -e ");
179 #[cfg(debug_assertions)]
180 print!("./target/x86-64-os/debug/kernel");
181 #[cfg(not(debug_assertions))]
182 print!("./target/x86-64-os/release/kernel");
183 extern "C" fn callback2(unwind_ctx: &UnwindContext<'_>, _arg: *mut c_void) -> UnwindReasonCode {
184 print!(" {:#x}", _Unwind_GetIP(unwind_ctx));
185 UnwindReasonCode::NO_REASON
186 }
187 _Unwind_Backtrace(callback2, core::ptr::null_mut() as _);
188 println!("\nhalting...");
189
190 cpu::cpu().pop_cli();
191}
192
193fn panic_trace(msg: Box<dyn Any + Send>) -> ! {
194 if PANIC_COUNT.load(Ordering::Relaxed) >= 1 {
195 stack_trace();
196 println!("thread panicked while processing panic. halting...");
197
198 qemu::exit(qemu::ExitStatus::Failure);
199 }
200 PANIC_COUNT.store(1, Ordering::Relaxed);
201 stack_trace();
202
203 let code = unwinding::panic::begin_panic(Box::new(msg));
204 println!(
205 "failed to initiate panic, maybe, no one is catching it?. got code: {} halting...",
206 code.0
207 );
208
209 qemu::exit(qemu::ExitStatus::Failure);
210}
211
212#[panic_handler]
213fn panic(info: &PanicInfo<'_>) -> ! {
214 unsafe { cpu::clear_interrupts() };
215 println!("{}", info);
216
217 struct NoPayload;
218 panic_trace(Box::new(NoPayload))
219}