diff --git a/Cargo.lock b/Cargo.lock index 437bfe616..5bf9146d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,7 +282,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -344,7 +344,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -514,7 +514,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -549,7 +549,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "unicode-xid", ] @@ -655,7 +655,7 @@ checksum = "ba330b70a5341d3bc730b8e205aaee97ddab5d9c448c4f51a7c2d924266fa8f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -755,7 +755,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1034,7 +1034,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1088,7 +1088,6 @@ version = "0.9.3" dependencies = [ "ostd", "owo-colors 4.0.0", - "unwinding", ] [[package]] @@ -1141,7 +1140,7 @@ dependencies = [ "proc-macro2", "quote", "rand", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1369,7 +1368,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1456,9 +1455,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1507,7 +1506,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1621,7 +1620,7 @@ checksum = "c19ee3a01d435eda42cb9931269b349d28a1762f91ddf01c68d276f74b957cc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1809,5 +1808,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] diff --git a/osdk/src/base_crate/main.rs.template b/osdk/src/base_crate/main.rs.template index fa2e00d3b..9b5503858 100644 --- a/osdk/src/base_crate/main.rs.template +++ b/osdk/src/base_crate/main.rs.template @@ -6,7 +6,7 @@ extern crate #TARGET_NAME#; #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { extern "Rust" { - pub fn __aster_panic_handler(info: &core::panic::PanicInfo) -> !; + pub fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> !; } - unsafe { __aster_panic_handler(info); } + unsafe { __ostd_panic_handler(info); } } diff --git a/osdk/test-kernel/Cargo.toml b/osdk/test-kernel/Cargo.toml index b35610120..36748187b 100644 --- a/osdk/test-kernel/Cargo.toml +++ b/osdk/test-kernel/Cargo.toml @@ -11,4 +11,3 @@ repository ="https://github.com/asterinas/asterinas" [dependencies] ostd = { version = "0.9.3", path = "../../ostd" } owo-colors = "4.0.0" -unwinding = { version = "0.2.3", default-features = false, features = ["fde-gnu-eh-frame-hdr", "hide-trace", "panic", "personality", "unwinder"] } diff --git a/osdk/test-kernel/src/lib.rs b/osdk/test-kernel/src/lib.rs index 164ce20ce..a02f0790c 100644 --- a/osdk/test-kernel/src/lib.rs +++ b/osdk/test-kernel/src/lib.rs @@ -16,7 +16,7 @@ use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec}; use core::{any::Any, format_args}; use ostd::{ - early_print, + early_print, early_println, ktest::{ get_ktest_crate_whitelist, get_ktest_test_whitelist, KtestError, KtestItem, KtestIter, }, @@ -54,11 +54,32 @@ fn main() { unreachable!("The spawn method will NOT return in the boot context") } +#[ostd::ktest::panic_handler] +fn panic_handler(info: &core::panic::PanicInfo) -> ! { + let _irq_guard = ostd::trap::disable_local(); + + use alloc::{boxed::Box, string::ToString}; + + use ostd::panic::begin_panic; + + let throw_info = ostd::ktest::PanicInfo { + message: info.message().to_string(), + file: info.location().unwrap().file().to_string(), + line: info.location().unwrap().line() as usize, + col: info.location().unwrap().column() as usize, + }; + + // Throw an exception and expecting it to be caught. + begin_panic(Box::new(throw_info.clone())); + + // If not caught, abort the kernel. + early_println!("An uncaught panic occurred: {:#?}", throw_info); + + ostd::prelude::abort(); +} + /// Run all the tests registered by `#[ktest]` in the `.ktest_array` section. /// -/// Need to provide a print function `print` to print the test result, and a `catch_unwind` -/// implementation to catch the panic. -/// /// The `whitelist` argument is optional. If it is `None`, all tests compiled will be run. /// If it is `Some`, only the tests whose test path being the suffix of any paths in the whitelist /// will be run. @@ -129,7 +150,7 @@ fn run_crate_ktests(crate_: &KtestCrate, whitelist: &Option) -> Ktes ); debug_assert_eq!(test.info().package, crate_name); match test.run( - &(unwinding::panic::catch_unwind::<(), fn()> + &(ostd::panic::catch_unwind::<(), fn()> as fn(fn()) -> Result<(), Box<(dyn Any + Send + 'static)>>), ) { Ok(()) => { diff --git a/ostd/libs/ostd-macros/src/lib.rs b/ostd/libs/ostd-macros/src/lib.rs index 869d75a18..7c5c57c97 100644 --- a/ostd/libs/ostd-macros/src/lib.rs +++ b/ostd/libs/ostd-macros/src/lib.rs @@ -31,8 +31,6 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream { #[no_mangle] #[linkage = "weak"] extern "Rust" fn __ostd_main() -> ! { - // SAFETY: The function is called only once on the BSP. - unsafe { ostd::init() }; #main_fn_name(); ostd::prelude::abort(); } @@ -58,8 +56,6 @@ pub fn test_main(_attr: TokenStream, item: TokenStream) -> TokenStream { quote!( #[no_mangle] extern "Rust" fn __ostd_main() -> ! { - // SAFETY: The function is called only once on the BSP. - unsafe { ostd::init() }; #main_fn_name(); ostd::prelude::abort(); } @@ -69,6 +65,49 @@ pub fn test_main(_attr: TokenStream, item: TokenStream) -> TokenStream { .into() } +/// A macro attribute for the panic handler. +/// +/// The attributed function will be used to override OSTD's default +/// implementation of Rust's `#[panic_handler]`. The function takes a single +/// parameter of type `&core::panic::PanicInfo` and does not return. +#[proc_macro_attribute] +pub fn panic_handler(_attr: TokenStream, item: TokenStream) -> TokenStream { + let handler_fn = parse_macro_input!(item as ItemFn); + let handler_fn_name = &handler_fn.sig.ident; + + quote!( + #[cfg(not(ktest))] + #[no_mangle] + extern "Rust" fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! { + #handler_fn_name(info); + } + + #[cfg(not(ktest))] + #handler_fn + ) + .into() +} + +/// A macro attribute for the panic handler. +/// +/// This macro is used for internal OSDK implementation. Do not use it +/// directly. +#[proc_macro_attribute] +pub fn test_panic_handler(_attr: TokenStream, item: TokenStream) -> TokenStream { + let handler_fn = parse_macro_input!(item as ItemFn); + let handler_fn_name = &handler_fn.sig.ident; + + quote!( + #[no_mangle] + extern "Rust" fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! { + #handler_fn_name(info); + } + + #handler_fn + ) + .into() +} + /// The test attribute macro to mark a test function. /// /// # Example diff --git a/ostd/libs/ostd-test/src/lib.rs b/ostd/libs/ostd-test/src/lib.rs index 6e245a4f2..f6d904143 100644 --- a/ostd/libs/ostd-test/src/lib.rs +++ b/ostd/libs/ostd-test/src/lib.rs @@ -75,7 +75,6 @@ pub struct PanicInfo { pub file: String, pub line: usize, pub col: usize, - pub resolve_panic: fn(), } impl core::fmt::Display for PanicInfo { @@ -163,7 +162,6 @@ impl KtestItem { Ok(()) => Err(KtestError::ShouldPanicButNoPanic), Err(e) => match e.downcast::() { Ok(s) => { - (s.resolve_panic)(); if let Some(expected) = self.should_panic.1 { if s.message == expected { Ok(()) diff --git a/ostd/src/arch/x86/irq.rs b/ostd/src/arch/x86/irq.rs index 2d6b16115..8e161994c 100644 --- a/ostd/src/arch/x86/irq.rs +++ b/ostd/src/arch/x86/irq.rs @@ -68,7 +68,7 @@ pub struct CallbackElement { impl CallbackElement { pub fn call(&self, element: &TrapFrame) { - self.function.call((element,)); + (self.function)(element); } } diff --git a/ostd/src/boot/mod.rs b/ostd/src/boot/mod.rs index 2bd462f25..c5c91d9e1 100644 --- a/ostd/src/boot/mod.rs +++ b/ostd/src/boot/mod.rs @@ -118,12 +118,18 @@ pub fn init() { /// /// This function should be only called from the bootloader-specific module. pub(crate) fn call_ostd_main() -> ! { + // The entry point of kernel code, which should be defined by the package that + // uses OSTD. + extern "Rust" { + fn __ostd_main() -> !; + } + + // SAFETY: The function is called only once on the BSP. + unsafe { crate::init() }; + + // SAFETY: This external function is defined by the package that uses OSTD, + // which should be generated by the `ostd::main` macro. So it is safe. unsafe { - // The entry point of kernel code, which should be defined by the package that - // uses OSTD. - extern "Rust" { - fn __ostd_main() -> !; - } __ostd_main(); } } diff --git a/ostd/src/lib.rs b/ostd/src/lib.rs index 61f646c9b..a05329f9d 100644 --- a/ostd/src/lib.rs +++ b/ostd/src/lib.rs @@ -11,6 +11,7 @@ #![feature(generic_const_exprs)] #![feature(iter_from_coroutine)] #![feature(let_chains)] +#![feature(linkage)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(ptr_sub_ptr)] @@ -36,7 +37,7 @@ mod error; pub mod io_mem; pub mod logger; pub mod mm; -pub mod panicking; +pub mod panic; pub mod prelude; pub mod smp; pub mod sync; @@ -47,7 +48,7 @@ pub mod user; use core::sync::atomic::AtomicBool; -pub use ostd_macros::main; +pub use ostd_macros::{main, panic_handler}; pub use ostd_pod::Pod; pub use self::{error::Error, prelude::Result}; @@ -65,7 +66,7 @@ pub use self::{error::Error, prelude::Result}; // make inter-initialization-dependencies more clear and reduce usages of // boot stage only global variables. #[doc(hidden)] -pub unsafe fn init() { +unsafe fn init() { arch::enable_cpu_features(); arch::serial::init(); @@ -153,6 +154,6 @@ pub mod ktest { //! It is rather discouraged to use the definitions here directly. The //! `ktest` attribute is sufficient for all normal use cases. - pub use ostd_macros::test_main as main; + pub use ostd_macros::{test_main as main, test_panic_handler as panic_handler}; pub use ostd_test::*; } diff --git a/ostd/src/panicking.rs b/ostd/src/panic.rs similarity index 62% rename from ostd/src/panicking.rs rename to ostd/src/panic.rs index c150efadb..a9ca9183e 100644 --- a/ostd/src/panicking.rs +++ b/ostd/src/panic.rs @@ -4,60 +4,45 @@ use core::ffi::c_void; +pub use unwinding::panic::{begin_panic, catch_unwind}; + use crate::{ arch::qemu::{exit_qemu, QemuExitCode}, - cpu_local_cell, early_print, early_println, + early_print, early_println, sync::SpinLock, }; extern crate cfg_if; extern crate gimli; + use gimli::Register; use unwinding::abi::{ UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_FindEnclosingFunction, _Unwind_GetGR, _Unwind_GetIP, }; -cpu_local_cell! { - static IN_PANIC: bool = false; -} - -/// The asterinas panic handler. +/// The default panic handler for OSTD based kernels. /// -/// The panic handler must be defined in the binary crate or in the crate that the binary -/// crate explicitly declares by `extern crate`. We cannot let the base crate depend on OSTD -/// due to prismatic dependencies. That's why we export this symbol and state the -/// panic handler in the binary crate. -#[export_name = "__aster_panic_handler"] -pub fn panic_handler(info: &core::panic::PanicInfo) -> ! { +/// The user can override it by defining their own panic handler with the macro +/// `#[ostd::panic_handler]`. +#[cfg(not(ktest))] +#[no_mangle] +pub fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! { let _irq_guard = crate::trap::disable_local(); + crate::cpu_local_cell! { + static IN_PANIC: bool = false; + } + if IN_PANIC.load() { - early_println!("{}", info); - early_println!("The panic handler panicked when processing the above panic. Aborting."); + early_println!("The panic handler panicked {:#?}", info); abort(); } - // If in ktest, we would like to catch the panics and resume the test. - #[cfg(ktest)] - { - use alloc::{boxed::Box, string::ToString}; + IN_PANIC.store(true); - use unwinding::panic::begin_panic; + early_println!("Non-resettable panic! {:#?}", info); - let throw_info = ostd_test::PanicInfo { - message: info.message().to_string(), - file: info.location().unwrap().file().to_string(), - line: info.location().unwrap().line() as usize, - col: info.location().unwrap().column() as usize, - resolve_panic: || { - IN_PANIC.store(false); - }, - }; - // Throw an exception and expecting it to be caught. - begin_panic(Box::new(throw_info.clone())); - } - early_println!("{}", info); print_stack_trace(); abort(); } @@ -67,7 +52,10 @@ pub fn abort() -> ! { exit_qemu(QemuExitCode::Failed); } -fn print_stack_trace() { +/// Prints the stack trace of the current thread to the console. +/// +/// The printing procedure is protected by a spin lock to prevent interleaving. +pub fn print_stack_trace() { /// We acquire a global lock to prevent the frames in the stack trace from /// interleaving. The spin lock is used merely for its simplicity. static BACKTRACE_PRINT_LOCK: SpinLock<()> = SpinLock::new(()); @@ -98,6 +86,10 @@ fn print_stack_trace() { cfg_if::cfg_if! { if #[cfg(target_arch = "x86_64")] { let reg_name = gimli::X86_64::register_name(Register(i)).unwrap_or("unknown"); + } else if #[cfg(target_arch = "riscv64")] { + let reg_name = gimli::RiscV::register_name(Register(i)).unwrap_or("unknown"); + } else if #[cfg(target_arch = "aarch64")] { + let reg_name = gimli::AArch64::register_name(Register(i)).unwrap_or("unknown"); } else { let reg_name = "unknown"; } diff --git a/ostd/src/prelude.rs b/ostd/src/prelude.rs index 2eb81b799..b11fe5ad1 100644 --- a/ostd/src/prelude.rs +++ b/ostd/src/prelude.rs @@ -15,5 +15,5 @@ pub use ostd_macros::ktest; pub use crate::{ early_print as print, early_println as println, mm::{Paddr, Vaddr}, - panicking::abort, + panic::abort, }; diff --git a/ostd/src/task/mod.rs b/ostd/src/task/mod.rs index 61d7fe0c4..8d09c2137 100644 --- a/ostd/src/task/mod.rs +++ b/ostd/src/task/mod.rs @@ -170,7 +170,7 @@ impl TaskOptions { extern "C" fn kernel_task_entry() { let current_task = current_task() .expect("no current task, it should have current task in kernel task entry"); - current_task.func.call(()); + (current_task.func)(); current_task.exit(); }