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 * Zephyr Kernel port
9 */
10
11 #include <zephyr/init.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/reboot.h>
14 #include <dfm.h>
15
16 #if DFM_CFG_ENABLE_COREDUMPS == 1
17 #include <zephyr/debug/coredump.h>
18 #endif
19
20 /* Scratch related includes */
21 #include <zephyr/storage/flash_map.h>
22 #include <dfmConfig.h>
23
24 #if ((DFM_CFG_ENABLED) == 1)
25
26 #if (defined(CONFIG_PERCEPIO_DFM_CFG_COREDUMP_RETAIN) && CONFIG_PERCEPIO_DFM_CFG_COREDUMP_RETAIN == 1) && !(defined(CONFIG_PERCEPIO_DFM_CFG_RETAINED_MEMORY) && CONFIG_PERCEPIO_DFM_CFG_RETAINED_MEMORY == 1)
27 #error "DFM is configured to store Core Dumps in Retained Memory but that isn't enabled in DFM."
28 #endif
29
30 #define MAX_COREDUMP_PARTS 4
31
32 DfmKernelPortData_t* pxKernelPortData;
33
xDfmKernelPortInitialize(DfmKernelPortData_t * pxBuffer)34 DfmResult_t xDfmKernelPortInitialize(DfmKernelPortData_t *pxBuffer)
35 {
36 if (pxBuffer == (void*)0)
37 {
38 return DFM_FAIL;
39 }
40
41 pxKernelPortData = pxBuffer;
42
43 return DFM_SUCCESS;
44 }
45
46 //TODO: since we're just referring to a variable, we should possibily point to a const char
xDfmKernelPortGetCurrentTaskName(char ** pszTaskName)47 DfmResult_t xDfmKernelPortGetCurrentTaskName(char** pszTaskName)
48 {
49 //TODO: Possbily add additional checking here
50 k_tid_t current_thread = k_current_get();
51 *pszTaskName = (char*)k_thread_name_get(current_thread);
52 return DFM_SUCCESS;
53 }
54
55 /**
56 * @brief Initialize aspects of the devalert module that must
57 * preceed the kernel initialization (scheduling, threads, etc.).
58 *
59 * @param[in] arg
60 */
devalert_pre_kernel_init(void)61 static int devalert_pre_kernel_init(void)
62 {
63 return 0;
64 }
65
66 /**
67 * @brief Initialize aspects of the devalert module that depends on
68 * the kernel being initialized.
69 *
70 * @param[in] arg
71 */
devalert_post_kernel_init(void)72 static int devalert_post_kernel_init(void)
73 {
74 return 0;
75 }
76
77 /* Specify recorder module initialization stages */
78 SYS_INIT(devalert_pre_kernel_init, PRE_KERNEL_2, 0);
79 SYS_INIT(devalert_post_kernel_init, POST_KERNEL, 99);
80
81 /*
82 * Functions for the coredump backend api, which in turn will use the dfmBackend for storing data
83 * TODO: We probably don't want this in the kernel port but rather in its own file, like with crashcatcher.
84 */
85
86 enum eDfmCoredumpState {
87 DFM_COREDUMP_STATE_STARTED,
88 DFM_COREDUMP_STATE_HEADER,
89 DFM_COREDUMP_STATE_DATA
90 };
91
92 typedef struct DfmCoredumpPayload {
93 uint8_t pubHeaderBuffer[32]; /* The header structs are packed, this is way more than the size they'll ever reach */
94 size_t ulHeaderSize;
95 void* pxContent;
96 size_t ulContentSize;
97 } DfmCoredumpPayload_t;
98
99 #if DFM_CFG_ENABLE_COREDUMPS == 1
100
101 static enum eDfmCoredumpState eCoreDumpState = DFM_COREDUMP_STATE_STARTED;
102 static DfmCoredumpPayload_t pxDfmCoredumpParts[MAX_COREDUMP_PARTS]; // The zephyr coredump process won't add more than 3 headers
103 static uint8_t ubDfmCoreDumpHeaderCounter = 0;
104 static uint8_t ubDfmPayloadBuffer[CONFIG_PERCEPIO_DFM_CFG_MAX_COREDUMP_SIZE];
105 static uint32_t ulDfmPayloadBufferBytesUsed = 0;
106
xDfmAlertAddCoredump(DfmAlertHandle_t xAlertHandle)107 DfmResult_t xDfmAlertAddCoredump(DfmAlertHandle_t xAlertHandle)
108 {
109 if (ubDfmCoreDumpHeaderCounter < 1)
110 return DFM_FAIL;
111
112 ulDfmPayloadBufferBytesUsed = 0;
113 memset(ubDfmPayloadBuffer, 0, sizeof(ubDfmPayloadBuffer));
114 uint8_t* pubDfmPayloadBufferPosition = ubDfmPayloadBuffer;
115
116 for (int i=0; i<ubDfmCoreDumpHeaderCounter; i++)
117 {
118 DfmCoredumpPayload_t* pxCurrentPayload = &pxDfmCoredumpParts[i];
119
120 size_t ulContentSize = pxCurrentPayload->ulContentSize;
121 if (ulContentSize > CONFIG_PERCEPIO_DFM_CFG_STACKDUMP_SIZE)
122 ulContentSize = CONFIG_PERCEPIO_DFM_CFG_STACKDUMP_SIZE;
123
124 /* Initial size check to avoid overflowing the buffer, i.e. does this part of the chunk fit within the chunk */
125 if (ulDfmPayloadBufferBytesUsed + pxCurrentPayload->ulHeaderSize + ulContentSize > CONFIG_PERCEPIO_DFM_CFG_MAX_COREDUMP_SIZE)
126 return DFM_FAIL;
127
128 /* Copy the header */
129 memcpy(pubDfmPayloadBufferPosition, pxCurrentPayload->pubHeaderBuffer, pxCurrentPayload->ulHeaderSize);
130 pubDfmPayloadBufferPosition += pxCurrentPayload->ulHeaderSize;
131 ulDfmPayloadBufferBytesUsed += pxCurrentPayload->ulHeaderSize;
132
133 if (pxCurrentPayload->pxContent != NULL)
134 {
135 /* Copy the content */
136 memcpy(pubDfmPayloadBufferPosition, pxCurrentPayload->pxContent, ulContentSize);
137 pubDfmPayloadBufferPosition += ulContentSize;
138 ulDfmPayloadBufferBytesUsed += ulContentSize;
139 }
140 }
141
142 DfmResult_t xResult;
143 xResult = xDfmAlertAddPayload(
144 xAlertHandle,
145 ubDfmPayloadBuffer,
146 ulDfmPayloadBufferBytesUsed,
147 "Coredump.dmp"
148 );
149
150 return xResult;
151 }
152
153 /**
154 * Start a new coredump, will wipe the memory area and reset the internal counters used by this kernel port when a coredump
155 * is saved.
156 * This function is called from the Zephyr kernel.
157 */
xDfmCoredumpBackendStart(void)158 static void xDfmCoredumpBackendStart(void)
159 {
160 eCoreDumpState = DFM_COREDUMP_STATE_STARTED;
161 ubDfmCoreDumpHeaderCounter = 0;
162 memset(pxDfmCoredumpParts, 0, sizeof(pxDfmCoredumpParts));
163 }
164
165 /**
166 * Internally store a part of a coredump within the internal array of DfmCoreDumpPayloads.
167 * This function is called from the Zephyr kernel.
168 * @param pxBuffer
169 * @param ulBufferLength
170 */
xDfmCoredumpBackendBufferOutput(uint8_t * pxBuffer,size_t ulBufferLength)171 static void xDfmCoredumpBackendBufferOutput(uint8_t* pxBuffer, size_t ulBufferLength)
172 {
173 /*
174 * To avoid nasty buffer overflows, stop dumping any more data in case the maximum
175 * amount of payloads has been reached. Since the signature of the function expected by Zephyr
176 * for outputting backend data is a void, error handling can unfortunately not be implemented here.
177 */
178 if (ubDfmCoreDumpHeaderCounter >= MAX_COREDUMP_PARTS)
179 return;
180
181 DfmCoredumpPayload_t* pxCurrentPayload = &pxDfmCoredumpParts[ubDfmCoreDumpHeaderCounter];
182
183 switch (eCoreDumpState)
184 {
185
186 case DFM_COREDUMP_STATE_STARTED:
187 {
188 /* TODO: Might be nice with some assertion here or similar */
189 if (ulBufferLength > 32)
190 return;
191
192 pxCurrentPayload->ulHeaderSize = ulBufferLength;
193 memcpy(pxCurrentPayload->pubHeaderBuffer, pxBuffer, ulBufferLength);
194 ubDfmCoreDumpHeaderCounter++;
195 eCoreDumpState = DFM_COREDUMP_STATE_HEADER;
196 break;
197 }
198
199 case DFM_COREDUMP_STATE_HEADER:
200 {
201 if (ulBufferLength > 32)
202 return;
203
204 pxCurrentPayload->ulHeaderSize = ulBufferLength;
205 memcpy(pxCurrentPayload->pubHeaderBuffer, pxBuffer, ulBufferLength);
206 eCoreDumpState = DFM_COREDUMP_STATE_DATA;
207 break;
208 }
209
210 case DFM_COREDUMP_STATE_DATA: {
211 pxCurrentPayload->ulContentSize = ulBufferLength;
212 pxCurrentPayload->pxContent = pxBuffer;
213 ubDfmCoreDumpHeaderCounter++;
214 eCoreDumpState = DFM_COREDUMP_STATE_HEADER;
215 break;
216 }
217
218 }
219
220 }
221
222 /**
223 * Finalize a coredump, which will result in, as long as the coredump reason is > 0xFFFF0000, the coredump parts recorded
224 * by the xDfmCoredumpBackendBufferOutput are dumped to the disk and an alert is generated.
225 * If the reason is > 0xFFFF0000, this is considered a user generated coredump, and thus, the user will need to call xDfmAlertAddCoredump
226 * to append the coredump to the appropriate alert.
227 * This function is called from the the Zephyr kernel.
228 */
xDfmCoredumpBackendEnd(void)229 static void xDfmCoredumpBackendEnd(void)
230 {
231 /* Examine the header to see whether this was a coredump created by the user or triggered from Zephyr */
232 struct coredump_hdr_t* pxCoredumpHeader = (struct coredump_hdr_t*)(&pxDfmCoredumpParts[0]);
233
234 /*
235 * There can exist architecture specific errors, so using a really high value here makes it possible for us to
236 * determine whether this coredump was triggered by a user or not.
237 * Since this is user triggered, the user will want to append these coredumps rather than having an alert created.
238 */
239 if (pxCoredumpHeader->reason > 0xFFFF0000)
240 {
241 return;
242 }
243
244 DfmAlertHandle_t xAlertHandle;
245 if (xDfmAlertBegin(DFM_TYPE_HARDFAULT, "HardfaultCoreD", &xAlertHandle) == DFM_SUCCESS)
246 {
247 /* TODO: Look into how to add various symptoms caught by the coredump here */
248 xDfmAlertAddCoredump(xAlertHandle);
249
250 /* Add the reason as a symptom */
251 xDfmAlertAddSymptom(xAlertHandle, DFM_SYMPTOM_HARDFAULT_REASON, pxCoredumpHeader->reason);
252
253 #if defined(CONFIG_PERCEPIO_DFM_CFG_ADD_TRACE) && CONFIG_PERCEPIO_DFM_CFG_ADD_TRACE == 1
254 xDfmAlertAddTrace(xAlertHandle);
255 #endif
256
257 #if defined(CONFIG_PERCEPIO_DFM_CFG_COREDUMP_RETAIN) && CONFIG_PERCEPIO_DFM_CFG_COREDUMP_RETAIN == 1
258 xDfmAlertEndCustom(xAlertHandle, DFM_ALERT_END_TYPE_RETAIN);
259 #elif defined(CONFIG_PERCEPIO_DFM_CFG_COREDUMP_STORE) && CONFIG_PERCEPIO_DFM_CFG_COREDUMP_STORE == 1
260 xDfmAlertEndCustom(xAlertHandle, DFM_ALERT_END_TYPE_STORE);
261 #endif
262 }
263
264 sys_reboot(SYS_REBOOT_COLD);
265 }
266
xDfmCoredumpBackendCmd(enum coredump_cmd_id eCmdId,void * arg)267 static int xDfmCoredumpBackendCmd(enum coredump_cmd_id eCmdId, void *arg)
268 {
269 return 0;
270 }
271
xDfmCoredumpBackendQuery(enum coredump_query_id eQueryId,void * arg)272 static int xDfmCoredumpBackendQuery(enum coredump_query_id eQueryId, void *arg)
273 {
274 /* TODO: Might want to implement a few commands here, such as the size of the coredump */
275 return 0;
276 }
277
278 struct coredump_backend_api coredump_backend_other = {
279 .start = xDfmCoredumpBackendStart,
280 .end = xDfmCoredumpBackendEnd,
281 .buffer_output = xDfmCoredumpBackendBufferOutput,
282 .cmd = xDfmCoredumpBackendCmd,
283 .query = xDfmCoredumpBackendQuery
284 };
285
286 #endif
287
288 #if defined(CONFIG_PERCEPIO_TRACERECORDER) && CONFIG_PERCEPIO_TRACERECORDER == 1 && defined(CONFIG_PERCEPIO_TRC_CFG_STREAM_PORT_RINGBUFFER)
xDfmAlertAddTrace(DfmAlertHandle_t xAlertHandle)289 DfmResult_t xDfmAlertAddTrace(DfmAlertHandle_t xAlertHandle)
290 {
291 void* pvBuffer = (void*)0;
292 uint32_t ulBufferSize = 0;
293
294 if(xTraceIsRecorderEnabled() == 1)
295 {
296 xTraceDisable();
297 }
298 else
299 {
300 return DFM_FAIL;
301 }
302
303 if (xTraceGetEventBuffer(&pvBuffer, &ulBufferSize) != DFM_SUCCESS)
304 {
305 return DFM_FAIL;
306 }
307
308 if (xDfmAlertAddPayload(xAlertHandle, pvBuffer, ulBufferSize, "dfm_trace.psfs") != DFM_SUCCESS)
309 {
310 return DFM_FAIL;
311 }
312
313 return DFM_SUCCESS;
314 }
315 #endif
316
317 #endif
318