esp_backtrace/riscv.rs
1use core::arch::asm;
2
3use crate::{Backtrace, BacktraceFrame};
4
5// subtract 4 from the return address
6// the return address is the address following the JALR
7// we get better results (especially if the caller was the last instruction in
8// the calling function) if we report the address of the JALR itself
9// even if it was a C.JALR we should get good results using RA - 4
10#[allow(unused)]
11pub(super) const RA_OFFSET: usize = 4;
12
13/// Registers saved in trap handler
14#[doc(hidden)]
15#[derive(Default, Clone, Copy)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[repr(C)]
18#[cfg(feature = "exception-handler")]
19pub(crate) struct TrapFrame {
20 /// Return address, stores the address to return to after a function call or
21 /// interrupt.
22 pub ra: usize,
23 /// Temporary register t0, used for intermediate values.
24 pub t0: usize,
25 /// Temporary register t1, used for intermediate values.
26 pub t1: usize,
27 /// Temporary register t2, used for intermediate values.
28 pub t2: usize,
29 /// Temporary register t3, used for intermediate values.
30 pub t3: usize,
31 /// Temporary register t4, used for intermediate values.
32 pub t4: usize,
33 /// Temporary register t5, used for intermediate values.
34 pub t5: usize,
35 /// Temporary register t6, used for intermediate values.
36 pub t6: usize,
37 /// Argument register a0, typically used to pass the first argument to a
38 /// function.
39 pub a0: usize,
40 /// Argument register a1, typically used to pass the second argument to a
41 /// function.
42 pub a1: usize,
43 /// Argument register a2, typically used to pass the third argument to a
44 /// function.
45 pub a2: usize,
46 /// Argument register a3, typically used to pass the fourth argument to a
47 /// function.
48 pub a3: usize,
49 /// Argument register a4, typically used to pass the fifth argument to a
50 /// function.
51 pub a4: usize,
52 /// Argument register a5, typically used to pass the sixth argument to a
53 /// function.
54 pub a5: usize,
55 /// Argument register a6, typically used to pass the seventh argument to a
56 /// function.
57 pub a6: usize,
58 /// Argument register a7, typically used to pass the eighth argument to a
59 /// function.
60 pub a7: usize,
61 /// Saved register s0, used to hold values across function calls.
62 pub s0: usize,
63 /// Saved register s1, used to hold values across function calls.
64 pub s1: usize,
65 /// Saved register s2, used to hold values across function calls.
66 pub s2: usize,
67 /// Saved register s3, used to hold values across function calls.
68 pub s3: usize,
69 /// Saved register s4, used to hold values across function calls.
70 pub s4: usize,
71 /// Saved register s5, used to hold values across function calls.
72 pub s5: usize,
73 /// Saved register s6, used to hold values across function calls.
74 pub s6: usize,
75 /// Saved register s7, used to hold values across function calls.
76 pub s7: usize,
77 /// Saved register s8, used to hold values across function calls.
78 pub s8: usize,
79 /// Saved register s9, used to hold values across function calls.
80 pub s9: usize,
81 /// Saved register s10, used to hold values across function calls.
82 pub s10: usize,
83 /// Saved register s11, used to hold values across function calls.
84 pub s11: usize,
85 /// Global pointer register, holds the address of the global data area.
86 pub gp: usize,
87 /// Thread pointer register, holds the address of the thread-local storage
88 /// area.
89 pub tp: usize,
90 /// Stack pointer register, holds the address of the top of the stack.
91 pub sp: usize,
92 /// Program counter, stores the address of the next instruction to be
93 /// executed.
94 pub pc: usize,
95 /// Machine status register, holds the current status of the processor,
96 /// including interrupt enable bits and privilege mode.
97 pub mstatus: usize,
98 /// Machine cause register, contains the reason for the trap (e.g.,
99 /// exception or interrupt number).
100 pub mcause: usize,
101 /// Machine trap value register, contains additional information about the
102 /// trap (e.g., faulting address).
103 pub mtval: usize,
104}
105
106#[cfg(feature = "exception-handler")]
107impl core::fmt::Debug for TrapFrame {
108 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
109 write!(
110 fmt,
111 "TrapFrame
112PC=0x{:08x} RA/x1=0x{:08x} SP/x2=0x{:08x} GP/x3=0x{:08x} TP/x4=0x{:08x}
113T0/x5=0x{:08x} T1/x6=0x{:08x} T2/x7=0x{:08x} S0/FP/x8=0x{:08x} S1/x9=0x{:08x}
114A0/x10=0x{:08x} A1/x11=0x{:08x} A2/x12=0x{:08x} A3/x13=0x{:08x} A4/x14=0x{:08x}
115A5/x15=0x{:08x} A6/x16=0x{:08x} A7/x17=0x{:08x} S2/x18=0x{:08x} S3/x19=0x{:08x}
116S4/x20=0x{:08x} S5/x21=0x{:08x} S6/x22=0x{:08x} S7/x23=0x{:08x} S8/x24=0x{:08x}
117S9/x25=0x{:08x} S10/x26=0x{:08x} S11/x27=0x{:08x} T3/x28=0x{:08x} T4/x29=0x{:08x}
118T5/x30=0x{:08x} T6/x31=0x{:08x}
119
120MSTATUS=0x{:08x}
121MCAUSE=0x{:08x}
122MTVAL=0x{:08x}
123 ",
124 self.pc,
125 self.ra,
126 self.gp,
127 self.sp,
128 self.tp,
129 self.t0,
130 self.t1,
131 self.t2,
132 self.s0,
133 self.s1,
134 self.a0,
135 self.a1,
136 self.a2,
137 self.a3,
138 self.a4,
139 self.a5,
140 self.a6,
141 self.a7,
142 self.s2,
143 self.s3,
144 self.s4,
145 self.s5,
146 self.s6,
147 self.s7,
148 self.s8,
149 self.s9,
150 self.s10,
151 self.s11,
152 self.t3,
153 self.t4,
154 self.t5,
155 self.t6,
156 self.mstatus,
157 self.mcause,
158 self.mtval,
159 )
160 }
161}
162
163/// Get an array of backtrace addresses.
164///
165/// This needs `force-frame-pointers` enabled.
166#[inline(never)]
167#[cold]
168pub fn backtrace() -> Backtrace {
169 let fp = unsafe {
170 let mut _tmp: u32;
171 asm!("mv {0}, x8", out(reg) _tmp);
172 _tmp
173 };
174
175 backtrace_internal(fp, 2)
176}
177
178pub(crate) fn backtrace_internal(fp: u32, suppress: u32) -> Backtrace {
179 let mut result = Backtrace(heapless::Vec::new());
180
181 let mut fp = fp;
182 let mut suppress = suppress;
183
184 if !crate::is_valid_ram_address(fp) {
185 return result;
186 }
187
188 while !result.0.is_full() {
189 // RA/PC
190 let address = unsafe { (fp as *const u32).offset(-1).read_volatile() };
191 // next FP
192 fp = unsafe { (fp as *const u32).offset(-2).read_volatile() };
193
194 if address == 0 {
195 break;
196 }
197
198 if !crate::is_valid_ram_address(fp) {
199 break;
200 }
201
202 if suppress == 0 {
203 _ = result.0.push(BacktraceFrame {
204 pc: address as usize,
205 });
206 } else {
207 suppress -= 1;
208 }
209 }
210
211 result
212}