1 /*
2 * Percepio DFM v2.1.0
3 * Copyright 2023 Percepio AB
4 * www.percepio.com
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 *
8 * DFM Crash Catcher integration
9 */
10
11 #include <CrashCatcher.h>
12 #include <string.h>
13 #include <dfm.h>
14 #include <dfmCrashCatcher.h>
15 #include <CrashCatcherPriv.h>
16 #include <dfmKernelPort.h>
17
18 #if ((DFM_CFG_ENABLED) >= 1)
19
20 #if ((DFM_CFG_CRASH_ADD_TRACE) >= 1)
21 #include <trcRecorder.h>
22 static void prvAddTracePayload(void);
23 #endif
24
25 /* See https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/system-control-block/configurable-fault-status-register*/
26 #define ARM_CORTEX_M_CFSR_REGISTER *(uint32_t*)0xE000ED28
27
28 static DfmAlertHandle_t xAlertHandle = 0;
29
30 dfmTrapInfo_t dfmTrapInfo = {-1, NULL, NULL, -1};
31
32 #if ((DFM_CFG_CRASH_ADD_TRACE) >= 1)
33 static TraceStringHandle_t TzUserEventChannel = NULL;
34 #endif
35
36 // TODO: Better to use a random number here, so it is harder to spoof?
37 void *__stack_chk_guard = (void *)0xdeadbeef;
38
39 static uint8_t* ucBufferPos;
40 static uint8_t ucDataBuffer[CRASH_DUMP_BUFFER_SIZE] __attribute__ ((aligned (8)));
41
42 static void dumpHalfWords(const uint16_t* pMemory, size_t elementCount);
43 static void dumpWords(const uint32_t* pMemory, size_t elementCount);
44
45 uint32_t stackPointer = 0;
46
47 /* Used for snprintf calls */
48 char cDfmPrintBuffer[128];
49
prvGetFileNameFromPath(char * szPath)50 static char* prvGetFileNameFromPath(char* szPath)
51 {
52 return strrchr(szPath, '/')+1; /* +1 to skip the last '/' character */
53 }
54
prvCalculateChecksum(char * ptr,size_t maxlen)55 static uint32_t prvCalculateChecksum(char *ptr, size_t maxlen)
56 {
57 uint32_t chksum = 0;
58 size_t i = 0;
59
60 if (ptr == NULL)
61 {
62 return 0;
63 }
64
65 while ((ptr[i] != (char)0) && (i < maxlen))
66 {
67 chksum += (uint32_t)ptr[i];
68 i++;
69 }
70
71 return chksum;
72 }
73
CrashCatcher_GetMemoryRegions(void)74 const CrashCatcherMemoryRegion* CrashCatcher_GetMemoryRegions(void)
75 {
76 static CrashCatcherMemoryRegion regions[] = {
77 {0xFFFFFFFF, 0xFFFFFFFF, CRASH_CATCHER_BYTE},
78 {CRASH_MEM_REGION1_START, CRASH_MEM_REGION1_START + CRASH_MEM_REGION1_SIZE, CRASH_CATCHER_BYTE},
79 {CRASH_MEM_REGION2_START, CRASH_MEM_REGION2_START + CRASH_MEM_REGION2_SIZE, CRASH_CATCHER_BYTE},
80 {CRASH_MEM_REGION3_START, CRASH_MEM_REGION3_START + CRASH_MEM_REGION3_SIZE, CRASH_CATCHER_BYTE}
81 };
82
83 /* Region 0 is reserved, always relative to the current stack pointer */
84 regions[0].startAddress = stackPointer;
85 regions[0].endAddress = stackPointer + CRASH_STACK_CAPTURE_SIZE;
86
87 return regions;
88 }
89
CrashCatcher_DumpStart(const CrashCatcherInfo * pInfo)90 void CrashCatcher_DumpStart(const CrashCatcherInfo* pInfo)
91 {
92 int alerttype;
93 char* szFileName = (void*)0;
94 char* szCurrentTaskName = (void*)0;
95
96 stackPointer = pInfo->sp;
97
98 #if ((DFM_CFG_CRASH_ADD_TRACE) >= 1)
99 if (TzUserEventChannel == 0)
100 {
101 xTraceStringRegister("ALERT", &TzUserEventChannel);
102 }
103 #endif
104
105 ucBufferPos = &ucDataBuffer[0];
106
107 DFM_DEBUG_PRINT("\nDFM Alert\n");
108
109 if (dfmTrapInfo.alertType >= 0)
110 {
111 /* On the DFM_TRAP macro.
112 * This sets dfmTrapInfo and then generates an NMI exception to trigger this error handler.
113 * dfmTrapInfo.message = "Assert failed" or similar.
114 * dfmTrapInfo.file = __FILE__ (full path, extract the filename from this!)
115 * dfmTrapInfo.line = __LINE__ (integer)
116 * */
117 szFileName = prvGetFileNameFromPath(dfmTrapInfo.file);
118 snprintf(cDfmPrintBuffer, sizeof(cDfmPrintBuffer), "%s at %s:%u", dfmTrapInfo.message, szFileName, dfmTrapInfo.line);
119
120 DFM_DEBUG_PRINT(" DFM_TRAP(): ");
121 DFM_DEBUG_PRINT(cDfmPrintBuffer);
122 DFM_DEBUG_PRINT("\n");
123
124 alerttype = dfmTrapInfo.alertType;
125 }
126 else
127 {
128 DFM_DEBUG_PRINT(" DFM: Hard fault\n");
129
130 snprintf(cDfmPrintBuffer, sizeof(cDfmPrintBuffer), "Hard fault exception (CFSR reg: 0x%08X)", (unsigned int)ARM_CORTEX_M_CFSR_REGISTER);
131
132 alerttype = DFM_TYPE_HARDFAULT;
133 }
134
135 if (xDfmAlertBegin(alerttype, cDfmPrintBuffer, &xAlertHandle) == DFM_SUCCESS)
136 {
137 (void)xDfmKernelPortGetCurrentTaskName(&szCurrentTaskName);
138
139 xDfmAlertAddSymptom(xAlertHandle, DFM_SYMPTOM_CURRENT_TASK, prvCalculateChecksum(szCurrentTaskName, 32));
140 xDfmAlertAddSymptom(xAlertHandle, DFM_SYMPTOM_STACKPTR, pInfo->sp);
141
142 if (dfmTrapInfo.alertType >= 0)
143 {
144 /* On DFM_TRAP */
145 xDfmAlertAddSymptom(xAlertHandle, DFM_SYMPTOM_FILE, prvCalculateChecksum(szFileName, 32));
146 xDfmAlertAddSymptom(xAlertHandle, DFM_SYMPTOM_LINE, dfmTrapInfo.line);
147 }
148 else
149 {
150 /* On hard faults */
151 xDfmAlertAddSymptom(xAlertHandle, DFM_SYMPTOM_ARM_SCB_FCSR, ARM_CORTEX_M_CFSR_REGISTER);
152
153 /******************************************************************
154 * TODO: Add MMAR and BFAR regs here as symptoms to get the address
155 * of the problematic instruction. Would be a good symptom.
156 *****************************************************************/
157 }
158
159 #if ((DFM_CFG_CRASH_ADD_TRACE) >= 1)
160 xTracePrint(TzUserEventChannel, cDfmPrintBuffer);
161 prvAddTracePayload();
162 #endif
163 DFM_DEBUG_PRINT(" DFM: Storing the alert.\n");
164 }
165 else
166 {
167 DFM_DEBUG_PRINT(" DFM: Not yet initialized. Alert ignored.\n"); // Always log this!
168 }
169 DFM_DEBUG_PRINT("\n");
170
171 dfmTrapInfo.alertType = -1;
172 dfmTrapInfo.message = NULL;
173 }
174
175 #if ((DFM_CFG_CRASH_ADD_TRACE) >= 1)
prvAddTracePayload(void)176 static void prvAddTracePayload(void)
177 {
178 char* szName;
179 void* pvBuffer = (void*)0;
180 uint32_t ulBufferSize = 0;
181 if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_STREAMING)
182 {
183 szName = "dfm_trace.psfs";
184 }
185 else if (TRC_CFG_RECORDER_MODE == TRC_RECORDER_MODE_SNAPSHOT)
186 {
187 szName = "dfm_trace.trc";
188 }
189
190 if (xTraceIsRecorderEnabled() == 1)
191 {
192 xTraceDisable();
193 }
194 xTraceGetEventBuffer(&pvBuffer, &ulBufferSize);
195 xDfmAlertAddPayload(xAlertHandle, pvBuffer, ulBufferSize, szName);
196 }
197 #endif
198
CrashCatcher_DumpMemory(const void * pvMemory,CrashCatcherElementSizes elementSize,size_t elementCount)199 void CrashCatcher_DumpMemory(const void* pvMemory, CrashCatcherElementSizes elementSize, size_t elementCount)
200 {
201 int32_t current_usage = (uint32_t)ucBufferPos - (uint32_t)ucDataBuffer;
202
203 /* This function is called when CrashCatcher detects an internal stack overflow (it has a separate stack) */
204 if (g_crashCatcherStack[0] != CRASH_CATCHER_STACK_SENTINEL)
205 {
206 /* Always try to print this error. But it might actually not print since the memory has been corrupted. */
207 DFM_ERROR_PRINT("DFM: ERROR, stack overflow in CrashCatcher, see comment in dfmCrashCatcher.c\n");
208
209 /**********************************************************************************************************
210
211 If you get here, there has been a stack overflow on the CrashCatcher stack.
212 This is separate from the main stack and defined in CrashCatcher.c (g_crashCatcherStack).
213
214 This error might happen because of diagnostic prints and other function calls while saving the alert.
215 You may increase the stack size in CrashCatcherPriv.h or turn off the logging (DFM_CFG_USE_DEBUG_LOGGING).
216
217 ***********************************************************************************************************/
218
219 vDfmDisableInterrupts();
220 for (;;); // Stop here...
221 }
222
223 if (elementCount == 0)
224 {
225 /* May happen if CRASH_MEM_REGION<X>_SIZE is set to 0 by mistake (e.g. if using 0 instead of 0xFFFFFFFF for CRASH_MEM_REGION<X>_START on unused slots. */
226 DFM_ERROR_PRINT("DFM: Warning, memory region size is zero!\n");
227 return;
228 }
229
230 switch (elementSize)
231 {
232
233 case CRASH_CATCHER_BYTE:
234
235 if ( current_usage + elementCount >= CRASH_DUMP_BUFFER_SIZE)
236 {
237 DFM_ERROR_PRINT("\nDFM: Error, ucDataBuffer not large enough!\n\n");
238 return;
239 }
240
241 memcpy((void*)ucBufferPos, pvMemory, elementCount);
242 ucBufferPos += elementCount;
243 break;
244
245 case CRASH_CATCHER_HALFWORD:
246
247 if ( current_usage + elementCount*2 >= CRASH_DUMP_BUFFER_SIZE)
248 {
249 DFM_ERROR_PRINT("\nDFM: Error, ucDataBuffer not large enough!\n\n");
250 return;
251 }
252 dumpHalfWords(pvMemory, elementCount);
253
254 break;
255
256 case CRASH_CATCHER_WORD:
257
258 if ( current_usage + elementCount*4 >= CRASH_DUMP_BUFFER_SIZE)
259 {
260 DFM_ERROR_PRINT("\nDFM: Error, ucDataBuffer not large enough!\n\n");
261 return;
262 }
263
264 dumpWords(pvMemory, elementCount);
265
266 break;
267
268 default:
269 DFM_ERROR_PRINT("\nDFM: Error, unhandled case!\n\n");
270 break;
271 }
272 }
273
dumpHalfWords(const uint16_t * pMemory,size_t elementCount)274 static void dumpHalfWords(const uint16_t* pMemory, size_t elementCount)
275 {
276 size_t i;
277 for (i = 0 ; i < elementCount ; i++)
278 {
279 uint16_t val = *pMemory++;
280 memcpy((void*)ucBufferPos, &val, sizeof(val));
281 ucBufferPos += sizeof(val);
282 }
283 }
284
dumpWords(const uint32_t * pMemory,size_t elementCount)285 static void dumpWords(const uint32_t* pMemory, size_t elementCount)
286 {
287 size_t i;
288 for (i = 0 ; i < elementCount ; i++)
289 {
290 uint32_t val = *pMemory++;
291 memcpy((void*)ucBufferPos, &val, sizeof(val));
292 ucBufferPos += sizeof(val);
293 }
294 }
295
CrashCatcher_DumpEnd(void)296 CrashCatcherReturnCodes CrashCatcher_DumpEnd(void)
297 {
298 if (xAlertHandle != 0)
299 {
300 uint32_t size = (uint32_t)ucBufferPos - (uint32_t)ucDataBuffer;
301 if (xDfmAlertAddPayload(xAlertHandle, ucDataBuffer, size, CRASH_DUMP_NAME) == DFM_SUCCESS)
302 {
303 DFM_DEBUG_PRINT(" DFM: Crash dump stored.\n");
304 }
305 else
306 {
307 DFM_ERROR_PRINT(" DFM: Failed storing crash dump.\n");
308 }
309
310 #ifdef DFM_CLOUD_PORT_ALWAYS_ATTEMPT_TRANSFER
311 /* The cloud port has indicated it is always OK to attempt to transfer */
312 if (xDfmAlertEnd(xAlertHandle) == DFM_SUCCESS)
313 #else
314 /* Cloud port transfer cannot be trusted, so we only attempt to store it */
315 if (xDfmAlertEndOffline(xAlertHandle) == DFM_SUCCESS)
316 #endif
317 {
318 DFM_DEBUG_PRINT(" DFM: Alert stored OK.\n");
319 }
320 else
321 {
322 DFM_ERROR_PRINT(" DFM: Failed storing alert.\n");
323 }
324
325 }
326
327 CRASH_FINALIZE();
328 return CRASH_CATCHER_EXIT;
329 }
330
331
332 /* Called by gcc stack-checking code when using the gcc option -fstack-protector-strong */
__stack_chk_fail(void)333 void __stack_chk_fail(void)
334 {
335 // If this happens, the stack has been corrupted by the previous function in the call stack.
336 // Note that the exact location of the stack corruption is not known, since detected when exiting the function.
337 DFM_TRAP(DFM_TYPE_STACK_CHK_FAILED, "Stack corruption detected");
338 }
339
340 #endif
341