1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_cpu_exception_cortex_m/crash.h"
16
17 #include <cinttypes>
18
19 #include "pw_cpu_exception_cortex_m/cpu_state.h"
20 #include "pw_cpu_exception_cortex_m/util.h"
21 #include "pw_cpu_exception_cortex_m_private/config.h"
22 #include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
23 #include "pw_preprocessor/arch.h"
24
25 namespace pw::cpu_exception::cortex_m {
26
AnalyzeCpuStateAndCrash(const pw_cpu_exception_State & cpu_state,const char * optional_thread_name)27 void AnalyzeCpuStateAndCrash(const pw_cpu_exception_State& cpu_state,
28 const char* optional_thread_name) {
29 const char* thread_name =
30 optional_thread_name == nullptr ? "?" : optional_thread_name;
31 const bool is_nested_fault = cpu_state.extended.hfsr & kHfsrForcedMask;
32 const uint32_t active_faults =
33 cpu_state.extended.cfsr &
34 (kCfsrMemAllErrorsMask | kCfsrBusAllErrorsMask | kCfsrUsageAllErrorsMask);
35 // The expression (n & (n - 1)) is 0 if n is a power of 2, i.e. n has
36 // only 1 bit (fault flag) set. So if it is != 0 there are multiple faults.
37 const bool has_multiple_faults = (active_faults & (active_faults - 1)) != 0;
38
39 // This provides a high-level assessment of the cause of the exception.
40 // These conditionals are ordered by priority to ensure the most critical
41 // issues are highlighted first. These are not mutually exclusive; a bus fault
42 // could occur during the handling of a MPU violation, causing a nested fault.
43 #if _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
44 if (cpu_state.extended.cfsr & kCfsrStkofMask) {
45 if (ProcessStackActive(cpu_state)) {
46 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
47 cpu_state,
48 "PSP stack overflow. Thread=%s PC=0x%08" PRIx32 " LR=0x%08" PRIx32
49 " CFSR=0x%08" PRIx32 " PSP=0x%08" PRIx32 " Nested=%d Multiple=%d",
50 thread_name,
51 cpu_state.base.pc,
52 cpu_state.base.lr,
53 cpu_state.extended.cfsr,
54 cpu_state.extended.psp,
55 is_nested_fault,
56 has_multiple_faults);
57 } else {
58 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
59 cpu_state,
60 "MSP stack overflow. Thread=%s PC=0x%08" PRIx32 " LR=0x%08" PRIx32
61 " CFSR=0x%08" PRIx32 " MSP=0x%08" PRIx32 " Nested=%d Multiple=%d",
62 thread_name,
63 cpu_state.base.pc,
64 cpu_state.base.lr,
65 cpu_state.extended.cfsr,
66 cpu_state.extended.msp,
67 is_nested_fault,
68 has_multiple_faults);
69 }
70 return;
71 }
72 #endif // _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
73
74 // Memory management fault.
75 if (cpu_state.extended.cfsr & kCfsrMemFaultMask) {
76 const bool is_mmfar_valid = cpu_state.extended.cfsr & kCfsrMmarvalidMask;
77 #if PW_CPU_EXCEPTION_CORTEX_M_CRASH_EXTENDED_CPU_ANALYSIS
78 if (cpu_state.extended.cfsr & kCfsrIaccviolMask) {
79 // The PC value stacked for the exception return points to the faulting
80 // instruction.
81 // The processor does not write the fault address to the MMFAR.
82 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
83 cpu_state,
84 "IACCVIOL: MPU violation on instruction fetch. Thread=%s "
85 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
86 " Nested=%d Multiple=%d",
87 thread_name,
88 cpu_state.base.pc,
89 cpu_state.base.lr,
90 cpu_state.extended.cfsr,
91 is_nested_fault,
92 has_multiple_faults);
93 return;
94 }
95 if (cpu_state.extended.cfsr & kCfsrDaccviolMask) {
96 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
97 cpu_state,
98 "DACCVIOL: MPU violation on memory read/write. Thread=%s "
99 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
100 " ValidMmfar=%d MMFAR=0x%08" PRIx32 "Nested=%d Multiple=%d",
101 thread_name,
102 cpu_state.base.pc,
103 cpu_state.base.lr,
104 cpu_state.extended.cfsr,
105 is_mmfar_valid,
106 cpu_state.extended.mmfar,
107 is_nested_fault,
108 has_multiple_faults);
109 return;
110 }
111 if (cpu_state.extended.cfsr & kCfsrMunstkerrMask) {
112 // The processor does not write the fault address to the MMFAR.
113 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
114 cpu_state,
115 "MUNSTKERR: MPU violation on exception return. Thread=%s "
116 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
117 " Nested=%d Multiple=%d",
118 thread_name,
119 cpu_state.base.pc,
120 cpu_state.base.lr,
121 cpu_state.extended.cfsr,
122 is_nested_fault,
123 has_multiple_faults);
124 return;
125 }
126 if (cpu_state.extended.cfsr & kCfsrMstkerrMask) {
127 // The processor does not write the fault address to the MMFAR.
128 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
129 cpu_state,
130 "MSTKERR: MPU violation on exception entry. Thread=%s PC=0x%08" PRIx32
131 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
132 thread_name,
133 cpu_state.base.pc,
134 cpu_state.base.lr,
135 cpu_state.extended.cfsr,
136 is_nested_fault,
137 has_multiple_faults);
138 return;
139 }
140 if (cpu_state.extended.cfsr & kCfsrMlsperrMask) {
141 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
142 cpu_state,
143 "MLSPERR: MPU violation on lazy FPU state preservation. Thread=%s "
144 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
145 " ValidMmfar=%d MMFAR=0x%08" PRIx32 " Nested=%d Multiple=%d",
146 thread_name,
147 cpu_state.base.pc,
148 cpu_state.base.lr,
149 cpu_state.extended.cfsr,
150 is_mmfar_valid,
151 cpu_state.extended.mmfar,
152 is_nested_fault,
153 has_multiple_faults);
154 return;
155 }
156 #endif // PW_CPU_EXCEPTION_CORTEX_M_CRASH_EXTENDED_CPU_ANALYSIS
157
158 PW_CPU_EXCEPTION_CORTEX_M_CRASH(cpu_state,
159 "MPU fault. Thread=%s PC=0x%08" PRIx32
160 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
161 " ValidMmfar=%d MMFAR=0x%08" PRIx32
162 " Nested=%d Multiple=%d",
163 thread_name,
164 cpu_state.base.pc,
165 cpu_state.base.lr,
166 cpu_state.extended.cfsr,
167 is_mmfar_valid,
168 cpu_state.extended.mmfar,
169 is_nested_fault,
170 has_multiple_faults);
171 return;
172 }
173
174 // Bus fault.
175 if (cpu_state.extended.cfsr & kCfsrBusFaultMask) {
176 const bool is_bfar_valid = cpu_state.extended.cfsr & kCfsrBfarvalidMask;
177 #if PW_CPU_EXCEPTION_CORTEX_M_CRASH_EXTENDED_CPU_ANALYSIS
178 if (cpu_state.extended.cfsr & kCfsrIbuserrMask) {
179 // The processor does not write the fault address to the BFAR.
180 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
181 cpu_state,
182 "IBUSERR: Bus fault on instruction fetch. Thread=%s PC=0x%08" PRIx32
183 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
184 thread_name,
185 cpu_state.base.pc,
186 cpu_state.base.lr,
187 cpu_state.extended.cfsr,
188 is_nested_fault,
189 has_multiple_faults);
190 return;
191 }
192 if (cpu_state.extended.cfsr & kCfsrPreciserrMask) {
193 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
194 cpu_state,
195 "PRECISERR: Precise bus fault. Thread=%s PC=0x%08" PRIx32
196 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
197 " ValidBfar=%d BFAR=0x%08" PRIx32 " Nested=%d Multiple=%d",
198 thread_name,
199 cpu_state.base.pc,
200 cpu_state.base.lr,
201 cpu_state.extended.cfsr,
202 is_bfar_valid,
203 cpu_state.extended.bfar,
204 is_nested_fault,
205 has_multiple_faults);
206 return;
207 }
208 if (cpu_state.extended.cfsr & kCfsrImpreciserrMask) {
209 // The processor does not write the fault address to the BFAR.
210 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
211 cpu_state,
212 "IMPRECISERR: Imprecise bus fault. Thread=%s PC=0x%08" PRIx32
213 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
214 thread_name,
215 cpu_state.base.pc,
216 cpu_state.base.lr,
217 cpu_state.extended.cfsr,
218 is_nested_fault,
219 has_multiple_faults);
220 return;
221 }
222 if (cpu_state.extended.cfsr & kCfsrUnstkerrMask) {
223 // The processor does not write the fault address to the BFAR.
224 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
225 cpu_state,
226 "UNSTKERR: Derived bus fault on exception context save. Thread=%s "
227 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
228 " Nested=%d Multiple=%d",
229 thread_name,
230 cpu_state.base.pc,
231 cpu_state.base.lr,
232 cpu_state.extended.cfsr,
233 is_nested_fault,
234 has_multiple_faults);
235 return;
236 }
237 if (cpu_state.extended.cfsr & kCfsrStkerrMask) {
238 // The processor does not write the fault address to the BFAR.
239 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
240 cpu_state,
241 "STKERR: Derived bus fault on exception context restore. Thread=%s "
242 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
243 " Nested=%d Multiple=%d",
244 thread_name,
245 cpu_state.base.pc,
246 cpu_state.base.lr,
247 cpu_state.extended.cfsr,
248 is_nested_fault,
249 has_multiple_faults);
250 return;
251 }
252 if (cpu_state.extended.cfsr & kCfsrLsperrMask) {
253 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
254 cpu_state,
255 "LSPERR: Derived bus fault on lazy FPU state preservation. Thread=%s "
256 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
257 " ValidBfar=%d BFAR=0x%08" PRIx32 " Nested=%d Multiple=%d",
258 thread_name,
259 cpu_state.base.pc,
260 cpu_state.base.lr,
261 cpu_state.extended.cfsr,
262 is_bfar_valid,
263 cpu_state.extended.bfar,
264 is_nested_fault,
265 has_multiple_faults);
266 return;
267 }
268 #endif // PW_CPU_EXCEPTION_CORTEX_M_CRASH_EXTENDED_CPU_ANALYSIS
269
270 PW_CPU_EXCEPTION_CORTEX_M_CRASH(cpu_state,
271 "Bus Fault. Thread=%s PC=0x%08" PRIx32
272 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
273 " ValidBfar=%d BFAR=0x%08" PRIx32
274 " Nested=%d Multiple=%d",
275 thread_name,
276 cpu_state.base.pc,
277 cpu_state.base.lr,
278 cpu_state.extended.cfsr,
279 is_bfar_valid,
280 cpu_state.extended.bfar,
281 is_nested_fault,
282 has_multiple_faults);
283 return;
284 }
285
286 // Usage fault.
287 if (cpu_state.extended.cfsr & kCfsrUsageFaultMask) {
288 #if PW_CPU_EXCEPTION_CORTEX_M_CRASH_EXTENDED_CPU_ANALYSIS
289 if (cpu_state.extended.cfsr & kCfsrUndefinstrMask) {
290 PW_CPU_EXCEPTION_CORTEX_M_CRASH(cpu_state,
291 "UNDEFINSTR: Encountered invalid "
292 "instruction. Thread=%s PC=0x%08" PRIx32
293 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
294 " Nested=%d Multiple=%d",
295 thread_name,
296 cpu_state.base.pc,
297 cpu_state.base.lr,
298 cpu_state.extended.cfsr,
299 is_nested_fault,
300 has_multiple_faults);
301 return;
302 }
303 if (cpu_state.extended.cfsr & kCfsrInvstateMask) {
304 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
305 cpu_state,
306 "INVSTATE: Attempted instruction with invalid EPSR value. Thread=%s "
307 "PC=0x%08" PRIx32 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
308 " Nested=%d Multiple=%d",
309 thread_name,
310 cpu_state.base.pc,
311 cpu_state.base.lr,
312 cpu_state.extended.cfsr,
313 is_nested_fault,
314 has_multiple_faults);
315 return;
316 }
317 if (cpu_state.extended.cfsr & kCfsrInvpcMask) {
318 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
319 cpu_state,
320 "INVPC: Invalid program counter. Thread=%s PC=0x%08" PRIx32
321 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
322 thread_name,
323 cpu_state.base.pc,
324 cpu_state.base.lr,
325 cpu_state.extended.cfsr,
326 is_nested_fault,
327 has_multiple_faults);
328 return;
329 }
330 if (cpu_state.extended.cfsr & kCfsrNocpMask) {
331 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
332 cpu_state,
333 "NOCP: Coprocessor disabled or not present. Thread=%s PC=0x%08" PRIx32
334 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
335 thread_name,
336 cpu_state.base.pc,
337 cpu_state.base.lr,
338 cpu_state.extended.cfsr,
339 is_nested_fault,
340 has_multiple_faults);
341 return;
342 }
343 if (cpu_state.extended.cfsr & kCfsrUnalignedMask) {
344 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
345 cpu_state,
346 "UNALIGNED: Unaligned memory access. Thread=%s PC=0x%08" PRIx32
347 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
348 thread_name,
349 cpu_state.base.pc,
350 cpu_state.base.lr,
351 cpu_state.extended.cfsr,
352 is_nested_fault,
353 has_multiple_faults);
354 return;
355 }
356 if (cpu_state.extended.cfsr & kCfsrDivbyzeroMask) {
357 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
358 cpu_state,
359 "DIVBYZERO: Division by zero. Thread=%s PC=0x%08" PRIx32
360 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
361 thread_name,
362 cpu_state.base.pc,
363 cpu_state.base.lr,
364 cpu_state.extended.cfsr,
365 is_nested_fault,
366 has_multiple_faults);
367 return;
368 }
369 #endif // PW_CPU_EXCEPTION_CORTEX_M_CRASH_EXTENDED_CPU_ANALYSIS
370 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
371 cpu_state,
372 "Fault=Usage Nested=%d Multiple=%d Thread=%s PC=0x%08" PRIx32
373 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32,
374 is_nested_fault,
375 has_multiple_faults,
376 thread_name,
377 cpu_state.base.pc,
378 cpu_state.base.lr,
379 cpu_state.extended.cfsr);
380 return;
381 }
382 if ((cpu_state.extended.icsr & kIcsrVectactiveMask) == kNmiIsrNum) {
383 PW_CPU_EXCEPTION_CORTEX_M_CRASH(
384 cpu_state,
385 "Non-Maskable Interrupt triggered. Thread=%s PC=0x%08" PRIx32
386 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32 " Nested=%d Multiple=%d",
387 thread_name,
388 cpu_state.base.pc,
389 cpu_state.base.lr,
390 cpu_state.extended.cfsr,
391 is_nested_fault,
392 has_multiple_faults);
393 return;
394 }
395
396 PW_CPU_EXCEPTION_CORTEX_M_CRASH(cpu_state,
397 "Unknown fault. Thread=%s PC=0x%08" PRIx32
398 " LR=0x%08" PRIx32 " CFSR=0x%08" PRIx32
399 " Nested=%d Multiple=%d",
400 thread_name,
401 cpu_state.base.pc,
402 cpu_state.base.lr,
403 cpu_state.extended.cfsr,
404 is_nested_fault,
405 has_multiple_faults);
406 return;
407 }
408
409 } // namespace pw::cpu_exception::cortex_m
410