* Print panic messages using semihosting * Don't use defmt's asserts * Make RA_OFFSET available without panic-handler * Re-add defmt imports where missing * Revert unintended test change * Initialise hal in critical-section test * Disable defmt in tests by default
425 lines
15 KiB
Rust
425 lines
15 KiB
Rust
use core::{arch::asm, fmt::Display};
|
|
|
|
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
|
|
#[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(())
|
|
}
|
|
}
|
|
|
|
/// Get an array of backtrace addresses.
|
|
pub fn backtrace() -> [Option<usize>; 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<usize>; 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;
|
|
|
|
// the address is 0 but we sanitized the address - then 0 becomes 0x40000000
|
|
if address == 0x40000000 {
|
|
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
|
|
}
|