From 3c12be8d2491e07ad78318b8410e696df331947b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Fri, 1 Aug 2025 09:57:42 +0200 Subject: [PATCH] Move exception handler from esp-backtrace to esp-hal (#3887) * Move exception handler from esp-backtrace to esp-hal * CHANGELOG.md * Fix * Only use defmt compatible display hints * Workaround xtensa-lx-rt and riscv-rt not implementing defmt::Format * Add `defmt` feature to xtensa-lx-rt and esp-riscv-rt * Favor feature gates over random `allow`s - remove unnecessary `allow`s --- esp-backtrace/CHANGELOG.md | 1 + esp-backtrace/Cargo.toml | 8 +- esp-backtrace/src/lib.rs | 69 +---- esp-backtrace/src/riscv.rs | 151 ----------- esp-backtrace/src/xtensa.rs | 354 +------------------------- esp-hal/CHANGELOG.md | 1 + esp-hal/Cargo.toml | 8 +- esp-hal/src/exception_handler/mod.rs | 51 ++++ esp-hal/src/lib.rs | 3 + esp-riscv-rt/CHANGELOG.md | 1 + esp-riscv-rt/Cargo.toml | 5 + esp-riscv-rt/src/lib.rs | 1 + examples/Cargo.toml | 2 +- hil-test/Cargo.toml | 2 +- qa-test/Cargo.toml | 2 +- xtensa-lx-rt/CHANGELOG.md | 2 + xtensa-lx-rt/Cargo.toml | 4 + xtensa-lx-rt/src/exception.rs | 1 + xtensa-lx-rt/src/exception/context.rs | 1 + 19 files changed, 95 insertions(+), 572 deletions(-) create mode 100644 esp-hal/src/exception_handler/mod.rs diff --git a/esp-backtrace/CHANGELOG.md b/esp-backtrace/CHANGELOG.md index 5e00bdea3..bd8430bbb 100644 --- a/esp-backtrace/CHANGELOG.md +++ b/esp-backtrace/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- the `exception-handler` feature got removed (#3887) ## [v0.17.0] - 2025-07-16 diff --git a/esp-backtrace/Cargo.toml b/esp-backtrace/Cargo.toml index 0ddf52155..97c452649 100644 --- a/esp-backtrace/Cargo.toml +++ b/esp-backtrace/Cargo.toml @@ -53,13 +53,11 @@ print-float-registers = [] # TODO support esp32p4 # additional functionality: ## Print messages in red colors = [] -## Invoke the extern function `custom_halt()` instead of doing a loop {} in case of a panic or exception +## Invoke the extern function `custom_halt()` instead of doing a loop {} in case of a panic custom-halt = [] -## Invoke the extern function `custom_pre_backtrace()` before handling a panic or exception +## Invoke the extern function `custom_pre_backtrace()` before handling a panic custom-pre-backtrace = [] -## Turn unhandled exceptions into panics -exception-handler = [] -## Halt both CPUs on ESP32 / ESP32-S3 instead of doing a `loop {}` in case of a panic or exception +## Halt both CPUs on ESP32 / ESP32-S3 instead of doing a `loop {}` in case of a panic halt-cores = [] ## Include a panic handler panic-handler = [] diff --git a/esp-backtrace/src/lib.rs b/esp-backtrace/src/lib.rs index 74538fbeb..145321f62 100644 --- a/esp-backtrace/src/lib.rs +++ b/esp-backtrace/src/lib.rs @@ -1,6 +1,5 @@ //! ## Feature Flags #![doc = document_features::document_features!()] -#![allow(rustdoc::bare_urls, unused_macros)] #![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))] //! This is a lightweight crate for obtaining backtraces during panics, exceptions, and hard faults //! on Espressif devices. It provides optional panic and exception handlers and supports a range of @@ -60,24 +59,26 @@ impl BacktraceFrame { } } +#[cfg(feature = "panic-handler")] const RESET: &str = "\u{001B}[0m"; +#[cfg(feature = "panic-handler")] const RED: &str = "\u{001B}[31m"; -#[cfg(feature = "defmt")] +#[cfg(all(feature = "panic-handler", feature = "defmt"))] macro_rules! println { ($($arg:tt)*) => { defmt::error!($($arg)*); }; } -#[cfg(all(feature = "println", not(feature = "defmt")))] +#[cfg(all(feature = "panic-handler", feature = "println"))] macro_rules! println { ($($arg:tt)*) => { esp_println::println!($($arg)*); }; } -#[allow(unused, unused_variables)] +#[cfg(feature = "panic-handler")] fn set_color_code(code: &str) { #[cfg(all(feature = "colors", feature = "println"))] { @@ -94,10 +95,12 @@ pub mod arch; fn panic_handler(info: &core::panic::PanicInfo) -> ! { pre_backtrace(); + set_color_code(RED); println!(""); println!("====================== PANIC ======================"); println!("{}", info); + set_color_code(RESET); println!(""); println!("Backtrace:"); @@ -117,53 +120,6 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { abort(); } -#[cfg(all(feature = "exception-handler", target_arch = "xtensa"))] -#[unsafe(no_mangle)] -#[unsafe(link_section = ".rwtext")] -unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) { - panic!("\n\nException occurred '{}'\n{:?}", cause, context); -} - -#[cfg(all(feature = "exception-handler", target_arch = "riscv32"))] -#[unsafe(export_name = "ExceptionHandler")] -fn exception_handler(context: &arch::TrapFrame) -> ! { - let mepc = context.pc; - let code = context.mcause & 0xff; - let mtval = context.mtval; - - if code == 14 { - panic!( - "Stack overflow detected at 0x{:x} called by 0x{:x}", - mepc, context.ra - ); - } 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", - }; - - panic!( - "Exception '{}' mepc=0x{:08x}, mtval=0x{:08x}\n{:?}", - code, mepc, mtval, context - ); - } -} - // 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 @@ -219,7 +175,7 @@ fn is_valid_ram_address(address: u32) -> bool { true } -#[allow(unused)] +#[cfg(feature = "panic-handler")] fn halt() -> ! { cfg_if::cfg_if! { if #[cfg(feature = "custom-halt")] { @@ -268,11 +224,10 @@ fn halt() -> ! { } } - #[allow(clippy::empty_loop)] loop {} } -#[allow(unused)] +#[cfg(feature = "panic-handler")] fn pre_backtrace() { #[cfg(feature = "custom-pre-backtrace")] { @@ -281,18 +236,14 @@ fn pre_backtrace() { } unsafe { custom_pre_backtrace() } } - - set_color_code(RED); } -#[allow(unused)] +#[cfg(feature = "panic-handler")] fn abort() -> ! { println!(""); println!(""); println!(""); - set_color_code(RESET); - cfg_if::cfg_if! { if #[cfg(feature = "semihosting")] { semihosting::process::abort(); diff --git a/esp-backtrace/src/riscv.rs b/esp-backtrace/src/riscv.rs index 6308449e5..7f4819c2d 100644 --- a/esp-backtrace/src/riscv.rs +++ b/esp-backtrace/src/riscv.rs @@ -7,159 +7,8 @@ use crate::{Backtrace, BacktraceFrame}; // we get better results (especially if the caller was the last instruction 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 -#[allow(unused)] pub(super) const RA_OFFSET: usize = 4; -/// Registers saved in trap handler -#[doc(hidden)] -#[derive(Default, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C)] -#[cfg(feature = "exception-handler")] -pub(crate) struct TrapFrame { - /// Return address, stores the address to return to after a function call or - /// interrupt. - pub ra: usize, - /// Temporary register t0, used for intermediate values. - pub t0: usize, - /// Temporary register t1, used for intermediate values. - pub t1: usize, - /// Temporary register t2, used for intermediate values. - pub t2: usize, - /// Temporary register t3, used for intermediate values. - pub t3: usize, - /// Temporary register t4, used for intermediate values. - pub t4: usize, - /// Temporary register t5, used for intermediate values. - pub t5: usize, - /// Temporary register t6, used for intermediate values. - pub t6: usize, - /// Argument register a0, typically used to pass the first argument to a - /// function. - pub a0: usize, - /// Argument register a1, typically used to pass the second argument to a - /// function. - pub a1: usize, - /// Argument register a2, typically used to pass the third argument to a - /// function. - pub a2: usize, - /// Argument register a3, typically used to pass the fourth argument to a - /// function. - pub a3: usize, - /// Argument register a4, typically used to pass the fifth argument to a - /// function. - pub a4: usize, - /// Argument register a5, typically used to pass the sixth argument to a - /// function. - pub a5: usize, - /// Argument register a6, typically used to pass the seventh argument to a - /// function. - pub a6: usize, - /// Argument register a7, typically used to pass the eighth argument to a - /// function. - pub a7: usize, - /// Saved register s0, used to hold values across function calls. - pub s0: usize, - /// Saved register s1, used to hold values across function calls. - pub s1: usize, - /// Saved register s2, used to hold values across function calls. - pub s2: usize, - /// Saved register s3, used to hold values across function calls. - pub s3: usize, - /// Saved register s4, used to hold values across function calls. - pub s4: usize, - /// Saved register s5, used to hold values across function calls. - pub s5: usize, - /// Saved register s6, used to hold values across function calls. - pub s6: usize, - /// Saved register s7, used to hold values across function calls. - pub s7: usize, - /// Saved register s8, used to hold values across function calls. - pub s8: usize, - /// Saved register s9, used to hold values across function calls. - pub s9: usize, - /// Saved register s10, used to hold values across function calls. - pub s10: usize, - /// Saved register s11, used to hold values across function calls. - pub s11: usize, - /// Global pointer register, holds the address of the global data area. - pub gp: usize, - /// Thread pointer register, holds the address of the thread-local storage - /// area. - pub tp: usize, - /// Stack pointer register, holds the address of the top of the stack. - pub sp: usize, - /// Program counter, stores the address of the next instruction to be - /// executed. - pub pc: usize, - /// Machine status register, holds the current status of the processor, - /// including interrupt enable bits and privilege mode. - pub mstatus: usize, - /// Machine cause register, contains the reason for the trap (e.g., - /// exception or interrupt number). - pub mcause: usize, - /// Machine trap value register, contains additional information about the - /// trap (e.g., faulting address). - pub mtval: usize, -} - -#[cfg(feature = "exception-handler")] -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. diff --git a/esp-backtrace/src/xtensa.rs b/esp-backtrace/src/xtensa.rs index 4d427d380..28541c3d3 100644 --- a/esp-backtrace/src/xtensa.rs +++ b/esp-backtrace/src/xtensa.rs @@ -1,4 +1,4 @@ -use core::{arch::asm, fmt::Display}; +use core::arch::asm; use crate::{Backtrace, BacktraceFrame}; @@ -6,360 +6,8 @@ use crate::{Backtrace, BacktraceFrame}; // 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 -#[allow(unused)] pub(super) const RA_OFFSET: usize = 3; -/// Exception Cause -#[doc(hidden)] -#[derive(Debug, Clone, Copy, PartialEq)] -#[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 - None = 255, -} - -impl Display for ExceptionCause { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - if *self == Self::Cp0Disabled { - write!( - f, - "Cp0Disabled (Access to the floating point coprocessor is not allowed. You may want to enable the `float-save-restore` feature of the `xtensa-lx-rt` crate.)" - ) - } else { - write!(f, "{self:?}") - } - } -} - -#[doc(hidden)] -#[allow(non_snake_case)] -#[derive(Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C)] -pub struct Context { - /// Program counter, stores the address of the next instruction to be - /// executed. - pub PC: u32, - /// Processor status, holds various status flags for the CPU. - pub PS: u32, - /// General-purpose register A0 used for data storage and manipulation. - pub A0: u32, - /// General-purpose register A1 used for data storage and manipulation. - pub A1: u32, - /// General-purpose register A2 used for data storage and manipulation. - pub A2: u32, - /// General-purpose register A3 used for data storage and manipulation. - pub A3: u32, - /// General-purpose register A4 used for data storage and manipulation. - pub A4: u32, - /// General-purpose register A5 used for data storage and manipulation. - pub A5: u32, - /// General-purpose register A6 used for data storage and manipulation. - pub A6: u32, - /// General-purpose register A7 used for data storage and manipulation. - pub A7: u32, - /// General-purpose register A8 used for data storage and manipulation. - pub A8: u32, - /// General-purpose register A9 used for data storage and manipulation. - pub A9: u32, - /// General-purpose register A10 used for data storage and manipulation. - pub A10: u32, - /// General-purpose register A11 used for data storage and manipulation. - pub A11: u32, - /// General-purpose register A12 used for data storage and manipulation. - pub A12: u32, - /// General-purpose register A13 used for data storage and manipulation. - pub A13: u32, - /// General-purpose register A14 used for data storage and manipulation. - pub A14: u32, - /// General-purpose register A15 used for data storage and manipulation. - pub A15: u32, - /// Shift amount register, used for shift and rotate instructions. - pub SAR: u32, - /// Exception cause, indicates the reason for the last exception. - pub EXCCAUSE: u32, - /// Exception address, holds the address related to the exception. - pub EXCVADDR: u32, - /// Loop start address, used in loop instructions. - pub LBEG: u32, - /// Loop end address, used in loop instructions. - pub LEND: u32, - /// Loop counter, used to count iterations in loop instructions. - pub LCOUNT: u32, - /// Thread pointer, used for thread-local storage. - pub THREADPTR: u32, - /// Compare register, used for certain compare instructions. - pub SCOMPARE1: u32, - /// Break register, used for breakpoint-related operations. - pub BR: u32, - /// Accumulator low register, used for extended arithmetic operations. - pub ACCLO: u32, - /// Accumulator high register, used for extended arithmetic operations. - pub ACCHI: u32, - /// Additional register M0 used for special operations. - pub M0: u32, - /// Additional register M1 used for special operations. - pub M1: u32, - /// Additional register M2 used for special operations. - pub M2: u32, - /// Additional register M3 used for special operations. - pub M3: u32, - /// 64-bit floating-point register (low part), available if the - /// `print-float-registers` feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F64R_LO: u32, - /// 64-bit floating-point register (high part), available if the - /// `print-float-registers` feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F64R_HI: u32, - /// Floating-point status register, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F64S: u32, - /// Floating-point control register, available if the - /// `print-float-registers` feature is enabled. - #[cfg(feature = "print-float-registers")] - pub FCR: u32, - /// Floating-point status register, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub FSR: u32, - /// Floating-point register F0, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F0: u32, - /// Floating-point register F1, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F1: u32, - /// Floating-point register F2, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F2: u32, - /// Floating-point register F3, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F3: u32, - /// Floating-point register F4, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F4: u32, - /// Floating-point register F5, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F5: u32, - /// Floating-point register F6, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F6: u32, - /// Floating-point register F7, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F7: u32, - /// Floating-point register F8, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F8: u32, - /// Floating-point register F9, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F9: u32, - /// Floating-point register F10, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F10: u32, - /// Floating-point register F11, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F11: u32, - /// Floating-point register F12, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F12: u32, - /// Floating-point register F13, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F13: u32, - /// Floating-point register F14, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - pub F14: u32, - /// Floating-point register F15, available if the `print-float-registers` - /// feature is enabled. - #[cfg(feature = "print-float-registers")] - 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} -", - 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, - )?; - #[cfg(feature = "print-float-registers")] - write!( - fmt, - "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.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, - )?; - - Ok(()) - } -} - /// This function returns the caller's frame pointer. #[inline(never)] #[cold] diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index dd912960d..d28eb26ca 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A reimplemntation of the `assign_resources!` macro (#3809) - `TrngSource` to manage random number generator entropy (#3829) - On RISC-V you can opt-out of nested interrupts for an interrupt handler by using `new_not_nested` (#3875) +- A new default feature `exception-handler` was added (#3887) ### Changed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index e2ace3175..81d224a53 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -99,7 +99,7 @@ serde = { version = "1.0.219", default-features = false, features = ["der jiff = { version = "0.2.10", default-features = false, features = ["static"] } [features] -default = ["rt"] +default = ["rt", "exception-handler"] # These features are considered private and unstable. They are not covered by # semver guarantees and may change or be removed without notice. @@ -195,6 +195,10 @@ rt = [ "esp32s3?/rt", ] +## Enable a simple exception handler turning exceptions into panics. +## +## If you are depending on `esp-hal` as a library, you should *not* enable the `exception-handler` feature under any circumstance. +exception-handler = [] #! ### Logging Feature Flags ## Enable logging output using version 0.4 of the `log` crate. @@ -217,6 +221,8 @@ defmt = [ "esp32s2?/defmt", "esp32s3?/defmt", "fugit/defmt", + "esp-riscv-rt?/defmt", + "xtensa-lx-rt?/defmt", ] #! ### PSRAM Feature Flags diff --git a/esp-hal/src/exception_handler/mod.rs b/esp-hal/src/exception_handler/mod.rs new file mode 100644 index 000000000..3f9393d02 --- /dev/null +++ b/esp-hal/src/exception_handler/mod.rs @@ -0,0 +1,51 @@ +use crate::trapframe::TrapFrame; + +#[cfg(xtensa)] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".rwtext")] +unsafe extern "C" fn __user_exception( + cause: xtensa_lx_rt::exception::ExceptionCause, + context: &TrapFrame, +) { + panic!("\n\nException occurred '{:?}'\n{:?}", cause, context); +} + +#[cfg(riscv)] +#[unsafe(no_mangle)] +unsafe extern "C" fn ExceptionHandler(context: &TrapFrame) -> ! { + let mepc = riscv::register::mepc::read(); + let code = riscv::register::mcause::read().code(); + let mtval = riscv::register::mtval::read(); + + if code == 14 { + panic!( + "Stack overflow detected at 0x{:x} called by 0x{:x}", + mepc, context.ra + ); + } 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", + }; + + panic!( + "Exception '{}' mepc={:08x}, mtval={:08x}\n{:?}", + code, mepc, mtval, context + ); + } +} diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index adf0fae5e..a0f10834d 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -263,6 +263,9 @@ pub use procmacros::load_lp_code; #[cfg_attr(not(feature = "unstable"), allow(unused))] pub use procmacros::{handler, ram}; +#[cfg(all(feature = "rt", feature = "exception-handler"))] +mod exception_handler; + // can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727 #[doc(hidden)] macro_rules! unstable_module { diff --git a/esp-riscv-rt/CHANGELOG.md b/esp-riscv-rt/CHANGELOG.md index 7f135eba0..1357010dd 100644 --- a/esp-riscv-rt/CHANGELOG.md +++ b/esp-riscv-rt/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- A new feature `defmt` which implements `defmt::Format` on `TrapFrame` (#3887) ### Changed diff --git a/esp-riscv-rt/Cargo.toml b/esp-riscv-rt/Cargo.toml index 668a77dce..0f36e5fed 100644 --- a/esp-riscv-rt/Cargo.toml +++ b/esp-riscv-rt/Cargo.toml @@ -17,14 +17,19 @@ test = false [dependencies] document-features = "0.2.11" +defmt = {version = "1.0.1", optional = true} riscv = "0.14.0" riscv-rt-macros = "0.5.0" [features] ## Indicate that the device supports `mie` and `mip` CSRs. has-mie-mip = [] + ## Indicate that the device has RTC RAM. rtc-ram = [] +## Implement `defmt::Format` +defmt = ["dep:defmt"] + # This feature is intended for testing; you probably don't want to enable it: ci = ["has-mie-mip", "rtc-ram"] diff --git a/esp-riscv-rt/src/lib.rs b/esp-riscv-rt/src/lib.rs index 6e2afe451..7abbafc53 100644 --- a/esp-riscv-rt/src/lib.rs +++ b/esp-riscv-rt/src/lib.rs @@ -70,6 +70,7 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { /// Registers saved in trap handler #[derive(Debug, Default, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct TrapFrame { /// Return address, stores the address to return to after a function call or diff --git a/examples/Cargo.toml b/examples/Cargo.toml index fd429a4c2..c606adb23 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -22,7 +22,7 @@ embedded-io = { version = "0.6.1", default-features = false } embedded-io-async = "0.6.1" embedded-storage = "0.3.1" esp-alloc = { path = "../esp-alloc" } -esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] } +esp-backtrace = { path = "../esp-backtrace", features = ["panic-handler", "println"] } esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" } esp-hal = { path = "../esp-hal", features = ["log-04"] } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 8587cc56b..2d5aabdf5 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -249,7 +249,7 @@ embedded-can = "0.4.1" embedded-hal-async = "1.0.0" embedded-hal-nb = "1.0.0" esp-alloc = { path = "../esp-alloc", optional = true } -esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "defmt", "semihosting"] } +esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["defmt", "semihosting"] } esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" } esp-hal = { path = "../esp-hal" } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } diff --git a/qa-test/Cargo.toml b/qa-test/Cargo.toml index 4c699998c..06ca9a49d 100644 --- a/qa-test/Cargo.toml +++ b/qa-test/Cargo.toml @@ -14,7 +14,7 @@ embassy-sync = "0.6.1" embedded-graphics = "0.8.1" embedded-hal-async = "1.0.0" esp-alloc = { path = "../esp-alloc" } -esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] } +esp-backtrace = { path = "../esp-backtrace", features = ["panic-handler", "println"] } esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" } esp-hal = { path = "../esp-hal", features = ["unstable", "log-04"] } esp-hal-embassy = { path = "../esp-hal-embassy" } diff --git a/xtensa-lx-rt/CHANGELOG.md b/xtensa-lx-rt/CHANGELOG.md index f4bc8951c..bec1594ba 100644 --- a/xtensa-lx-rt/CHANGELOG.md +++ b/xtensa-lx-rt/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- A new feature `defmt` which implements `defmt::Format` on `Context` (#3887) + ### Changed diff --git a/xtensa-lx-rt/Cargo.toml b/xtensa-lx-rt/Cargo.toml index 1893360ea..0a25e7cb5 100644 --- a/xtensa-lx-rt/Cargo.toml +++ b/xtensa-lx-rt/Cargo.toml @@ -17,6 +17,7 @@ test = false [dependencies] document-features = "0.2.11" +defmt = {version = "1.0.1", optional = true} macros = { version = "0.4.0", package = "xtensa-lx-rt-proc-macros", path = "../xtensa-lx-rt-proc-macros" } r0 = "1.0.0" xtensa-lx = { version = "0.12.0", path = "../xtensa-lx" } @@ -27,5 +28,8 @@ xtensa-lx = { version = "0.12.0", path = "../xtensa-lx" } ## Save and restore float registers for exceptions float-save-restore = [] +## Implement `defmt::Format` +defmt = ["dep:defmt"] + [lints.rust] unexpected_cfgs = "allow" diff --git a/xtensa-lx-rt/src/exception.rs b/xtensa-lx-rt/src/exception.rs index c24efaa88..0ecf1c059 100644 --- a/xtensa-lx-rt/src/exception.rs +++ b/xtensa-lx-rt/src/exception.rs @@ -34,6 +34,7 @@ pub use context::Context; /// vectors). #[allow(unused)] #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub enum ExceptionCause { /// Illegal Instruction diff --git a/xtensa-lx-rt/src/exception/context.rs b/xtensa-lx-rt/src/exception/context.rs index 8ac32c750..c811be1e9 100644 --- a/xtensa-lx-rt/src/exception/context.rs +++ b/xtensa-lx-rt/src/exception/context.rs @@ -6,6 +6,7 @@ use super::ExceptionCause; #[repr(C)] #[allow(non_snake_case)] #[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Context { pub PC: u32, pub PS: u32,