• 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 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