From 1797c4510ef2fc07f386cf0efe0f155a02c56379 Mon Sep 17 00:00:00 2001 From: Seiya Nuta Date: Sun, 19 Dec 2021 15:36:40 +0900 Subject: [PATCH 1/2] Support saving kernel crash logs using boot2dump Boot2dump is a new tiny operating system written by me, which is specialized for saving a crash dump. Once Kerla panics, it jumps into the boot2dump's boot code. Boot2dump reads an ext4 filesytem in a virtio-blk device, saves the dump into a file and then it resets the computer. It's an already working mechanism in my deployment in DigitalOcean! --- Cargo.lock | 7 +++++++ kernel/Cargo.toml | 1 + kernel/lang_items.rs | 46 +++++++++++++++++++++++++++++++++++++++++++- kernel/logger.rs | 15 +++++++++++---- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94e226e2..115b4793 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,12 @@ dependencies = [ "wyz", ] +[[package]] +name = "boot2dump" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225c5460e6cda737adafc046c6106ca1769963bce43261b8a38a18dfcc2efa50" + [[package]] name = "buddy_system_allocator" version = "0.8.0" @@ -180,6 +186,7 @@ dependencies = [ "arrayvec 0.7.2", "atomic_refcell", "bitflags", + "boot2dump", "crossbeam", "goblin", "hashbrown", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 121bc9d1..e6682f49 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -13,6 +13,7 @@ log = "0.4" spin = "0.9.2" goblin = { version = "0.4", default-features = false, features = ["elf64"] } smoltcp = { version = "0.7.5", default-features = false, features = ["alloc", "proto-ipv4", "socket", "socket-raw", "socket-udp", "socket-tcp", "proto-dhcpv4", "ethernet"] } +boot2dump = { version = "0" } # Data structues. bitflags = "1.3.2" diff --git a/kernel/lang_items.rs b/kernel/lang_items.rs index a5d91dab..816824ec 100644 --- a/kernel/lang_items.rs +++ b/kernel/lang_items.rs @@ -1,6 +1,27 @@ use core::sync::atomic::AtomicBool; pub static PANICKED: AtomicBool = AtomicBool::new(false); +static mut KERNEL_DUMP_BUF: KernelDump = KernelDump::empty(); + +#[repr(C, packed)] +struct KernelDump { + /// `0xdeadbeee` + magic: u32, + /// The length of the kernel log. + len: u32, + /// The kernel log (including the panic message). + log: [u8; 4096], +} + +impl KernelDump { + const fn empty() -> KernelDump { + KernelDump { + magic: 0, + len: 0, + log: [0; 4096], + } + } +} #[alloc_error_handler] fn alloc_error_handler(layout: core::alloc::Layout) -> ! { @@ -11,6 +32,7 @@ fn alloc_error_handler(layout: core::alloc::Layout) -> ! { #[panic_handler] #[cfg(not(test))] fn panic(info: &core::panic::PanicInfo) -> ! { + use crate::logger::KERNEL_LOG_BUF; use core::sync::atomic::Ordering; if PANICKED.load(Ordering::SeqCst) { @@ -21,5 +43,27 @@ fn panic(info: &core::panic::PanicInfo) -> ! { PANICKED.store(true, Ordering::SeqCst); error!("{}", info); kerla_runtime::backtrace::backtrace(); - kerla_runtime::arch::halt(); + + unsafe { + warn!("preparing a crash dump..."); + KERNEL_LOG_BUF.force_unlock(); + let mut off = 0; + let mut log_buffer = KERNEL_LOG_BUF.lock(); + while let Some(slice) = log_buffer.pop_slice(KERNEL_DUMP_BUF.log.len().saturating_sub(off)) + { + KERNEL_DUMP_BUF.log[off..(off + slice.len())].copy_from_slice(&slice); + off += slice.len(); + } + + KERNEL_DUMP_BUF.magic = 0xdeadbeee; + KERNEL_DUMP_BUF.len = off as u32; + + warn!("prepared crash dump: log_len={}", off); + warn!("booting boot2dump..."); + let dump_as_bytes = core::slice::from_raw_parts( + &KERNEL_DUMP_BUF as *const _ as *const u8, + core::mem::size_of::(), + ); + boot2dump::save_to_file_and_reboot("kerla.dump", dump_as_bytes); + } } diff --git a/kernel/logger.rs b/kernel/logger.rs index ef55af48..379a59b9 100644 --- a/kernel/logger.rs +++ b/kernel/logger.rs @@ -25,11 +25,18 @@ impl Printer for LoggedPrinter { fn print_bytes(&self, s: &[u8]) { self.inner.print_bytes(s); - // Don't write into the kernel log buffer as it may call a printk function - // due to an assertion. - if !PANICKED.load(Ordering::SeqCst) { - KERNEL_LOG_BUF.lock().push_slice(s); + if PANICKED.load(Ordering::SeqCst) { + // If kernel panics, it's uncertain whether KERNEL_LOG_BUF is in + // a dead lock. + // + // Since only single CPU can continue handling a panic, we can + // ensure it's safe to unlock it. + unsafe { + KERNEL_LOG_BUF.force_unlock(); + } } + + KERNEL_LOG_BUF.lock().push_slice(s); } } From ea1678342e1fe3df38b9b7b04352d4db1843d57c Mon Sep 17 00:00:00 2001 From: Seiya Nuta Date: Sun, 19 Dec 2021 15:45:49 +0900 Subject: [PATCH 2/2] Make clippy happy --- kernel/lang_items.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/lang_items.rs b/kernel/lang_items.rs index 816824ec..fae6da4a 100644 --- a/kernel/lang_items.rs +++ b/kernel/lang_items.rs @@ -51,7 +51,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { let mut log_buffer = KERNEL_LOG_BUF.lock(); while let Some(slice) = log_buffer.pop_slice(KERNEL_DUMP_BUF.log.len().saturating_sub(off)) { - KERNEL_DUMP_BUF.log[off..(off + slice.len())].copy_from_slice(&slice); + KERNEL_DUMP_BUF.log[off..(off + slice.len())].copy_from_slice(slice); off += slice.len(); }