kernel/
panic_handler.rs

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
27// this should be 'core-local/thread-local', but that's okay, as we want to halt the whole kernel
28static 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        // reset panic count
160        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}