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