Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ starry-vm = "0.2"

starry-core = { path = "./core" }
starry-api = { path = "./api" }
axmacros = { git = "https://github.com/Starry-OS/axmacros" }

[package]
name = "starry"
Expand Down Expand Up @@ -131,6 +132,15 @@ starry-signal.workspace = true

starry-core.workspace = true
starry-api.workspace = true
axmacros.workspace = true

[target.'cfg(not(target_arch = "loongarch64"))'.dependencies]
unwinding = { version = "=0.2.8", default-features = false, features = [
"unwinder",
"fde-gnu-eh-frame-hdr",
"panic",
"personality",
] }

[dependencies.axplat-riscv64-visionfive2]
git = "https://github.com/Starry-OS/axplat-riscv64-visionfive2.git"
Expand Down
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![feature(panic_can_unwind)]
#![doc = include_str!("../README.md")]

#[macro_use]
Expand All @@ -13,13 +14,17 @@ use alloc::{borrow::ToOwned, vec::Vec};
use axfs_ng::FS_CONTEXT;

mod entry;
mod panic;

pub const CMDLINE: &[&str] = &["/bin/sh", "-c", include_str!("init.sh")];

#[unsafe(no_mangle)]
#[axmacros::main]
fn main() {
starry_api::init();

#[cfg(not(target_arch = "loongarch64"))]
panic::test_unwind();

let args = CMDLINE
.iter()
.copied()
Expand Down
148 changes: 148 additions & 0 deletions src/panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use core::panic::PanicInfo;

#[cfg(not(target_arch = "loongarch64"))]
#[allow(unused)]
pub use unwind::{KPanicInfo, catch_panics_as_oops};

#[cfg(target_arch = "loongarch64")]
#[axmacros::panic_handler]
fn panic(info: &PanicInfo) -> ! {
ax_println!("{}", info);
axhal::power::system_off()
}

#[cfg(not(target_arch = "loongarch64"))]
mod unwind {
use alloc::boxed::Box;
use core::{
ffi::c_void,
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};

use unwinding::{
abi::{
_Unwind_Backtrace, _Unwind_FindEnclosingFunction, _Unwind_GetIP, UnwindContext,
UnwindReasonCode,
},
panic,
};

use super::PanicInfo;

static RECURSION: AtomicBool = AtomicBool::new(false);
#[derive(Debug)]
pub struct KPanicInfo;

impl KPanicInfo {
pub fn new() -> Self {
Self
}
}

#[axmacros::panic_handler]
fn panic_handler(info: &PanicInfo) -> ! {
if let Some(p) = info.location() {
ax_println!("line {}, file {}: {}", p.line(), p.file(), info.message());
} else {
ax_println!("no location information available");
}
if !RECURSION.swap(true, core::sync::atomic::Ordering::SeqCst) {
if info.can_unwind() {
let guard = Box::new(KPanicInfo::new());
print_stack_trace();
let _res = unwinding::panic::begin_panic(guard);
panic!("panic unreachable: {:?}", _res.0);
}
}
axhal::power::system_off()
}

pub fn print_stack_trace() {
ax_println!("Rust Panic Backtrace:");
struct CallbackData {
counter: usize,
kernel_main: bool,
}
extern "C" fn callback(
unwind_ctx: &UnwindContext<'_>,
arg: *mut c_void,
) -> UnwindReasonCode {
let data = unsafe { &mut *(arg as *mut CallbackData) };
if data.kernel_main {
// If we are in kernel_main, we don't need to print the backtrace.
return UnwindReasonCode::NORMAL_STOP;
}
data.counter += 1;
let pc = _Unwind_GetIP(unwind_ctx);
if pc > 0 {
let fde_initial_address = _Unwind_FindEnclosingFunction(pc as *mut c_void) as usize;
// TODO: lookup_kallsyms
ax_println!(
"#{:<2} {:#018x} - <unknown> + {:#x}",
data.counter,
pc,
pc - fde_initial_address
);
}
UnwindReasonCode::NO_REASON
}
let mut data = CallbackData {
counter: 0,
kernel_main: false,
};
_Unwind_Backtrace(callback, &mut data as *mut _ as _);
}

/// The maximum number of oops allowed before the kernel panics.
///
/// It is the same as Linux's default value.
const MAX_OOPS_COUNT: usize = 10_000;

static OOPS_COUNT: AtomicUsize = AtomicUsize::new(0);

/// Catch panics in the given closure and treat them as kernel oops.
pub fn catch_panics_as_oops<F, R>(f: F) -> Result<R, KPanicInfo>
where
F: FnOnce() -> R,
{
let result = panic::catch_unwind(f);

match result {
Ok(result) => Ok(result),
Err(err) => {
let info = err.downcast::<KPanicInfo>().unwrap();

let count = OOPS_COUNT.fetch_add(1, Ordering::Relaxed);
if count >= MAX_OOPS_COUNT {
// Too many oops. Abort the kernel.
axlog::error!("Too many oops. The kernel panics.");
axhal::power::system_off();
}
Err(*info)
}
}
}
}

#[cfg(not(target_arch = "loongarch64"))]
pub fn test_unwind() {
struct UnwindTest;
impl Drop for UnwindTest {
fn drop(&mut self) {
ax_println!("Drop UnwindTest");
}
}
let res1 = catch_panics_as_oops(|| {
let _unwind_test = UnwindTest;
ax_println!("Test panic...");
panic!("Test panic");
});
assert!(res1.is_err());
let res2 = catch_panics_as_oops(|| {
let _unwind_test = UnwindTest;
ax_println!("Test no panic...");
0
});
assert!(res2.is_ok());
ax_println!("Unwind test passed.");
}
Loading