From e0a8ed665a05a793e7ff1d09957b2e0cbd924cdb Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Wed, 22 May 2024 11:54:26 -0700 Subject: [PATCH] Add the `esp-backtrace` package to the repository --- Cargo.toml | 1 + esp-backtrace/Cargo.toml | 45 +++++ esp-backtrace/README.md | 52 ++++++ esp-backtrace/build.rs | 28 +++ esp-backtrace/src/lib.rs | 336 ++++++++++++++++++++++++++++++++++++ esp-backtrace/src/riscv.rs | 168 ++++++++++++++++++ esp-backtrace/src/xtensa.rs | 309 +++++++++++++++++++++++++++++++++ examples/Cargo.toml | 6 +- xtask/src/lib.rs | 5 + 9 files changed, 945 insertions(+), 5 deletions(-) create mode 100644 esp-backtrace/Cargo.toml create mode 100644 esp-backtrace/README.md create mode 100644 esp-backtrace/build.rs create mode 100644 esp-backtrace/src/lib.rs create mode 100644 esp-backtrace/src/riscv.rs create mode 100644 esp-backtrace/src/xtensa.rs diff --git a/Cargo.toml b/Cargo.toml index fa1020cb313..e52389628ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = ["xtask"] exclude = [ "esp-alloc", + "esp-backtrace", "esp-build", "esp-hal", "esp-hal-procmacros", diff --git a/esp-backtrace/Cargo.toml b/esp-backtrace/Cargo.toml new file mode 100644 index 00000000000..5730d95a59c --- /dev/null +++ b/esp-backtrace/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "esp-backtrace" +version = "0.11.1" +edition = "2021" +description = "Bare-metal backtrace support for ESP32" +repository = "https://github.com/esp-rs/esp-hal" +license = "MIT OR Apache-2.0" + +[package.metadata.docs.rs] +default-target = "riscv32imc-unknown-none-elf" +features = ["esp32c3", "panic-handler", "exception-handler", "println", "esp-println/uart"] + +[dependencies] +defmt = { version = "0.3.6", optional = true } +esp-println = { version = "0.9.1", optional = true, default-features = false, path = "../esp-println" } +semihosting = { version = "0.1.7", optional = true } + +[build-dependencies] +esp-build = { version = "0.1.0", path = "../esp-build" } + +[features] +default = ["colors"] + +# You must enable exactly one of the below features to support the correct chip: +esp32 = ["esp-println?/esp32", "semihosting?/openocd-semihosting"] +esp32c2 = ["esp-println?/esp32c2"] +esp32c3 = ["esp-println?/esp32c3"] +esp32c6 = ["esp-println?/esp32c6"] +esp32h2 = ["esp-println?/esp32h2"] +esp32p4 = ["esp-println?/esp32p4"] +esp32s2 = ["esp-println?/esp32s2", "semihosting?/openocd-semihosting"] +esp32s3 = ["esp-println?/esp32s3", "semihosting?/openocd-semihosting"] + +# Use esp-println +println = ["dep:esp-println"] + +# Use defmt +defmt = ["dep:defmt"] + +# You may optionally enable one or more of the below features to provide +# additional functionality: +colors = [] +exception-handler = [] +halt-cores = [] +panic-handler = [] diff --git a/esp-backtrace/README.md b/esp-backtrace/README.md new file mode 100644 index 00000000000..7b41e07b90f --- /dev/null +++ b/esp-backtrace/README.md @@ -0,0 +1,52 @@ +# esp-backtrace - backtrace for ESP32 bare-metal + +Supports the ESP32, ESP32-C2/C3/C6, ESP32-H2, ESP32-P4, and ESP32-S2/S3. Optional exception and panic handlers are included, both of which can be enabled via their respective features. + +Please note that when targeting a RISC-V device, you **need** to force frame pointers (i.e. `"-C", "force-frame-pointers",` in your `.cargo/config.toml`); this is **not** required for Xtensa. + +You can get an array of backtrace addresses (currently limited to 10) via `arch::backtrace()` if +you want to create a backtrace yourself (i.e. not using the panic or exception handler). + +When using the panic and/or exception handler make sure to include `use esp_backtrace as _;`. + +## Features + +| Feature | Description | +| ----------------- | ------------------------------------------------------------------------------------------------------------------ | +| esp32 | Target ESP32 | +| esp32c2 | Target ESP32-C2 | +| esp32c3 | Target ESP32-C3 | +| esp32c6 | Target ESP32-C6 | +| esp32h2 | Target ESP32-H2 | +| esp32p4 | Target ESP32-P4 | +| esp32s2 | Target ESP32-S2 | +| esp32s3 | Target ESP32-S3 | +| panic-handler | Include a panic handler, will add `esp-println` as a dependency | +| exception-handler | Include an exception handler, will add `esp-println` as a dependency | +| println | Use `esp-println` to print messages | +| defmt | Use `defmt` logging to print messages\* (check [example](https://github.com/playfulFence/backtrace-defmt-example)) | +| colors | Print messages in red\* | +| halt-cores | Halt both CPUs on ESP32 / ESP32-S3 in case of a panic or exception | +| semihosting | Call `semihosting::process::abort()` on panic. | + +\* _only used for panic and exception handlers_ + +### `defmt` Feature + +Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README: +https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv + +## License + +Licensed under either of: + +- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in +the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without +any additional terms or conditions. diff --git a/esp-backtrace/build.rs b/esp-backtrace/build.rs new file mode 100644 index 00000000000..23915762e3d --- /dev/null +++ b/esp-backtrace/build.rs @@ -0,0 +1,28 @@ +use esp_build::assert_unique_used_features; + +fn main() { + // Ensure that only a single chip is specified: + assert_unique_used_features!( + "esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3" + ); + + // Ensure that exactly a backend is selected: + assert_unique_used_features!("defmt", "println"); + + if is_nightly() { + println!("cargo:rustc-cfg=nightly"); + } +} + +fn is_nightly() -> bool { + let version_output = std::process::Command::new( + std::env::var_os("RUSTC").unwrap_or_else(|| std::ffi::OsString::from("rustc")), + ) + .arg("-V") + .output() + .unwrap() + .stdout; + let version_string = String::from_utf8_lossy(&version_output); + + version_string.contains("nightly") +} diff --git a/esp-backtrace/src/lib.rs b/esp-backtrace/src/lib.rs new file mode 100644 index 00000000000..56aabed1e11 --- /dev/null +++ b/esp-backtrace/src/lib.rs @@ -0,0 +1,336 @@ +#![no_std] +#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))] +#![allow(rustdoc::bare_urls)] +#![doc = include_str!("../README.md")] +#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] +#![cfg_attr(nightly, feature(panic_info_message))] + +#[cfg(feature = "defmt")] +use defmt as _; +#[cfg(feature = "println")] +use esp_println as _; + +const MAX_BACKTRACE_ADDRESSES: usize = 10; + +#[cfg(feature = "colors")] +const RESET: &str = "\u{001B}[0m"; +#[cfg(feature = "colors")] +const RED: &str = "\u{001B}[31m"; + +#[cfg(feature = "defmt")] +macro_rules! println { + ("") => { + // Do nothing if the string is just a space + }; + ($($arg:tt)*) => { + defmt::error!($($arg)*); + }; +} + +#[cfg(all(feature = "println", not(feature = "defmt")))] +macro_rules! println { + ($($arg:tt)*) => { + esp_println::println!($($arg)*); + }; +} + +#[allow(unused, unused_variables)] +fn set_color_code(code: &str) { + #[cfg(feature = "println")] + { + println!("{}", code); + } +} + +#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")] +#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")] +pub mod arch; + +#[cfg(feature = "panic-handler")] +#[panic_handler] +fn panic_handler(info: &core::panic::PanicInfo) -> ! { + #[cfg(feature = "colors")] + set_color_code(RED); + + println!(""); + println!(""); + + if let Some(location) = info.location() { + let (file, line, column) = (location.file(), location.line(), location.column()); + println!( + "!! A panic occured in '{}', at line {}, column {}:", + file, line, column + ); + } else { + println!("!! A panic occured at an unknown location:"); + } + + #[cfg(not(nightly))] + { + #[cfg(not(feature = "defmt"))] + println!("{:#?}", info); + + #[cfg(feature = "defmt")] + println!("{:#?}", defmt::Display2Format(info)); + } + + #[cfg(nightly)] + { + if let Some(message) = info.message() { + #[cfg(not(feature = "defmt"))] + println!("{}", message); + + #[cfg(feature = "defmt")] + println!("{}", defmt::Display2Format(message)); + } + } + + println!(""); + println!("Backtrace:"); + println!(""); + + let backtrace = crate::arch::backtrace(); + #[cfg(target_arch = "riscv32")] + if backtrace.iter().filter(|e| e.is_some()).count() == 0 { + println!("No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)"); + } + for e in backtrace { + if let Some(addr) = e { + #[cfg(all(feature = "colors", feature = "println"))] + println!("{}0x{:x}", RED, addr - crate::arch::RA_OFFSET); + + #[cfg(not(all(feature = "colors", feature = "println")))] + println!("0x{:x}", addr - crate::arch::RA_OFFSET); + } + } + + #[cfg(feature = "colors")] + set_color_code(RESET); + + #[cfg(feature = "semihosting")] + semihosting::process::abort(); + + halt(); +} + +#[cfg(all(feature = "exception-handler", target_arch = "xtensa"))] +#[no_mangle] +#[link_section = ".rwtext"] +unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) { + #[cfg(feature = "colors")] + set_color_code(RED); + + // Unfortunately, a different formatter string is used + #[cfg(not(feature = "defmt"))] + esp_println::println!("\n\nException occured '{:?}'", cause); + + #[cfg(feature = "defmt")] + defmt::error!("\n\nException occured '{}'", cause); + + println!("{:?}", context); + + let backtrace = crate::arch::backtrace_internal(context.A1, 0); + for e in backtrace { + if let Some(addr) = e { + println!("0x{:x}", addr); + } + } + println!(""); + println!(""); + println!(""); + + #[cfg(feature = "colors")] + set_color_code(RESET); + + #[cfg(feature = "semihosting")] + semihosting::process::abort(); + + halt(); +} + +#[cfg(all(feature = "exception-handler", target_arch = "riscv32"))] +#[export_name = "ExceptionHandler"] +fn exception_handler(context: &arch::TrapFrame) -> ! { + let mepc = context.pc; + let code = context.mcause & 0xff; + let mtval = context.mtval; + + #[cfg(feature = "colors")] + set_color_code(RED); + + if code == 14 { + println!(""); + println!( + "Stack overflow detected at 0x{:x} called by 0x{:x}", + mepc, context.ra + ); + println!(""); + } else { + let code = match code { + 0 => "Instruction address misaligned", + 1 => "Instruction access fault", + 2 => "Illegal instruction", + 3 => "Breakpoint", + 4 => "Load address misaligned", + 5 => "Load access fault", + 6 => "Store/AMO address misaligned", + 7 => "Store/AMO access fault", + 8 => "Environment call from U-mode", + 9 => "Environment call from S-mode", + 10 => "Reserved", + 11 => "Environment call from M-mode", + 12 => "Instruction page fault", + 13 => "Load page fault", + 14 => "Reserved", + 15 => "Store/AMO page fault", + _ => "UNKNOWN", + }; + + println!( + "Exception '{}' mepc=0x{:08x}, mtval=0x{:08x}", + code, mepc, mtval + ); + #[cfg(not(feature = "defmt"))] + println!("{:x?}", context); + + #[cfg(feature = "defmt")] + println!("{:?}", context); + + let backtrace = crate::arch::backtrace_internal(context.s0 as u32, 0); + if backtrace.iter().filter(|e| e.is_some()).count() == 0 { + println!("No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)"); + } + for e in backtrace { + if let Some(addr) = e { + #[cfg(all(feature = "colors", feature = "println"))] + println!("{}0x{:x}", RED, addr - crate::arch::RA_OFFSET); + + #[cfg(not(all(feature = "colors", feature = "println")))] + println!("0x{:x}", addr - crate::arch::RA_OFFSET); + } + } + } + + println!(""); + println!(""); + println!(""); + + #[cfg(feature = "colors")] + set_color_code(RESET); + + #[cfg(feature = "semihosting")] + semihosting::process::abort(); + + halt(); +} + +// Ensure that the address is in DRAM and that it is 16-byte aligned. +// +// Based loosely on the `esp_stack_ptr_in_dram` function from +// `components/esp_hw_support/include/esp_memory_utils.h` in ESP-IDF. +// +// Address ranges can be found in `components/soc/$CHIP/include/soc/soc.h` as +// `SOC_DRAM_LOW` and `SOC_DRAM_HIGH`. +fn is_valid_ram_address(address: u32) -> bool { + if (address & 0xF) != 0 { + return false; + } + + #[cfg(feature = "esp32")] + if !(0x3FFA_E000..=0x4000_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32c2")] + if !(0x3FCA_0000..=0x3FCE_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32c3")] + if !(0x3FC8_0000..=0x3FCE_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32c6")] + if !(0x4080_0000..=0x4088_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32h2")] + if !(0x4080_0000..=0x4085_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32p4")] + if !(0x4FF0_0000..=0x4FFC_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32s2")] + if !(0x3FFB_0000..=0x4000_0000).contains(&address) { + return false; + } + + #[cfg(feature = "esp32s3")] + if !(0x3FC8_8000..=0x3FD0_0000).contains(&address) { + return false; + } + + true +} + +#[cfg(any( + not(any(feature = "esp32", feature = "esp32p4", feature = "esp32s3")), + not(feature = "halt-cores") +))] +#[allow(unused)] +fn halt() -> ! { + loop {} +} + +// TODO: Enable `halt` function for `esp32p4` feature once implemented +#[cfg(all(any(feature = "esp32", feature = "esp32s3"), feature = "halt-cores"))] +#[allow(unused)] +fn halt() -> ! { + #[cfg(feature = "esp32")] + mod registers { + pub(crate) const OPTIONS0: u32 = 0x3ff48000; + pub(crate) const SW_CPU_STALL: u32 = 0x3ff480ac; + } + + #[cfg(feature = "esp32p4")] + mod registers { + pub(crate) const SW_CPU_STALL: u32 = 0x50115200; + } + + #[cfg(feature = "esp32s3")] + mod registers { + pub(crate) const OPTIONS0: u32 = 0x60008000; + pub(crate) const SW_CPU_STALL: u32 = 0x600080bc; + } + + let sw_cpu_stall = registers::SW_CPU_STALL as *mut u32; + + #[cfg(feature = "esp32p4")] + unsafe {} + + #[cfg(not(feature = "esp32p4"))] + unsafe { + // We need to write the value "0x86" to stall a particular core. The write + // location is split into two separate bit fields named "c0" and "c1", and the + // two fields are located in different registers. Each core has its own pair of + // "c0" and "c1" bit fields. + + let options0 = registers::OPTIONS0 as *mut u32; + + options0.write_volatile(options0.read_volatile() & !(0b1111) | 0b1010); + + sw_cpu_stall.write_volatile( + sw_cpu_stall.read_volatile() & !(0b111111 << 20) & !(0b111111 << 26) + | (0x21 << 20) + | (0x21 << 26), + ); + } + + loop {} +} diff --git a/esp-backtrace/src/riscv.rs b/esp-backtrace/src/riscv.rs new file mode 100644 index 00000000000..99db0995c3f --- /dev/null +++ b/esp-backtrace/src/riscv.rs @@ -0,0 +1,168 @@ +use core::arch::asm; + +use crate::MAX_BACKTRACE_ADDRESSES; + +// subtract 4 from the return address +// the return address is the address following the JALR +// we get better results (especially if the caller was the last function in the +// calling function) if we report the address of the JALR itself +// even if it was a C.JALR we should get good results using RA - 4 +pub(super) const RA_OFFSET: usize = 4; + +/// Registers saved in trap handler +#[doc(hidden)] +#[allow(missing_docs)] +#[derive(Default, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub(crate) struct TrapFrame { + pub ra: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, + pub s0: usize, + pub s1: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, + pub s9: usize, + pub s10: usize, + pub s11: usize, + pub gp: usize, + pub tp: usize, + pub sp: usize, + pub pc: usize, + pub mstatus: usize, + pub mcause: usize, + pub mtval: usize, +} + +impl core::fmt::Debug for TrapFrame { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!( + fmt, + "TrapFrame +PC=0x{:08x} RA/x1=0x{:08x} SP/x2=0x{:08x} GP/x3=0x{:08x} TP/x4=0x{:08x} +T0/x5=0x{:08x} T1/x6=0x{:08x} T2/x7=0x{:08x} S0/FP/x8=0x{:08x} S1/x9=0x{:08x} +A0/x10=0x{:08x} A1/x11=0x{:08x} A2/x12=0x{:08x} A3/x13=0x{:08x} A4/x14=0x{:08x} +A5/x15=0x{:08x} A6/x16=0x{:08x} A7/x17=0x{:08x} S2/x18=0x{:08x} S3/x19=0x{:08x} +S4/x20=0x{:08x} S5/x21=0x{:08x} S6/x22=0x{:08x} S7/x23=0x{:08x} S8/x24=0x{:08x} +S9/x25=0x{:08x} S10/x26=0x{:08x} S11/x27=0x{:08x} T3/x28=0x{:08x} T4/x29=0x{:08x} +T5/x30=0x{:08x} T6/x31=0x{:08x} + +MSTATUS=0x{:08x} +MCAUSE=0x{:08x} +MTVAL=0x{:08x} + ", + self.pc, + self.ra, + self.gp, + self.sp, + self.tp, + self.t0, + self.t1, + self.t2, + self.s0, + self.s1, + self.a0, + self.a1, + self.a2, + self.a3, + self.a4, + self.a5, + self.a6, + self.a7, + self.s2, + self.s3, + self.s4, + self.s5, + self.s6, + self.s7, + self.s8, + self.s9, + self.s10, + self.s11, + self.t3, + self.t4, + self.t5, + self.t6, + self.mstatus, + self.mcause, + self.mtval, + ) + } +} + +/// Get an array of backtrace addresses. +/// +/// This needs `force-frame-pointers` enabled. +pub fn backtrace() -> [Option; MAX_BACKTRACE_ADDRESSES] { + let fp = unsafe { + let mut _tmp: u32; + asm!("mv {0}, x8", out(reg) _tmp); + _tmp + }; + + backtrace_internal(fp, 2) +} + +pub(crate) fn backtrace_internal( + fp: u32, + suppress: i32, +) -> [Option; MAX_BACKTRACE_ADDRESSES] { + let mut result = [None; 10]; + let mut index = 0; + + let mut fp = fp; + let mut suppress = suppress; + let mut old_address = 0; + loop { + unsafe { + let address = (fp as *const u32).offset(-1).read_volatile(); // RA/PC + fp = (fp as *const u32).offset(-2).read_volatile(); // next FP + + if old_address == address { + break; + } + + old_address = address; + + if address == 0 { + break; + } + + if !crate::is_valid_ram_address(fp) { + break; + } + + if suppress == 0 { + result[index] = Some(address as usize); + index += 1; + + if index >= MAX_BACKTRACE_ADDRESSES { + break; + } + } else { + suppress -= 1; + } + } + } + + result +} diff --git a/esp-backtrace/src/xtensa.rs b/esp-backtrace/src/xtensa.rs new file mode 100644 index 00000000000..dab0347b258 --- /dev/null +++ b/esp-backtrace/src/xtensa.rs @@ -0,0 +1,309 @@ +use core::arch::asm; + +use crate::MAX_BACKTRACE_ADDRESSES; + +// subtract 3 from the return address +// the return address is the address following the callxN +// we get better results (especially if the caller was the last function in the +// calling function) if we report the address of callxN itself +pub(super) const RA_OFFSET: usize = 3; + +#[doc(hidden)] +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub enum ExceptionCause { + /// Illegal Instruction + IllegalInstruction = 0, + /// System Call (Syscall Instruction) + Syscall = 1, + /// Instruction Fetch Error + InstrFetchError = 2, + /// Load Store Error + LoadStoreError = 3, + /// Level 1 Interrupt + LevelOneInterrupt = 4, + /// Stack Extension Assist (movsp Instruction) For Alloca + Alloca = 5, + /// Integer Divide By Zero + DivideByZero = 6, + /// Use Of Failed Speculative Access (Not Implemented) + NextPCValueIllegal = 7, + /// Privileged Instruction + PrivilegedInstruction = 8, + /// Unaligned Load Or Store + UnalignedLoadOrStore = 9, + /// Reserved + ExternalRegisterPrivilegeError = 10, + /// Reserved + ExclusiveError = 11, + /// Pif Data Error On Instruction Fetch (Rb-200x And Later) + InstrDataError = 12, + /// Pif Data Error On Load Or Store (Rb-200x And Later) + LoadStoreDataError = 13, + /// Pif Address Error On Instruction Fetch (Rb-200x And Later) + InstrAddrError = 14, + /// Pif Address Error On Load Or Store (Rb-200x And Later) + LoadStoreAddrError = 15, + /// Itlb Miss (No Itlb Entry Matches, Hw Refill Also Missed) + ItlbMiss = 16, + /// Itlb Multihit (Multiple Itlb Entries Match) + ItlbMultiHit = 17, + /// Ring Privilege Violation On Instruction Fetch + InstrRing = 18, + /// Size Restriction On Ifetch (Not Implemented) + Reserved19 = 19, + /// Cache Attribute Does Not Allow Instruction Fetch + InstrProhibited = 20, + /// Reserved + Reserved21 = 21, + /// Reserved + Reserved22 = 22, + /// Reserved + Reserved23 = 23, + /// Dtlb Miss (No Dtlb Entry Matches, Hw Refill Also Missed) + DtlbMiss = 24, + /// Dtlb Multihit (Multiple Dtlb Entries Match) + DtlbMultiHit = 25, + /// Ring Privilege Violation On Load Or Store + LoadStoreRing = 26, + /// Size Restriction On Load/Store (Not Implemented) + Reserved27 = 27, + /// Cache Attribute Does Not Allow Load + LoadProhibited = 28, + /// Cache Attribute Does Not Allow Store + StoreProhibited = 29, + /// Reserved + Reserved30 = 30, + /// Reserved + Reserved31 = 31, + /// Access To Coprocessor 0 When Disabled + Cp0Disabled = 32, + /// Access To Coprocessor 1 When Disabled + Cp1Disabled = 33, + /// Access To Coprocessor 2 When Disabled + Cp2Disabled = 34, + /// Access To Coprocessor 3 When Disabled + Cp3Disabled = 35, + /// Access To Coprocessor 4 When Disabled + Cp4Disabled = 36, + /// Access To Coprocessor 5 When Disabled + Cp5Disabled = 37, + /// Access To Coprocessor 6 When Disabled + Cp6Disabled = 38, + /// Access To Coprocessor 7 When Disabled + Cp7Disabled = 39, + + None = 255, +} + +#[doc(hidden)] +#[allow(missing_docs, non_snake_case)] +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct Context { + pub PC: u32, + pub PS: u32, + pub A0: u32, + pub A1: u32, + pub A2: u32, + pub A3: u32, + pub A4: u32, + pub A5: u32, + pub A6: u32, + pub A7: u32, + pub A8: u32, + pub A9: u32, + pub A10: u32, + pub A11: u32, + pub A12: u32, + pub A13: u32, + pub A14: u32, + pub A15: u32, + pub SAR: u32, + pub EXCCAUSE: u32, + pub EXCVADDR: u32, + pub LBEG: u32, + pub LEND: u32, + pub LCOUNT: u32, + pub THREADPTR: u32, + pub SCOMPARE1: u32, + pub BR: u32, + pub ACCLO: u32, + pub ACCHI: u32, + pub M0: u32, + pub M1: u32, + pub M2: u32, + pub M3: u32, + pub F64R_LO: u32, + pub F64R_HI: u32, + pub F64S: u32, + pub FCR: u32, + pub FSR: u32, + pub F0: u32, + pub F1: u32, + pub F2: u32, + pub F3: u32, + pub F4: u32, + pub F5: u32, + pub F6: u32, + pub F7: u32, + pub F8: u32, + pub F9: u32, + pub F10: u32, + pub F11: u32, + pub F12: u32, + pub F13: u32, + pub F14: u32, + pub F15: u32, +} + +impl core::fmt::Debug for Context { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!( + fmt, + "Context +PC=0x{:08x} PS=0x{:08x} +A0=0x{:08x} A1=0x{:08x} A2=0x{:08x} A3=0x{:08x} A4=0x{:08x} +A5=0x{:08x} A6=0x{:08x} A7=0x{:08x} A8=0x{:08x} A9=0x{:08x} +A10=0x{:08x} A11=0x{:08x} A12=0x{:08x} A13=0x{:08x} A14=0x{:08x} +A15=0x{:08x} +SAR={:08x} +EXCCAUSE=0x{:08x} EXCVADDR=0x{:08x} +LBEG=0x{:08x} LEND=0x{:08x} LCOUNT=0x{:08x} +THREADPTR=0x{:08x} +SCOMPARE1=0x{:08x} +BR=0x{:08x} +ACCLO=0x{:08x} ACCHI=0x{:08x} +M0=0x{:08x} M1=0x{:08x} M2=0x{:08x} M3=0x{:08x} +F64R_LO=0x{:08x} F64R_HI=0x{:08x} F64S=0x{:08x} +FCR=0x{:08x} FSR=0x{:08x} +F0=0x{:08x} F1=0x{:08x} F2=0x{:08x} F3=0x{:08x} F4=0x{:08x} +F5=0x{:08x} F6=0x{:08x} F7=0x{:08x} F8=0x{:08x} F9=0x{:08x} +F10=0x{:08x} F11=0x{:08x} F12=0x{:08x} F13=0x{:08x} F14=0x{:08x} +F15=0x{:08x} +", + self.PC, + self.PS, + self.A0, + self.A1, + self.A2, + self.A3, + self.A4, + self.A5, + self.A6, + self.A7, + self.A8, + self.A9, + self.A10, + self.A11, + self.A12, + self.A13, + self.A14, + self.A15, + self.SAR, + self.EXCCAUSE, + self.EXCVADDR, + self.LBEG, + self.LEND, + self.LCOUNT, + self.THREADPTR, + self.SCOMPARE1, + self.BR, + self.ACCLO, + self.ACCHI, + self.M0, + self.M1, + self.M2, + self.M3, + self.F64R_LO, + self.F64R_HI, + self.F64S, + self.FCR, + self.FSR, + self.F0, + self.F1, + self.F2, + self.F3, + self.F4, + self.F5, + self.F6, + self.F7, + self.F8, + self.F9, + self.F10, + self.F11, + self.F12, + self.F13, + self.F14, + self.F15, + ) + } +} + +/// Get an array of backtrace addresses. +pub fn backtrace() -> [Option; MAX_BACKTRACE_ADDRESSES] { + let sp = unsafe { + let mut _tmp: u32; + asm!("mov {0}, a1", out(reg) _tmp); + _tmp + }; + + backtrace_internal(sp, 1) +} + +pub(crate) fn sanitize_address(address: u32) -> u32 { + (address & 0x3fff_ffff) | 0x4000_0000 +} + +pub(crate) fn backtrace_internal( + sp: u32, + suppress: i32, +) -> [Option; MAX_BACKTRACE_ADDRESSES] { + let mut result = [None; 10]; + let mut index = 0; + + let mut fp = sp; + let mut suppress = suppress; + let mut old_address = 0; + + loop { + unsafe { + let address = sanitize_address((fp as *const u32).offset(-4).read_volatile()); // RA/PC + fp = (fp as *const u32).offset(-3).read_volatile(); // next FP + + if old_address == address { + break; + } + + old_address = address; + + if address == 0 { + break; + } + + if !crate::is_valid_ram_address(fp) { + break; + } + + if fp == 0 { + break; + } + + if suppress == 0 { + result[index] = Some(address as usize); + index += 1; + + if index >= MAX_BACKTRACE_ADDRESSES { + break; + } + } else { + suppress -= 1; + } + } + } + + result +} diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 5297baac024..8059a054b9a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -25,7 +25,7 @@ embedded-hal-async = "1.0.0" embedded-hal-bus = "0.1.0" embedded-io-async = "0.6.1" esp-alloc = { version = "0.3.0", path = "../esp-alloc" } -esp-backtrace = { version = "0.11.1", features = ["exception-handler", "panic-handler", "println"] } +esp-backtrace = { version = "0.11.1", path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] } esp-hal = { version = "0.17.0", path = "../esp-hal", features = ["log"] } esp-hal-smartled = { version = "0.10.0", path = "../esp-hal-smartled", optional = true } esp-ieee802154 = { version = "0.1.0", path = "../esp-ieee802154", optional = true } @@ -79,7 +79,3 @@ incremental = false opt-level = 3 lto = 'fat' overflow-checks = false - -# TODO: Remove patch once `esp-backtrace` package has been added to repository -[patch.crates-io] -esp-println = { version = "0.9.1", path = "../esp-println" } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 062c585163d..cdb9ea15ebb 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -18,11 +18,16 @@ pub mod cargo; #[derive(Debug, Clone, Copy, PartialEq, Eq, Display, EnumIter, ValueEnum)] #[strum(serialize_all = "kebab-case")] pub enum Package { + EspAlloc, + EspBacktrace, + EspBuild, EspHal, EspHalProcmacros, EspHalSmartled, EspIeee802154, EspLpHal, + EspMetadata, + EspPrintln, EspRiscvRt, Examples, HilTest,