• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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