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