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