From 91cc4adba9d748794e79bac4117444860ce9acc9 Mon Sep 17 00:00:00 2001 From: linfeng Date: Wed, 2 Apr 2025 13:49:57 +0800 Subject: [PATCH] fix the panic error for riscv64 (#1123) * fix the panic error for riscv64 --- kernel/Cargo.lock | 3 +- kernel/Cargo.toml | 34 +++++---- kernel/Makefile | 2 +- kernel/crates/intertrait/src/lib.rs | 4 +- kernel/src/arch/riscv64/mod.rs | 7 ++ kernel/src/arch/riscv64/syscall/mod.rs | 12 +--- kernel/src/arch/x86_64/mod.rs | 3 + kernel/src/arch/x86_64/syscall/mod.rs | 12 +--- kernel/src/debug/kallsyms.c | 4 +- kernel/src/debug/panic/hook.rs | 44 +++++++----- kernel/src/debug/panic/mod.rs | 79 +++++++++++++++++---- kernel/src/debug/traceback/mod.rs | 21 ++++-- kernel/src/lib.rs | 3 +- kernel/src/mm/allocator/kernel_allocator.rs | 2 +- kernel/src/syscall/mod.rs | 8 +-- 15 files changed, 159 insertions(+), 79 deletions(-) diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index f3ef4f384..33bc6034a 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -1816,7 +1816,8 @@ dependencies = [ [[package]] name = "unwinding" version = "0.2.3" -source = "git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/unwinding?rev=4eb845da62#4eb845da624f4c9899639ca116beb6d2f87e18bc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987" dependencies = [ "gimli 0.31.1", ] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 60f663a74..d6d54f92b 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -10,14 +10,11 @@ edition = "2021" crate-type = ["staticlib"] [workspace] -members = [ - "crates/*", -] +members = ["crates/*"] [features] -default = ["backtrace", "kvm", "fatfs", "fatfs-secure", "static_keys_test"] +default = ["fatfs", "kvm", "fatfs-secure", "static_keys_test"] # 内核栈回溯 -backtrace = ["dep:unwinding"] # kvm kvm = [] @@ -51,8 +48,19 @@ klog_types = { path = "crates/klog_types" } linkme = "=0.3.27" num = { version = "=0.4.0", default-features = false } num-derive = "=0.3" -num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false } -smoltcp = { version = "=0.11.0", default-features = false, features = ["log", "alloc", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "socket-dhcpv4", "socket-dns", "proto-ipv4", "proto-ipv6"]} +num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev = "1597c1c", default-features = false } +smoltcp = { version = "=0.11.0", default-features = false, features = [ + "log", + "alloc", + "socket-raw", + "socket-udp", + "socket-tcp", + "socket-icmp", + "socket-dhcpv4", + "socket-dns", + "proto-ipv4", + "proto-ipv6", +] } system_error = { path = "crates/system_error" } uefi = { version = "=0.26.0", features = ["alloc"] } uefi-raw = "=0.5.0" @@ -69,12 +77,12 @@ rbpf = { path = "crates/rbpf" } printf-compat = { version = "0.1.1", default-features = false } static-keys = "=0.6.1" -unwinding = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/unwinding", rev = "4eb845da62", default-features = false, optional = true, features = [ +unwinding = { version = "=0.2.3", default-features = false, features = [ "unwinder", "fde-gnu-eh-frame-hdr", "panic", - "personality" -]} + "personality", +] } defer = "0.2.1" cfg-if = { version = "1.0.0" } @@ -87,7 +95,9 @@ x86_64 = "=0.14.10" # target为riscv64时,使用下面的依赖 [target.'cfg(target_arch = "riscv64")'.dependencies] -riscv = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/riscv.git", rev = "4241a97", features = [ "s-mode" ] } +riscv = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/riscv.git", rev = "4241a97", features = [ + "s-mode", +] } sbi-rt = { version = "=0.0.3", features = ["legacy"] } @@ -103,7 +113,7 @@ features = ["spin_no_std"] # The development profile, used for `cargo build` [profile.dev] # opt-level = 0 # Controls the --opt-level the compiler builds with -debug = true # Controls whether the compiler passes `-g` +debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] diff --git a/kernel/Makefile b/kernel/Makefile index c5458a00d..fc00d57f2 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,7 +26,7 @@ clean: fmt: RUSTFLAGS="$(RUSTFLAGS)" cargo fmt --all $(FMT_CHECK) ifeq ($(ARCH), x86_64) - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 clippy --all-features + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 clippy --all-features --target x86_64-unknown-none endif diff --git a/kernel/crates/intertrait/src/lib.rs b/kernel/crates/intertrait/src/lib.rs index 75073dfe3..6130e9f3b 100644 --- a/kernel/crates/intertrait/src/lib.rs +++ b/kernel/crates/intertrait/src/lib.rs @@ -124,11 +124,11 @@ static mut CASTER_MAP: Option &'static HashMap<(TypeId, TypeId), BoxedCaster, BuildFastHasher> { - return unsafe { + unsafe { CASTER_MAP.as_ref().unwrap_or_else(|| { panic!("intertrait_caster_map() must be called after CASTER_MAP is initialized") }) - }; + } } /// Initializes the global [`CASTER_MAP`] with [`CASTERS`]. diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs index fc4ca2491..c2b5e3091 100644 --- a/kernel/src/arch/riscv64/mod.rs +++ b/kernel/src/arch/riscv64/mod.rs @@ -32,3 +32,10 @@ pub use self::ipc::signal::RiscV64SignalArch as CurrentSignalArch; pub use crate::arch::smp::RiscV64SMPArch as CurrentSMPArch; pub use crate::arch::sched::RiscV64SchedArch as CurrentSchedArch; + +pub fn panic_pre_work() { + unsafe { riscv::register::sstatus::set_fs(riscv::register::sstatus::FS::Initial) }; +} +pub fn panic_post_work() { + unsafe { riscv::register::sstatus::set_fs(riscv::register::sstatus::FS::Off) }; +} diff --git a/kernel/src/arch/riscv64/syscall/mod.rs b/kernel/src/arch/riscv64/syscall/mod.rs index 6f80d2446..e43670a6f 100644 --- a/kernel/src/arch/riscv64/syscall/mod.rs +++ b/kernel/src/arch/riscv64/syscall/mod.rs @@ -36,16 +36,8 @@ pub(super) fn syscall_handler(syscall_num: usize, frame: &mut TrapFrame) -> () { let args = [frame.a0, frame.a1, frame.a2, frame.a3, frame.a4, frame.a5]; let mut syscall_handle = || -> usize { - #[cfg(feature = "backtrace")] - { - Syscall::catch_handle(syscall_num, &args, frame) - .unwrap_or_else(|e| e.to_posix_errno() as usize) - } - #[cfg(not(feature = "backtrace"))] - { - Syscall::handle(syscall_num, &args, frame) - .unwrap_or_else(|e| e.to_posix_errno() as usize) - } + Syscall::catch_handle(syscall_num, &args, frame) + .unwrap_or_else(|e| e.to_posix_errno() as usize) }; syscall_return!(syscall_handle(), frame, false); } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 73e1a10eb..cb8404e44 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -48,3 +48,6 @@ pub use crate::arch::vm::x86_kvm_ops as kvm_arch_ops; pub use crate::arch::vm::kvm_host::vcpu::X86VcpuArch as VirtCpuArch; pub use crate::arch::vm::kvm_host::KvmVcpuStat as VirtCpuStat; + +pub fn panic_pre_work() {} +pub fn panic_post_work() {} diff --git a/kernel/src/arch/x86_64/syscall/mod.rs b/kernel/src/arch/x86_64/syscall/mod.rs index cc4756992..30b4c118b 100644 --- a/kernel/src/arch/x86_64/syscall/mod.rs +++ b/kernel/src/arch/x86_64/syscall/mod.rs @@ -120,16 +120,8 @@ pub extern "sysv64" fn syscall_handler(frame: &mut TrapFrame) { _ => {} } let mut syscall_handle = || -> u64 { - #[cfg(feature = "backtrace")] - { - Syscall::catch_handle(syscall_num, &args, frame) - .unwrap_or_else(|e| e.to_posix_errno() as usize) as u64 - } - #[cfg(not(feature = "backtrace"))] - { - Syscall::handle(syscall_num, &args, frame) - .unwrap_or_else(|e| e.to_posix_errno() as usize) as u64 - } + Syscall::catch_handle(syscall_num, &args, frame) + .unwrap_or_else(|e| e.to_posix_errno() as usize) as u64 }; syscall_return!(syscall_handle(), frame, show); } diff --git a/kernel/src/debug/kallsyms.c b/kernel/src/debug/kallsyms.c index 3f95550c3..3e295e205 100644 --- a/kernel/src/debug/kallsyms.c +++ b/kernel/src/debug/kallsyms.c @@ -61,7 +61,7 @@ int read_symbol(FILE *filp, struct kernel_symbol_entry_t *entry) int retval = sscanf(str, "%llx %c %512c", &entry->vaddr, &entry->type, symbol_name); // 如果当前行不符合要求 - if (retval != 3) + if (retval != 3 || entry->type != 'T') { return -1; } @@ -217,4 +217,4 @@ int main(int argc, char **argv) read_map(stdin); generate_result(); -} \ No newline at end of file +} diff --git a/kernel/src/debug/panic/hook.rs b/kernel/src/debug/panic/hook.rs index 0b7f57280..8f9995ba6 100644 --- a/kernel/src/debug/panic/hook.rs +++ b/kernel/src/debug/panic/hook.rs @@ -1,24 +1,34 @@ use crate::debug::traceback::lookup_kallsyms; -use unwinding::abi::{UnwindContext, UnwindReasonCode, _Unwind_GetIP}; -use unwinding::panic::UserUnwindTrace; +use crate::libs::spinlock::SpinLock; +use core::ffi::c_void; +use unwinding::abi::{UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_GetIP}; -/// User hook for unwinding -/// -/// During stack backtrace, the user can print the function location of the current stack frame. -pub struct Tracer; -pub struct CallbackData { - pub counter: usize, -} -impl UserUnwindTrace for Tracer { - type Arg = CallbackData; - - fn trace(ctx: &UnwindContext<'_>, arg: *mut Self::Arg) -> UnwindReasonCode { - let data = unsafe { &mut *(arg) }; +pub fn print_stack_trace() { + static GLOBAL_LOCK: SpinLock<()> = SpinLock::new(()); + let _lock = GLOBAL_LOCK.lock(); + 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(ctx); - unsafe { - lookup_kallsyms(pc as u64, data.counter as i32); + let pc = _Unwind_GetIP(unwind_ctx); + if pc > 0 { + let is_kernel_main = unsafe { lookup_kallsyms(pc as u64, data.counter as i32) }; + data.kernel_main = is_kernel_main; } UnwindReasonCode::NO_REASON } + + let mut data = CallbackData { + counter: 0, + kernel_main: false, + }; + _Unwind_Backtrace(callback, &mut data as *mut _ as _); } diff --git a/kernel/src/debug/panic/mod.rs b/kernel/src/debug/panic/mod.rs index 0dbdd60e0..915ecd5d2 100644 --- a/kernel/src/debug/panic/mod.rs +++ b/kernel/src/debug/panic/mod.rs @@ -1,7 +1,12 @@ -#[cfg(feature = "backtrace")] mod hook; +use alloc::boxed::Box; use cfg_if::cfg_if; +use log::error; + +use crate::process; +use system_error::SystemError; + cfg_if! { if #[cfg(target_os = "none")] { use core::panic::PanicInfo; @@ -10,15 +15,29 @@ cfg_if! { static PANIC_COUNTER: AtomicU8 = AtomicU8::new(0); } } + +#[derive(Debug)] +struct PanicGuard; + +impl PanicGuard { + pub fn new() -> Self { + crate::arch::panic_pre_work(); + Self + } +} + +impl Drop for PanicGuard { + fn drop(&mut self) { + crate::arch::panic_post_work(); + } +} + /// 全局的panic处理函数 /// #[cfg(target_os = "none")] #[panic_handler] #[no_mangle] pub fn panic(info: &PanicInfo) -> ! { - use log::error; - - use crate::process; PANIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed); error!("Kernel Panic Occurred."); @@ -43,15 +62,12 @@ pub fn panic(info: &PanicInfo) -> ! { ); loop {} } - #[cfg(feature = "backtrace")] - { - let mut data = hook::CallbackData { counter: 0 }; - println!("Rust Panic Backtrace:"); - let _res = unwinding::panic::begin_panic_with_hook::( - alloc::boxed::Box::new(()), - &mut data, - ); - // log::error!("panic unreachable: {:?}", res.0); + + if info.can_unwind() { + let guard = Box::new(PanicGuard::new()); + hook::print_stack_trace(); + let _res = unwinding::panic::begin_panic(guard); + // log::error!("panic unreachable: {:?}", _res.0); } println!( "Current PCB:\n\t{:?}", @@ -59,3 +75,40 @@ pub fn panic(info: &PanicInfo) -> ! { ); process::ProcessManager::exit(usize::MAX); } + +/// The wrapper of `unwinding::panic::begin_panic`. If the panic is +/// caught, it will return the result of the function. +/// If the panic is not caught, it will return an error. +pub fn kernel_catch_unwind R>(f: F) -> Result { + let res = unwinding::panic::catch_unwind(f); + match res { + Ok(r) => Ok(r), + Err(e) => { + log::error!("Catch Unwind Error: {:?}", e); + Err(SystemError::MAXERRNO) + } + } +} + +#[allow(unused)] +pub fn test_unwind() { + struct UnwindTest; + impl Drop for UnwindTest { + fn drop(&mut self) { + log::info!("Drop UnwindTest"); + } + } + log::error!("Test unwind"); + let res1 = unwinding::panic::catch_unwind(|| { + let _unwind_test = UnwindTest; + log::error!("Test panic..."); + panic!("Test panic"); + }); + assert!(res1.is_err()); + let res2 = unwinding::panic::catch_unwind(|| { + let _unwind_test = UnwindTest; + log::error!("Test no panic..."); + 0 + }); + assert!(res2.is_ok()); +} diff --git a/kernel/src/debug/traceback/mod.rs b/kernel/src/debug/traceback/mod.rs index bd88174ab..103fe64e7 100644 --- a/kernel/src/debug/traceback/mod.rs +++ b/kernel/src/debug/traceback/mod.rs @@ -13,7 +13,9 @@ fn kallsyms_names_index() {} #[no_mangle] fn kallsyms_names() {} -pub unsafe fn lookup_kallsyms(addr: u64, level: i32) -> Option<()> { +/// print the func name according to the pc address and +/// return true if the func is kernel_main +pub unsafe fn lookup_kallsyms(addr: u64, level: i32) -> bool { let sym_names = kallsyms_names as *const u8; // 由于符号表使用nm -n生成,因此是按照地址升序排列的,因此可以二分 let sym_num = kallsyms_num as usize; @@ -28,10 +30,14 @@ pub unsafe fn lookup_kallsyms(addr: u64, level: i32) -> Option<()> { break; } } - return if index < sym_num { + let mut is_kernel_main = false; + if index < sym_num { let sym_name = CStr::from_ptr(sym_names.add(sym_names_index[index] as usize) as _) .to_str() .unwrap(); + if sym_name.starts_with("kernel_main") { + is_kernel_main = true; + } println!( "[{}] function:{}() \t(+) {:04} address:{:#018x}", level, @@ -39,11 +45,18 @@ pub unsafe fn lookup_kallsyms(addr: u64, level: i32) -> Option<()> { addr - kallsyms_address_list[index], addr ); - Some(()) } else { - None + println!( + "[{}] function:unknown \t(+) {:04} address:{:#018x}", + level, + addr - kallsyms_address_list[sym_num - 1], + addr + ); }; + return is_kernel_main; } + +/// Get the address of the symbol pub unsafe fn addr_from_symbol(symbol: &str) -> Option { let sym_num = kallsyms_num as usize; let sym_names = kallsyms_names as *const u8; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index bb9f1aa4f..6c8ace08a 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,4 +1,5 @@ #![no_main] // <1> +#![no_std] #![feature(alloc_error_handler)] #![feature(new_zeroed_alloc)] #![feature(allocator_api)] @@ -20,7 +21,7 @@ #![feature(c_variadic)] #![feature(asm_goto)] #![feature(linkage)] -#![cfg_attr(target_os = "none", no_std)] +#![feature(panic_can_unwind)] #![allow(static_mut_refs, non_local_definitions, internal_features)] // clippy的配置 #![deny(clippy::all)] diff --git a/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs index b09a944f5..226f7850b 100644 --- a/kernel/src/mm/allocator/kernel_allocator.rs +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -154,7 +154,7 @@ fn dealloc_debug_log(source: LogSource, layout: Layout, ptr: *mut u8) { ) } -/// 为内核slab分配器实现Allocator特性 +// 为内核slab分配器实现Allocator特性 // unsafe impl Allocator for KernelAllocator { // fn allocate(&self, layout: Layout) -> Result, AllocError> { // let memory = unsafe {self.local_alloc(layout)}; diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 18faf9482..3d2646949 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -5,6 +5,7 @@ use core::{ use crate::{ arch::{ipc::signal::SigSet, syscall::nr::*}, + debug::panic::kernel_catch_unwind, filesystem::vfs::syscall::{PosixStatfs, PosixStatx}, ipc::shm::{ShmCtlCmd, ShmFlags, ShmId, ShmKey}, libs::{futex::constant::FutexFlag, rand::GRandFlags}, @@ -77,16 +78,13 @@ impl Syscall { /// 系统调用分发器,用于分发系统调用。 /// /// 与[handle]不同,这个函数会捕获系统调用处理函数的panic,返回错误码。 - #[cfg(feature = "backtrace")] pub fn catch_handle( syscall_num: usize, args: &[usize], frame: &mut TrapFrame, ) -> Result { - let res = unwinding::panic::catch_unwind(|| Self::handle(syscall_num, args, frame)); - res.unwrap_or_else(|_| loop { - core::hint::spin_loop(); - }) + let res = kernel_catch_unwind(|| Self::handle(syscall_num, args, frame))?; + res } /// @brief 系统调用分发器,用于分发系统调用。 ///