diff --git a/Cargo.lock b/Cargo.lock index 97a36a049555b5c08e16b174205f107399b3ff79..21525f45a2cbe106ccc63278fe813c999ad38ac8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,6 +210,12 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "elf" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b183d6ce6ca4cf30e3db37abf5b52568b5f9015c97d9fbdd7026aa5dcdd758" + [[package]] name = "exr" version = "1.6.3" @@ -317,6 +323,7 @@ version = "0.1.0" dependencies = [ "bit_field", "chrono", + "elf", "image", "limine", "paste", diff --git a/Cargo.toml b/Cargo.toml index fbf5ebe817768389dae87b4b24163c74227fd5f5..d12d3e2b4851e0979cee1f857e0e046d63c9a077 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ uart_16550 = "0.2" pc-keyboard = "0.7" paste = "1.0" bit_field = "0.10" +elf = { version = "0.7", default-features = false } # bitvec = "1.0.1" #tracing = { version = "0.1.37", default-features = false } diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 4f474d49c10b21cc65df9ca0b7a6a5cc7126cb1a..aa63e3d102a223d6266ea0d596a0afe314a75682 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,5 +1,5 @@ use crate::{driver, error, smp::Cpu, warn}; -use x86_64::instructions::{self as ins, interrupts as int, random::RdRand}; +use x86_64::instructions::{self as ins, random::RdRand}; // @@ -39,14 +39,10 @@ pub fn early_per_cpu(cpu: &Cpu) { if cfg!(debug_assertions) { warn!("[debug_assertions] {cpu} throwing a debug interrupt exception"); - debug_interrupt(); + int::debug(); } } -pub fn debug_interrupt() { - int::int3(); -} - pub fn rng_seed() -> u64 { RdRand::new().and_then(RdRand::get_u64).unwrap_or_else(|| { error!("Failed to generate a rng seed with x86_64 RDSEED"); @@ -54,12 +50,36 @@ pub fn rng_seed() -> u64 { }) } -pub fn wait_interrupt() { - ins::hlt() +pub mod int { + use x86_64::instructions::interrupts as int; + + pub fn debug() { + int::int3(); + } + + pub fn disable() { + int::disable() + } + + pub fn enable() { + int::enable() + } + + pub fn are_enabled() -> bool { + int::are_enabled() + } + + pub fn without<T>(f: impl FnOnce() -> T) -> T { + int::without_interrupts(f) + } + + pub fn wait() { + int::enable_and_hlt() + } } pub fn done() -> ! { loop { - wait_interrupt() + int::wait() } } diff --git a/src/backtrace.rs b/src/backtrace.rs new file mode 100644 index 0000000000000000000000000000000000000000..a0ca0ab01fa2a7dc2592d4901cfaf0ef213a4346 --- /dev/null +++ b/src/backtrace.rs @@ -0,0 +1,109 @@ +use core::{ + arch::asm, + mem, + ptr::{self, NonNull}, +}; + +use crate::{arch, boot, println}; +use elf::{ + endian::AnyEndian, parse::ParsingTable, string_table::StringTable, symbol::SymbolTable, + ElfBytes, ParseError, +}; +use spin::Lazy; + +// + +pub type BacktraceResult<T> = Result<T, BacktraceError>; + +#[derive(Debug)] +pub enum BacktraceError { + NoSymtabOrStrtab, + ElfNotLoaded, + ElfParse(ParseError), + + // TODO: this is temporary + Inner(&'static Self), +} + +static KERNEL_ELF: Lazy<BacktraceResult<ElfBytes<'static, AnyEndian>>> = Lazy::new(|| { + let bytes = boot::kernel_file().ok_or(BacktraceError::ElfNotLoaded)?; + ElfBytes::minimal_parse(bytes).map_err(BacktraceError::ElfParse) + // ElfBytes::minimal_parse(bytes) + // .ok() + // .ok_or(BacktraceError::ElfParse) +}); + +static SYMTAB: Lazy< + Result<(SymbolTable<'static, AnyEndian>, StringTable<'static>), BacktraceError>, +> = Lazy::new(|| { + let elf = KERNEL_ELF.as_ref().map_err(BacktraceError::Inner)?; + + elf.symbol_table() + .map_err(BacktraceError::ElfParse)? + .ok_or(BacktraceError::NoSymtabOrStrtab) +}); + +static UNKNOWN: &str = "<unknown>"; + +pub fn symbol(instr_ptr: u64) -> Result<&'static str, BacktraceError> { + let (symtab, strtab) = SYMTAB.as_ref().map_err(BacktraceError::Inner)?; + + let symbol = symtab + .iter() + .find(|sym| (sym.st_value..sym.st_value + sym.st_size).contains(&instr_ptr)); + + let Some(symbol) = symbol else { + return Ok(UNKNOWN); + }; + + strtab + .get(symbol.st_name as _) + .map_err(BacktraceError::ElfParse) +} + +pub fn unwind_stack(mut f: impl FnMut(usize, &'static str)) { + arch::int::disable(); + + // TODO: move to arch + let mut frame_ptr: usize; + let mut instr_ptr: usize = x86_64::registers::read_rip().as_u64() as _; + unsafe { + asm!("mov {}, rbp", out(reg) frame_ptr); + } + + println!("{frame_ptr} {instr_ptr}"); + + if frame_ptr == 0 { + println!("empty"); + } + + loop { + if frame_ptr == 0 { + break; + } + + let rip_rbp = frame_ptr + mem::size_of::<usize>(); + + let instr_ptr = unsafe { ptr::read_volatile(rip_rbp as *const usize) }; + if instr_ptr == 0 { + break; + } + + frame_ptr = unsafe { ptr::read_volatile(frame_ptr as *const usize) }; + + f(instr_ptr, symbol(instr_ptr as _).unwrap_or(UNKNOWN)); + } + + // TODO: should reset to what it was before + arch::int::enable(); +} + +pub fn print_backtrace() { + println!("--[ begin backtrace ]--"); + let mut i = 0usize; + unwind_stack(|ip, sym| { + println!("{i:>3} : {ip:#018x} - {sym}"); + i += 1; + }); + println!("--[ end backtrace ]--"); +} diff --git a/src/boot/limine/cmdline.rs b/src/boot/limine/cmdline.rs index 9d92ae6900e4850953f03b09e90d42c722b1ef42..0692c2189fdc0d22cc70e2f172e7cc5a1901f1d7 100644 --- a/src/boot/limine/cmdline.rs +++ b/src/boot/limine/cmdline.rs @@ -1,9 +1,8 @@ -use limine::LimineKernelFileRequest; +use super::kernel::REQ; // pub fn cmdline() -> Option<&'static str> { - static REQ: LimineKernelFileRequest = LimineKernelFileRequest::new(0); REQ.get_response() .get() .and_then(|resp| resp.kernel_file.get()) diff --git a/src/boot/limine/kernel.rs b/src/boot/limine/kernel.rs new file mode 100644 index 0000000000000000000000000000000000000000..1c7085b081644cd488381e720b6a44c8d7c3bb3d --- /dev/null +++ b/src/boot/limine/kernel.rs @@ -0,0 +1,15 @@ +use core::slice; +use limine::LimineKernelFileRequest; + +// + +pub(crate) static REQ: LimineKernelFileRequest = LimineKernelFileRequest::new(0); + +pub fn kernel_file() -> Option<&'static [u8]> { + REQ.get_response() + .get() + .and_then(|resp| resp.kernel_file.get()) + .and_then(|file| { + Some(unsafe { slice::from_raw_parts(file.base.as_ptr()?, file.length as _) }) + }) +} diff --git a/src/boot/limine/mod.rs b/src/boot/limine/mod.rs index 54c9487af87a7a08e7b6908894faca5fb7fac82c..ff232a0c01cc02c34c87f39fe929961f9c54e592 100644 --- a/src/boot/limine/mod.rs +++ b/src/boot/limine/mod.rs @@ -8,6 +8,7 @@ pub use addr::phys_addr; pub use addr::virt_addr; pub use cmdline::cmdline; pub use framebuffer::framebuffer; +pub use kernel::kernel_file; pub use mem::memmap; pub use rsdp::rsdp; pub use smp::{boot_cpu, init as smp_init}; @@ -18,6 +19,7 @@ pub use term::_print; mod addr; mod cmdline; mod framebuffer; +mod kernel; mod mem; mod rsdp; mod smp; diff --git a/src/main.rs b/src/main.rs index 9d8d9b1cb4595b0b7e73c0e46ec57eca72c0a747..8121468e0480525b02a37603fc9a657602d24d73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,17 +3,19 @@ #![no_std] #![no_main] // -#![feature(format_args_nl)] -#![feature(abi_x86_interrupt)] -#![feature(allocator_api)] -#![feature(pointer_is_aligned)] -#![feature(int_roundings)] -#![feature(array_chunks)] -#![feature(cfg_target_has_atomic)] -#![feature(slice_as_chunks)] -#![feature(core_intrinsics)] -// -#![feature(custom_test_frameworks)] +#![feature( + format_args_nl, + abi_x86_interrupt, + allocator_api, + pointer_is_aligned, + int_roundings, + array_chunks, + cfg_target_has_atomic, + slice_as_chunks, + core_intrinsics, + custom_test_frameworks, + panic_can_unwind +)] #![test_runner(crate::testfw::test_runner)] #![reexport_test_harness_main = "test_main"] @@ -27,6 +29,7 @@ extern crate alloc; #[path = "arch/x86_64/mod.rs"] pub mod arch; +pub mod backtrace; pub mod boot; pub mod driver; pub mod log; @@ -70,6 +73,9 @@ fn kernel_main() -> ! { // ofc. every kernel has to have this cringy ascii name splash info!("\n{}\n", include_str!("./splash")); + backtrace::print_backtrace(); + panic!("test panic"); + if let Some(bl) = boot::BOOT_NAME.get() { debug!("{KERNEL_NAME} {KERNEL_VERSION} was booted with {bl}"); } diff --git a/src/panic.rs b/src/panic.rs index a06b3c7f917c25310131879cbdc6baa5f92a3670..914dae05ce31a8af7b2983b0a8b241c29a40bb19 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,4 +1,7 @@ -use crate::arch::done; +use crate::{ + arch::{done, int}, + backtrace, +}; use core::panic::PanicInfo; // @@ -6,13 +9,21 @@ use core::panic::PanicInfo; #[cfg(not(test))] #[panic_handler] fn panic_handler(info: &PanicInfo) -> ! { - crate::println!("Kernel CPU {info}"); + int::disable(); + panic_unwind(info); done(); } #[cfg(test)] #[panic_handler] fn panic_handler(info: &PanicInfo) -> ! { + int::disable(); + panic_unwind(info); crate::testfw::test_panic_handler(info); done(); } + +fn panic_unwind(info: &PanicInfo) { + crate::println!("Kernel CPU {info} {}", info.can_unwind()); + backtrace::print_backtrace(); +}