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