Skip to main content

kernel/lib/
panic.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Kernel panic paths: [`kernel_panic`] for explicit kernel panics and the
3//! required `#[panic_handler]` for language-level panics.
4//!
5//! Authors: MarioS271
6
7use crate::cpu::instructions;
8use crate::lib::panic_codes::PanicCode;
9use crate::lib::types::fmt_buffer::FmtBuffer;
10use crate::SIMPLE_STATE;
11use core::panic::PanicInfo;
12use core::sync::atomic::{AtomicU8, Ordering};
13
14// TODO: respect SIMPLE_STATE.panic_action instead of hardcoded halt
15
16/// Used to prevent infinite panic handler reentry
17static PANIC_TRIGGERED: AtomicU8 = AtomicU8::new(0);
18
19/// Enum helper to not have to write the raw bitshifts on every PANIC_TRIGGERED interaction
20#[repr(u8)]
21enum PanicTriggered {
22    WasKernelPanicTriggered = 1 << 0,
23    WasRustPanicTriggered = 1 << 1
24}
25
26/// Halt the kernel with a diagnostic message; re-entrant calls skip straight to the halt loop.
27#[cold]
28pub fn kernel_panic(panic_code: PanicCode, panic_message: &str) -> ! {
29    instructions::disable_interrupts();
30
31    if PANIC_TRIGGERED.load(Ordering::Acquire) & PanicTriggered::WasKernelPanicTriggered as u8 != 0 {
32        core::hint::cold_path();
33        loop {
34            instructions::halt_cpu();
35        }
36    }
37    PANIC_TRIGGERED.fetch_or(PanicTriggered::WasKernelPanicTriggered as u8, Ordering::AcqRel);
38
39    // Safety: We acknowledge the risk of corrupted output, which isn't too bad considering we already panic'd
40    unsafe { force_unlock_loggers(); }
41
42    if SIMPLE_STATE.is_uart_initialized() {
43        use crate::drivers::tty::uart::UartOps;
44
45        // Safety: we check above if uart is already initialized, making this safe
46        let mut uart = unsafe { SIMPLE_STATE.uart().lock() };
47
48        let _ = uart.write_string("\nKernel Panic! :(\n");
49        let _ = uart.write_string("Panic Code: ");
50        let _ = uart.write_string(panic_code.as_str());
51        let _ = uart.write_string("\nPanic Type: ");
52        let _ = uart.write_string(panic_code.get_error_type_str());
53        let _ = uart.write_string("\n\n");
54        let _ = uart.write_string(panic_message);
55    }
56
57    if SIMPLE_STATE.is_basic_fb_initialized() && SIMPLE_STATE.is_basic_fb_psf2_font_initialized() {
58        // Safety: we check above if basic_fb is already initialized, making this safe
59        let fb = unsafe { SIMPLE_STATE.basic_fb().lock() };
60        // Safety: we check above if basic_fb_psf2_font is already initialized, making this safe
61        let font = unsafe { SIMPLE_STATE.basic_fb_psf2_font() };
62
63        let mut x: usize = 0;
64        let mut y: usize = 0;
65
66        fb.clear();
67        font.draw_string(&*fb, "Kernel Panic! :(\n", &mut x, &mut y, Some(0x00FF0000));
68        font.draw_string(&*fb, "Panic Code: ", &mut x, &mut y, None);
69        font.draw_string(&*fb, panic_code.as_str(), &mut x, &mut y, None);
70        font.draw_string(&*fb, "\nPanic Type: ", &mut x, &mut y, None);
71        font.draw_string(&*fb, panic_code.get_error_type_str(), &mut x, &mut y, None);
72        font.draw_string(&*fb, "\n\n", &mut x, &mut y, None);
73        font.draw_string(&*fb, panic_message, &mut x, &mut y, None);
74    }
75
76    instructions::halt_forever();
77}
78
79/// Rust's required `#[panic_handler]` for language-level panics.
80#[panic_handler]
81fn panic(panic_info: &PanicInfo) -> ! {
82    use core::fmt::Write;
83
84    instructions::disable_interrupts();
85
86    if PANIC_TRIGGERED.load(Ordering::Acquire) & PanicTriggered::WasRustPanicTriggered as u8 != 0 {
87        core::hint::cold_path();
88        loop {
89            instructions::halt_cpu();
90        }
91    }
92    PANIC_TRIGGERED.fetch_or(PanicTriggered::WasRustPanicTriggered as u8, Ordering::AcqRel);
93
94    // Safety: We acknowledge the risk of corrupted output, which isn't too bad considering we already panic'd
95    unsafe { force_unlock_loggers(); }
96
97    let mut message_buf: FmtBuffer<512> = FmtBuffer::new();
98    if panic_info.message().as_str().is_none() {
99        let _ = write!(message_buf, "(No panic message given)\n\n");
100    } else {
101        let _ = write!(message_buf, "Panic Message: {}\n\n", panic_info.message());
102    }
103
104    let mut location_buf: FmtBuffer<256> = FmtBuffer::new();
105    if panic_info.location().is_none() {
106        let _ = write!(location_buf, "(No panic location given)");
107    } else {
108        let location = panic_info.location().unwrap();
109        let _ = write!(location_buf, "{}\n  on line {}", location.file(), location.line());
110    }
111
112    if SIMPLE_STATE.is_uart_initialized() {
113        use crate::drivers::tty::uart::UartOps;
114
115        // Safety: we check above if uart is already initialized, making this safe
116        let mut uart = unsafe { SIMPLE_STATE.uart().lock() };
117
118        let _ = uart.write_string("\nKernel Panic! :(\n");
119        let _ = uart.write_string("[!] This panic was triggered by Rust (runtime error or panic!() called)\n\n");
120        let _ = uart.write_string(message_buf.as_str());
121        let _ = uart.write_string(location_buf.as_str());
122    }
123
124    if SIMPLE_STATE.is_basic_fb_initialized() && SIMPLE_STATE.is_basic_fb_psf2_font_initialized() {
125        // Safety: we check above if basic_fb is already initialized, making this safe
126        let fb = unsafe { SIMPLE_STATE.basic_fb().lock() };
127        // Safety: we check above if basic_fb_psf2_font is already initialized, making this safe
128        let font = unsafe { SIMPLE_STATE.basic_fb_psf2_font() };
129
130        let mut x: usize = 0;
131        let mut y: usize = 0;
132
133        fb.clear();
134        font.draw_string(&*fb, "Kernel Panic! :(\n", &mut x, &mut y, Some(0x00FF0000));
135        font.draw_string(&*fb, "[!] This panic was triggered by Rust (runtime error or panic!() called)\n\n", &mut x, &mut y, None);
136        font.draw_string(&*fb, message_buf.as_str(), &mut x, &mut y, None);
137        font.draw_string(&*fb, location_buf.as_str(), &mut x, &mut y, None);
138    }
139
140    instructions::halt_forever();
141}
142
143/// Force-unlocks the kernel serial logger and the basic_fb framebuffer
144///
145/// # Safety
146/// Calling this will force-unlock the serial and basic_fb `IrqMutex`es, which can result in
147/// corrupted output. This should only be called from irrecoverable scenarios such as a kernel panic.
148unsafe fn force_unlock_loggers() {
149    unsafe {
150        if SIMPLE_STATE.is_uart_initialized() {
151            SIMPLE_STATE.uart().force_unlock();
152        }
153        if SIMPLE_STATE.is_basic_fb_initialized() && SIMPLE_STATE.is_basic_fb_psf2_font_initialized() {
154            SIMPLE_STATE.basic_fb().force_unlock();
155        }
156    }
157}