ostd/
panic.rs

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