1#![allow(rustdoc::bare_urls, unused_macros)]
2#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))]
3#![doc = include_str!("../README.md")]
4#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
5#![no_std]
6
7#[cfg(feature = "defmt")]
8use defmt as _;
9#[cfg(feature = "println")]
10use esp_println as _;
11
12const MAX_BACKTRACE_ADDRESSES: usize =
13 esp_config::esp_config_int!(usize, "ESP_BACKTRACE_CONFIG_BACKTRACE_FRAMES");
14
15pub struct Backtrace(pub(crate) heapless::Vec<BacktraceFrame, MAX_BACKTRACE_ADDRESSES>);
16
17impl Backtrace {
18 #[inline]
20 pub fn capture() -> Self {
21 arch::backtrace()
22 }
23
24 #[inline]
25 #[cfg(feature = "exception-handler")]
26 fn from_sp(sp: u32) -> Self {
27 arch::backtrace_internal(sp, 0)
28 }
29
30 #[inline]
32 pub fn frames(&self) -> &[BacktraceFrame] {
33 &self.0
34 }
35}
36
37pub struct BacktraceFrame {
38 pub(crate) pc: usize,
39}
40
41impl BacktraceFrame {
42 pub fn program_counter(&self) -> usize {
43 self.pc - crate::arch::RA_OFFSET
44 }
45}
46
47const RESET: &str = "\u{001B}[0m";
48const RED: &str = "\u{001B}[31m";
49
50#[cfg(feature = "defmt")]
51macro_rules! println {
52 ($($arg:tt)*) => {
53 defmt::error!($($arg)*);
54 };
55}
56
57#[cfg(all(feature = "println", not(feature = "defmt")))]
58macro_rules! println {
59 ($($arg:tt)*) => {
60 esp_println::println!($($arg)*);
61 };
62}
63
64#[allow(unused, unused_variables)]
65fn set_color_code(code: &str) {
66 #[cfg(all(feature = "colors", feature = "println"))]
67 {
68 println!("{}", code);
69 }
70}
71
72#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
73#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
74pub mod arch;
75
76#[cfg(feature = "panic-handler")]
77#[panic_handler]
78fn panic_handler(info: &core::panic::PanicInfo) -> ! {
79 pre_backtrace();
80
81 println!("");
82 println!("====================== PANIC ======================");
83
84 println!("{}", info);
85
86 println!("");
87 println!("Backtrace:");
88 println!("");
89
90 let backtrace = Backtrace::capture();
91 #[cfg(target_arch = "riscv32")]
92 if backtrace.frames().is_empty() {
93 println!(
94 "No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)"
95 );
96 }
97 for frame in backtrace.frames() {
98 println!("0x{:x}", frame.program_counter());
99 }
100
101 abort();
102}
103
104#[cfg(all(feature = "exception-handler", target_arch = "xtensa"))]
105#[unsafe(no_mangle)]
106#[unsafe(link_section = ".rwtext")]
107unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) {
108 pre_backtrace();
109
110 println!("\n\nException occurred '{}'", cause);
111 println!("{:?}", context);
112
113 let backtrace = Backtrace::from_sp(context.A1);
114 for frame in backtrace.frames() {
115 println!("0x{:x}", frame.program_counter());
116 }
117
118 abort();
119}
120
121#[cfg(all(feature = "exception-handler", target_arch = "riscv32"))]
122#[unsafe(export_name = "ExceptionHandler")]
123fn exception_handler(context: &arch::TrapFrame) -> ! {
124 pre_backtrace();
125
126 let mepc = context.pc;
127 let code = context.mcause & 0xff;
128 let mtval = context.mtval;
129
130 if code == 14 {
131 println!("");
132 println!(
133 "Stack overflow detected at 0x{:x} called by 0x{:x}",
134 mepc, context.ra
135 );
136 println!("");
137 } else {
138 let code = match code {
139 0 => "Instruction address misaligned",
140 1 => "Instruction access fault",
141 2 => "Illegal instruction",
142 3 => "Breakpoint",
143 4 => "Load address misaligned",
144 5 => "Load access fault",
145 6 => "Store/AMO address misaligned",
146 7 => "Store/AMO access fault",
147 8 => "Environment call from U-mode",
148 9 => "Environment call from S-mode",
149 10 => "Reserved",
150 11 => "Environment call from M-mode",
151 12 => "Instruction page fault",
152 13 => "Load page fault",
153 14 => "Reserved",
154 15 => "Store/AMO page fault",
155 _ => "UNKNOWN",
156 };
157
158 println!(
159 "Exception '{}' mepc=0x{:08x}, mtval=0x{:08x}",
160 code, mepc, mtval
161 );
162
163 println!("{:?}", context);
164
165 let backtrace = Backtrace::from_sp(context.s0 as u32);
166 let frames = backtrace.frames();
167 if frames.is_empty() {
168 println!(
169 "No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)"
170 );
171 }
172 for frame in backtrace.frames() {
173 println!("0x{:x}", frame.program_counter());
174 }
175 }
176
177 abort();
178}
179
180fn is_valid_ram_address(address: u32) -> bool {
188 if (address & 0xF) != 0 {
189 return false;
190 }
191
192 #[cfg(feature = "esp32")]
193 if !(0x3FFA_E000..=0x4000_0000).contains(&address) {
194 return false;
195 }
196
197 #[cfg(feature = "esp32c2")]
198 if !(0x3FCA_0000..=0x3FCE_0000).contains(&address) {
199 return false;
200 }
201
202 #[cfg(feature = "esp32c3")]
203 if !(0x3FC8_0000..=0x3FCE_0000).contains(&address) {
204 return false;
205 }
206
207 #[cfg(feature = "esp32c6")]
208 if !(0x4080_0000..=0x4088_0000).contains(&address) {
209 return false;
210 }
211
212 #[cfg(feature = "esp32h2")]
213 if !(0x4080_0000..=0x4085_0000).contains(&address) {
214 return false;
215 }
216
217 #[cfg(feature = "esp32p4")]
218 if !(0x4FF0_0000..=0x4FFC_0000).contains(&address) {
219 return false;
220 }
221
222 #[cfg(feature = "esp32s2")]
223 if !(0x3FFB_0000..=0x4000_0000).contains(&address) {
224 return false;
225 }
226
227 #[cfg(feature = "esp32s3")]
228 if !(0x3FC8_8000..=0x3FD0_0000).contains(&address) {
229 return false;
230 }
231
232 true
233}
234
235#[allow(unused)]
236fn halt() -> ! {
237 cfg_if::cfg_if! {
238 if #[cfg(feature = "custom-halt")] {
239 unsafe extern "Rust" {
241 fn custom_halt() -> !;
242 }
243 unsafe { custom_halt() }
244 } else if #[cfg(any(feature = "esp32", feature = "esp32s3"))] {
245 #[cfg(feature = "esp32")]
247 mod registers {
248 pub(crate) const OPTIONS0: u32 = 0x3ff48000;
249 pub(crate) const SW_CPU_STALL: u32 = 0x3ff480ac;
250 }
251
252 #[cfg(feature = "esp32p4")]
253 mod registers {
254 pub(crate) const SW_CPU_STALL: u32 = 0x50115200;
255 }
256
257 #[cfg(feature = "esp32s3")]
258 mod registers {
259 pub(crate) const OPTIONS0: u32 = 0x60008000;
260 pub(crate) const SW_CPU_STALL: u32 = 0x600080bc;
261 }
262
263 let sw_cpu_stall = registers::SW_CPU_STALL as *mut u32;
264
265 unsafe {
266 let options0 = registers::OPTIONS0 as *mut u32;
272
273 options0.write_volatile(options0.read_volatile() & !(0b1111) | 0b1010);
274
275 sw_cpu_stall.write_volatile(
276 sw_cpu_stall.read_volatile() & !(0b111111 << 20) & !(0b111111 << 26)
277 | (0x21 << 20)
278 | (0x21 << 26),
279 );
280 }
281 }
282 }
283
284 #[allow(clippy::empty_loop)]
285 loop {}
286}
287
288#[allow(unused)]
289fn pre_backtrace() {
290 #[cfg(feature = "custom-pre-backtrace")]
291 {
292 unsafe extern "Rust" {
293 fn custom_pre_backtrace();
294 }
295 unsafe { custom_pre_backtrace() }
296 }
297
298 set_color_code(RED);
299}
300
301#[allow(unused)]
302fn abort() -> ! {
303 println!("");
304 println!("");
305 println!("");
306
307 set_color_code(RESET);
308
309 cfg_if::cfg_if! {
310 if #[cfg(feature = "semihosting")] {
311 semihosting::process::abort();
312 } else {
313 halt();
314 }
315 }
316}