Skip to main content

ostd/
panic.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Panic support.
4
5use crate::early_println;
6
7extern crate gimli;
8
9/// The default panic handler for OSTD based kernels.
10///
11/// The user can override it by defining their own panic handler with the macro
12/// `#[ostd::panic_handler]`.
13#[linkage = "weak"]
14// SAFETY: The name does not collide with other symbols.
15#[unsafe(no_mangle)]
16pub fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! {
17    let _irq_guard = crate::irq::disable_local();
18
19    crate::cpu_local_cell! {
20        static IN_PANIC: bool = false;
21    }
22
23    if IN_PANIC.load() {
24        early_println!("The panic handler panicked {:#?}", info);
25        abort();
26    }
27
28    IN_PANIC.store(true);
29
30    early_println!("Non-resettable panic! {:#?}", info);
31
32    print_stack_trace();
33    abort();
34}
35
36/// Aborts the system.
37///
38/// This function will first attempt to power off the system. If that fails, it will halt all CPUs.
39pub fn abort() -> ! {
40    // TODO: The main purpose of powering off here is to allow QEMU to exit. Otherwise, the CI may
41    // freeze after panicking. However, this is unnecessary and may prevent debugging on a real
42    // machine (i.e., the message will disappear afterward).
43    crate::power::poweroff(crate::power::ExitCode::Failure);
44}
45
46/// A guard that aborts the system if dropped.
47///
48/// This is useful to ensure that certain objects will not be dropped during
49/// panic handling.
50#[derive(Debug)]
51pub(crate) struct PanicGuard {
52    _private: (),
53}
54
55impl Drop for PanicGuard {
56    fn drop(&mut self) {
57        early_println!("Panicked in `PanicGuard`, aborting the system");
58        abort();
59    }
60}
61
62impl PanicGuard {
63    /// Creates a panic guard that aborts the system if dropped.
64    pub(crate) fn new() -> Self {
65        PanicGuard { _private: () }
66    }
67
68    /// Finishes panic guarding by forgetting the guard.
69    ///
70    /// After the panic guarding finishes, it no longer aborts the system
71    /// when panic happens.
72    pub(crate) fn forget(self) {
73        core::mem::forget(self);
74    }
75}
76
77#[cfg(not(target_arch = "loongarch64"))]
78pub use unwinding::panic::{begin_panic, catch_unwind};
79
80/// Prints the stack trace of the current thread to the console.
81///
82/// The printing procedure is protected by a spin lock to prevent interleaving.
83#[cfg(not(target_arch = "loongarch64"))]
84pub fn print_stack_trace() {
85    use core::ffi::c_void;
86
87    use gimli::Register;
88    use unwinding::abi::{
89        _Unwind_Backtrace, _Unwind_FindEnclosingFunction, _Unwind_GetGR, _Unwind_GetIP,
90        UnwindContext, UnwindReasonCode,
91    };
92
93    use crate::{early_print, sync::SpinLock};
94
95    /// We acquire a global lock to prevent the frames in the stack trace from
96    /// interleaving. The spin lock is used merely for its simplicity.
97    static BACKTRACE_PRINT_LOCK: SpinLock<()> = SpinLock::new(());
98    let _lock = BACKTRACE_PRINT_LOCK.lock();
99
100    early_println!("Printing stack trace:");
101
102    struct CallbackData {
103        counter: usize,
104    }
105    extern "C" fn callback(unwind_ctx: &UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode {
106        let data = unsafe { &mut *(arg as *mut CallbackData) };
107        data.counter += 1;
108        let pc = _Unwind_GetIP(unwind_ctx);
109        if pc > 0 {
110            let fde_initial_address = _Unwind_FindEnclosingFunction(pc as *mut c_void) as usize;
111            early_println!(
112                "{:4}: fn {:#18x} - pc {:#18x} / registers:",
113                data.counter,
114                fde_initial_address,
115                pc,
116            );
117        }
118        // Print the first 8 general registers for any architecture. The register number follows
119        // the DWARF standard.
120        for i in 0..8u16 {
121            let reg_name = cfg_select! {
122                target_arch = "x86_64" => gimli::X86_64::register_name(Register(i)),
123                target_arch = "riscv64" => gimli::RiscV::register_name(Register(i)),
124                target_arch = "aarch64" => gimli::AArch64::register_name(Register(i)),
125                _ => None,
126            };
127            let reg_val = _Unwind_GetGR(unwind_ctx, i as i32);
128            if i.is_multiple_of(4) {
129                early_print!("\n    ");
130            }
131            early_print!(" {} {:#18x};", reg_name.unwrap_or("unknown"), reg_val);
132        }
133        early_print!("\n\n");
134        UnwindReasonCode::NO_REASON
135    }
136
137    let mut data = CallbackData { counter: 0 };
138    _Unwind_Backtrace(callback, &mut data as *mut _ as _);
139}
140
141/// Catches unwinding panics.
142#[cfg(target_arch = "loongarch64")]
143pub fn catch_unwind<R, F: FnOnce() -> R>(
144    f: F,
145) -> Result<R, alloc::boxed::Box<dyn core::any::Any + Send>> {
146    // TODO: Support unwinding in LoongArch.
147    Ok(f())
148}
149
150/// Begins panic handling
151#[cfg(target_arch = "loongarch64")]
152pub fn begin_panic<R>(_: alloc::boxed::Box<R>) {
153    // TODO: Support panic context in LoongArch.
154}
155
156/// Prints the stack trace of the current thread to the console.
157#[cfg(target_arch = "loongarch64")]
158pub fn print_stack_trace() {
159    // TODO: Support stack trace print in LoongArch.
160    early_println!("Printing stack trace:");
161}