I'm working on a hobby RISC-V OS kernel (QEMU virt) in no_std
Rust and want my panic_handler
to print the panic information to UART (is it the right way?).
My println!
macro eventually calls a _print
function that locks a Spinlock
to get exclusive access to the UART registers.
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
let mut guard = UART_INSTANCE.lock();
guard
.write_fmt(args)
.map_err(|e| {
drop(guard);
panic!("UART write error: {}", e);
})
.ok();
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
However, if a piece of code already has the UART locked and then panics, my panic_handler
will try to lock the same UART to print the panic message. Since the original lock is never released (the code panicked), my panic_handler
deadlocks on the spinlock.
pub struct Spinlock<T> {
locked: AtomicBool,
data: UnsafeCell<T>,
}
impl<T> Spinlock<T> {
pub const fn new(data: T) -> Self {
Self {
locked: AtomicBool::new(false),
data: UnsafeCell::new(data),
}
}
pub fn lock(&self) -> SpinlockGuard<'_, T> {
while self
.locked
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
SpinlockGuard { lock: self }
}
fn unlock(&self) {
self.locked.store(false, Ordering::Release);
}
}
*Note that I have a spinlock guard that call unlock
on drop.
#[panic_handler]
fn _panic(info: &PanicInfo) -> ! {
println!("KERNEL PANIC: {}", info);
halt();
}
What is the strategy to properly handle this?
If your OS is single threaded (or cooperative multithreaded), since that thread has panicked you know it won't release (or use) the UART ever again. So you can just steal it, assuming it is yours.
If it is preemptive multi-threaded, you can't assume that, as one thread may panic while another one is holding the UART lock. In that case, a possible approach is to store a thread ID while claiming the lock; then, when panicking, try to claim it if it is held by another thread, and if it is held by the current thread, just steal it.