1 /*
2  * Copyright (c) 2020 Intel Corporation.
3  * Copyright (c) 2025 Bang & Olufsen A/S, Denmark
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <errno.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/shell/shell.h>
11 
12 #include <zephyr/debug/coredump.h>
13 
14 #if defined(CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING)
15 #define COREDUMP_BACKEND_STR "logging"
16 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_FLASH_PARTITION)
17 #define COREDUMP_BACKEND_STR "flash partition"
18 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_INTEL_ADSP_MEM_WINDOW)
19 #define COREDUMP_BACKEND_STR "ADSP memory window"
20 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_IN_MEMORY)
21 #define COREDUMP_BACKEND_STR "In memory - volatile -"
22 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_OTHER)
23 #define COREDUMP_BACKEND_STR "other"
24 #else
25 #error "Need to select a coredump backend"
26 #endif
27 
28 /* Length of buffer of printable size */
29 #define PRINT_BUF_SZ		64
30 /* Length of copy buffer */
31 #define COPY_BUF_SZ		128
32 
33 /* Length of buffer of printable size plus null character */
34 #define PRINT_BUF_SZ_RAW	(PRINT_BUF_SZ + 1)
35 
36 /* Print buffer */
37 static char print_buf[PRINT_BUF_SZ_RAW];
38 static off_t print_buf_ptr;
39 
40 /**
41  * Since enum coredump_tgt_code is sequential and starting from 0,
42  * let's just have an array
43  */
44 static const char * const coredump_target_code2str[] = {
45 	"Unknown",
46 	"x86",
47 	"x86_64",
48 	"ARM Cortex-m",
49 	"Risc V",
50 	"Xtensa",
51 	"ARM64"
52 };
53 
54 /**
55  * @brief Shell command to get backend error.
56  *
57  * @param sh shell instance
58  * @param argc (not used)
59  * @param argv (not used)
60  * @return 0
61  */
cmd_coredump_error_get(const struct shell * sh,size_t argc,char ** argv)62 static int cmd_coredump_error_get(const struct shell *sh,
63 				  size_t argc, char **argv)
64 {
65 	int ret;
66 
67 	ARG_UNUSED(argc);
68 	ARG_UNUSED(argv);
69 
70 	ret = coredump_query(COREDUMP_QUERY_GET_ERROR, NULL);
71 	if (ret == 0) {
72 		shell_print(sh, "No error.");
73 	} else if (ret == -ENOTSUP) {
74 		shell_print(sh, "Unsupported query from the backend");
75 	} else {
76 		shell_print(sh, "Error: %d", ret);
77 	}
78 
79 	return 0;
80 }
81 
82 /**
83  * @brief Shell command to clear backend error.
84  *
85  * @param sh shell instance
86  * @param argc (not used)
87  * @param argv (not used)
88  * @return 0
89  */
cmd_coredump_error_clear(const struct shell * sh,size_t argc,char ** argv)90 static int cmd_coredump_error_clear(const struct shell *sh,
91 				    size_t argc, char **argv)
92 {
93 	int ret;
94 
95 	ARG_UNUSED(argc);
96 	ARG_UNUSED(argv);
97 
98 	ret = coredump_cmd(COREDUMP_CMD_CLEAR_ERROR, NULL);
99 	if (ret == 0) {
100 		shell_print(sh, "Error cleared.");
101 	} else if (ret == -ENOTSUP) {
102 		shell_print(sh, "Unsupported command from the backend");
103 	} else {
104 		shell_print(sh, "Failed to clear the error: %d", ret);
105 	}
106 
107 	return 0;
108 }
109 
110 /**
111  * @brief Shell command to see if there is a stored coredump in flash.
112  *
113  * @param sh shell instance
114  * @param argc (not used)
115  * @param argv (not used)
116  * @return 0
117  */
cmd_coredump_has_stored_dump(const struct shell * sh,size_t argc,char ** argv)118 static int cmd_coredump_has_stored_dump(const struct shell *sh,
119 					size_t argc, char **argv)
120 {
121 	int ret;
122 
123 	ARG_UNUSED(argc);
124 	ARG_UNUSED(argv);
125 
126 	ret = coredump_query(COREDUMP_QUERY_HAS_STORED_DUMP, NULL);
127 	if (ret == 1) {
128 		shell_print(sh, "Stored coredump found.");
129 	} else if (ret == 0) {
130 		shell_print(sh, "Stored coredump NOT found.");
131 	} else if (ret == -ENOTSUP) {
132 		shell_print(sh, "Unsupported query from the backend");
133 	} else {
134 		shell_print(sh, "Failed to perform query: %d", ret);
135 	}
136 
137 	return 0;
138 }
139 
140 /**
141  * @brief Shell command to verify if the stored coredump is valid.
142  *
143  * @param sh shell instance
144  * @param argc (not used)
145  * @param argv (not used)
146  * @return 0
147  */
cmd_coredump_verify_stored_dump(const struct shell * sh,size_t argc,char ** argv)148 static int cmd_coredump_verify_stored_dump(const struct shell *sh,
149 					   size_t argc, char **argv)
150 {
151 	int ret;
152 
153 	ARG_UNUSED(argc);
154 	ARG_UNUSED(argv);
155 
156 	ret = coredump_cmd(COREDUMP_CMD_VERIFY_STORED_DUMP, NULL);
157 	if (ret == 1) {
158 		shell_print(sh, "Stored coredump verified.");
159 	} else if (ret == 0) {
160 		shell_print(sh, "Stored coredump verification failed "
161 			    "or there is no stored coredump.");
162 	} else if (ret == -ENOTSUP) {
163 		shell_print(sh, "Unsupported command from the backend");
164 	} else {
165 		shell_print(sh, "Failed to perform verify command: %d", ret);
166 	}
167 
168 	return 0;
169 }
170 
171 /**
172  * @brief Flush the print buffer to shell.
173  *
174  * This prints what is in the print buffer to the shell.
175  *
176  * @param sh shell instance.
177  */
flush_print_buf(const struct shell * sh)178 static void flush_print_buf(const struct shell *sh)
179 {
180 	shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, print_buf);
181 	print_buf_ptr = 0;
182 	(void)memset(print_buf, 0, sizeof(print_buf));
183 }
184 
185 /**
186  * @brief Helper to print Zephyr coredump header to shell
187  *
188  * @param sh shell instance
189  * @param buf pointer to header
190  * @return size of data processed, -EINVAL if error converting data
191  */
print_coredump_hdr(const struct shell * sh,uint8_t * buf)192 static int print_coredump_hdr(const struct shell *sh, uint8_t *buf)
193 {
194 	struct coredump_hdr_t *hdr = (struct coredump_hdr_t *)buf;
195 
196 	if (memcmp(hdr->id, "ZE", sizeof(char)*2) != 0) {
197 		shell_print(sh, "Not a Zephyr coredump header");
198 		return -EINVAL;
199 	}
200 
201 	shell_print(sh, "**** Zephyr Coredump ****");
202 	shell_print(sh, "\tVersion %u", hdr->hdr_version);
203 	shell_print(sh, "\tTarget: %s",
204 		    coredump_target_code2str[sys_le16_to_cpu(
205 				    hdr->tgt_code)]);
206 	shell_print(sh, "\tPointer size: %u",
207 		    (uint16_t)BIT(hdr->ptr_size_bits));
208 	shell_print(sh, "\tFlag: %u", hdr->flag);
209 	shell_print(sh, "\tReason: %u", sys_le16_to_cpu(hdr->reason));
210 
211 	return sizeof(struct coredump_hdr_t);
212 }
213 
214 /**
215  * @brief Helper to print stored coredump data to shell
216  *
217  * This converts the binary data in @p buf to hexadecimal digits
218  * which can be printed to the shell.
219  *
220  * @param sh shell instance
221  * @param buf binary data buffer
222  * @param len number of bytes in buffer to be printed
223  * @return 0 if no issues; -EINVAL if error converting data
224  */
print_stored_dump(const struct shell * sh,uint8_t * buf,size_t len)225 static int print_stored_dump(const struct shell *sh, uint8_t *buf, size_t len)
226 {
227 	int ret = 0;
228 	size_t i = 0;
229 	size_t remaining = len;
230 
231 	if (len == 0) {
232 		/* Flush print buffer */
233 		flush_print_buf(sh);
234 		goto out;
235 	}
236 
237 	while (remaining > 0) {
238 		if (hex2char(buf[i] >> 4, &print_buf[print_buf_ptr]) < 0) {
239 			ret = -EINVAL;
240 			break;
241 		}
242 		print_buf_ptr++;
243 
244 		if (hex2char(buf[i] & 0xf, &print_buf[print_buf_ptr]) < 0) {
245 			ret = -EINVAL;
246 			break;
247 		}
248 		print_buf_ptr++;
249 
250 		remaining--;
251 		i++;
252 
253 		if (print_buf_ptr == PRINT_BUF_SZ) {
254 			flush_print_buf(sh);
255 		}
256 	}
257 
258 out:
259 	return ret;
260 }
261 
262 /**
263  * @brief Print raw data to shell in hexadecimal (prefixed)
264  *
265  * @param sh Shell instance
266  * @param copy A pointer on the coredump copy current context
267  * @param size Size of the data to recover/print
268  * @param error A boolean indicating so query coredump error at the end
269  * @return 0 on success, -EINVAL otherwise
270  */
print_raw_data(const struct shell * sh,struct coredump_cmd_copy_arg * copy,int size,bool error)271 static int print_raw_data(const struct shell *sh,
272 			  struct coredump_cmd_copy_arg *copy,
273 			  int size, bool error)
274 {
275 	int ret;
276 
277 	copy->length = COPY_BUF_SZ;
278 
279 	print_buf_ptr = 0;
280 	(void)memset(print_buf, 0, sizeof(print_buf));
281 
282 	shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, COREDUMP_BEGIN_STR);
283 
284 	while (size > 0) {
285 		if (size < COPY_BUF_SZ) {
286 			copy->length = size;
287 		}
288 
289 		ret = coredump_cmd(COREDUMP_CMD_COPY_STORED_DUMP, copy);
290 		if (ret != 0) {
291 			return -EINVAL;
292 		}
293 
294 		ret = print_stored_dump(sh, copy->buffer, copy->length);
295 		if (ret != 0) {
296 			return -EINVAL;
297 		}
298 
299 		if (print_buf_ptr != 0) {
300 			shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, print_buf);
301 		}
302 
303 		copy->offset += copy->length;
304 		size -= copy->length;
305 	}
306 
307 	if (error && coredump_query(COREDUMP_QUERY_GET_ERROR, NULL) != 0) {
308 		shell_print(sh, "%s%s", COREDUMP_PREFIX_STR,
309 			    COREDUMP_ERROR_STR);
310 	}
311 
312 	shell_print(sh, "%s%s\n", COREDUMP_PREFIX_STR, COREDUMP_END_STR);
313 
314 	return 0;
315 }
316 
317 /**
318  * @brief Helper parsing and pretty-printing the coredump
319  *
320  * @param sh shell instance
321  * @param coredump_header Pointer to a boolean indicating zephyr coredump
322  *        got parsed/printed aleardy header
323  * @param copy A pointer on the coredump copy context
324  * @param left_size How much of the coredump has not been parsed/printed yet
325  * @return 0 if all has been processed, a positive value indicating the amount
326  *        that the function call has processed, a negative errno otherwise
327  */
parse_and_print_coredump(const struct shell * sh,bool * coredump_header,struct coredump_cmd_copy_arg * copy,size_t left_size)328 static int parse_and_print_coredump(const struct shell *sh,
329 				    bool *coredump_header,
330 				    struct coredump_cmd_copy_arg *copy,
331 				    size_t left_size)
332 {
333 	int processed_size = 0;
334 	int data_size = 0;
335 	int ret;
336 
337 	if (!*coredump_header) {
338 		copy->length = sizeof(struct coredump_hdr_t);
339 	} else {
340 		/* thread header is of same size, but memory header */
341 		copy->length = sizeof(struct coredump_arch_hdr_t);
342 	}
343 
344 	if (copy->length > left_size) {
345 		return -ENOMEM;
346 	}
347 
348 	ret = coredump_cmd(COREDUMP_CMD_COPY_STORED_DUMP, copy);
349 	if (ret != 0) {
350 		return ret;
351 	}
352 
353 	if (!*coredump_header) {
354 		ret = print_coredump_hdr(sh, copy->buffer);
355 		shell_print(sh, "\tSize of the coredump: %lu\n",
356 			    (unsigned long)left_size);
357 
358 		*coredump_header = true;
359 
360 		goto hdr_done;
361 	}
362 
363 	/* hdr id */
364 	switch (copy->buffer[0]) {
365 	case COREDUMP_ARCH_HDR_ID: {
366 		struct coredump_arch_hdr_t *hdr =
367 			(struct coredump_arch_hdr_t *)copy->buffer;
368 
369 		shell_print(sh, "-> Arch coredump header found");
370 		shell_print(sh, "\tVersion %u", hdr->hdr_version);
371 		shell_print(sh, "\tSize %u", hdr->num_bytes);
372 
373 		data_size = hdr->num_bytes;
374 		break;
375 	}
376 	case THREADS_META_HDR_ID: {
377 		struct coredump_threads_meta_hdr_t *hdr =
378 			(struct coredump_threads_meta_hdr_t *)copy->buffer;
379 
380 		shell_print(sh, "-> Thread coredump header found");
381 		shell_print(sh, "\tVersion %u", hdr->hdr_version);
382 		shell_print(sh, "\tSize %u", hdr->num_bytes);
383 
384 		data_size = hdr->num_bytes;
385 		break;
386 	}
387 	case COREDUMP_MEM_HDR_ID: {
388 		struct coredump_mem_hdr_t *hdr;
389 
390 		copy->length = sizeof(struct coredump_mem_hdr_t);
391 		if (copy->length > left_size) {
392 			return -ENOMEM;
393 		}
394 
395 		ret = coredump_cmd(COREDUMP_CMD_COPY_STORED_DUMP, copy);
396 		if (ret != 0) {
397 			return -ENOMEM;
398 		}
399 
400 		hdr = (struct coredump_mem_hdr_t *)copy->buffer;
401 
402 		if (sizeof(uintptr_t) == 8) {
403 			hdr->start = sys_le64_to_cpu(hdr->start);
404 			hdr->end = sys_le64_to_cpu(hdr->end);
405 		} else {
406 			hdr->start = sys_le32_to_cpu(hdr->start);
407 			hdr->end = sys_le32_to_cpu(hdr->end);
408 		}
409 
410 		data_size = hdr->end - hdr->start;
411 
412 		shell_print(sh, "-> Memory coredump header found");
413 		shell_print(sh, "\tVersion %u", hdr->hdr_version);
414 		shell_print(sh, "\tSize %u", data_size);
415 		shell_print(sh, "\tStarts at %p ends at %p",
416 			    (void *)hdr->start, (void *)hdr->end);
417 		break;
418 	}
419 	default:
420 		return -EINVAL;
421 	}
422 
423 	if (data_size > left_size) {
424 		return -ENOMEM;
425 	}
426 
427 hdr_done:
428 	copy->offset += copy->length;
429 	processed_size += copy->length + data_size;
430 
431 	if (data_size == 0) {
432 		goto out;
433 	}
434 
435 	shell_print(sh, "Data:");
436 
437 	ret = print_raw_data(sh, copy, data_size, false);
438 	if (ret !=  0) {
439 		return ret;
440 	}
441 out:
442 	return processed_size;
443 }
444 
445 
446 /**
447  * @brief Print out the coredump in a human-readable way
448  *
449  * @param sh Shell instance
450  * @param size Size of the coredump
451  * @return 0
452  */
pretty_print_coredump(const struct shell * sh,int size)453 static int pretty_print_coredump(const struct shell *sh, int size)
454 {
455 	uint8_t rbuf[COPY_BUF_SZ];
456 	struct coredump_cmd_copy_arg copy = {
457 		.offset = 0,
458 		.buffer = rbuf,
459 	};
460 	bool cdump_hdr = false;
461 	int ret;
462 
463 	while (size > 0) {
464 		ret = parse_and_print_coredump(sh, &cdump_hdr, &copy, size);
465 		if (ret < 0) {
466 			goto error;
467 		}
468 
469 		if (ret == 0) {
470 			break;
471 		}
472 
473 		size -= ret;
474 	}
475 
476 	shell_print(sh, "Stored coredump printed");
477 
478 	goto out;
479 error:
480 	if (ret == -ENOTSUP) {
481 		shell_print(sh, "Unsupported command from the backend");
482 	} else {
483 		shell_print(sh, "Error while retrieving/parsing coredump: %d", ret);
484 	}
485 out:
486 	return 0;
487 }
488 
489 /**
490  * @brief Print out the coredump fully in hexadecimal
491  *
492  * @param sh Shell instance
493  * @param size Size of the coredump
494  * @return 0
495  */
hex_print_coredump(const struct shell * sh,int size)496 static int hex_print_coredump(const struct shell *sh, int size)
497 {
498 	uint8_t rbuf[COPY_BUF_SZ];
499 	struct coredump_cmd_copy_arg copy = {
500 		.offset = 0,
501 		.buffer = rbuf,
502 	};
503 	int ret;
504 
505 	ret = print_raw_data(sh, &copy, size, true);
506 	if (ret == 0) {
507 		shell_print(sh, "Stored coredump printed.");
508 	} else {
509 		shell_print(sh, "Failed to print: %d", ret);
510 	}
511 
512 	return 0;
513 }
514 
515 /**
516  * @brief Shell command to print stored coredump data to shell
517  *
518  * @param sh Shell instance
519  * @param argc Number of elements in argv
520  * @param argv Parsed arguments
521  * @return 0
522  */
cmd_coredump_print_stored_dump(const struct shell * sh,size_t argc,char ** argv)523 static int cmd_coredump_print_stored_dump(const struct shell *sh,
524 					  size_t argc, char **argv)
525 {
526 	int size;
527 	int ret;
528 
529 	if (argc > 2) {
530 		shell_print(sh, "Too many options");
531 		return 0;
532 	}
533 
534 	if (argv[1] != NULL) {
535 		if (strncmp(argv[1], "pretty", 6) != 0) {
536 			shell_print(sh, "Unknown option: %s", argv[1]);
537 			return 0;
538 		}
539 	}
540 
541 	/* Verify first to see if stored dump is valid */
542 	ret = coredump_cmd(COREDUMP_CMD_VERIFY_STORED_DUMP, NULL);
543 	if (ret == 0) {
544 		shell_print(sh, "Stored coredump verification failed "
545 			    "or there is no stored coredump.");
546 		return 0;
547 	} else if (ret == -ENOTSUP) {
548 		shell_print(sh, "Unsupported command from the backend");
549 		return 0;
550 	} else if (ret != 1) {
551 		shell_print(sh, "Failed to perform verify command: %d", ret);
552 		return 0;
553 	}
554 
555 	size = coredump_query(COREDUMP_QUERY_GET_STORED_DUMP_SIZE, NULL);
556 	if (size <= 0) {
557 		shell_print(sh, "Invalid coredump size: %d", size);
558 		return 0;
559 	}
560 
561 	if (argv[1] != NULL) {
562 		pretty_print_coredump(sh, size);
563 	} else {
564 		hex_print_coredump(sh, size);
565 	}
566 
567 	return 0;
568 }
569 
570 /**
571  * @brief Shell command to erase stored coredump.
572  *
573  * @param sh shell instance
574  * @param argc (not used)
575  * @param argv (not used)
576  * @return 0
577  */
cmd_coredump_erase_stored_dump(const struct shell * sh,size_t argc,char ** argv)578 static int cmd_coredump_erase_stored_dump(const struct shell *sh,
579 					  size_t argc, char **argv)
580 {
581 	int ret;
582 
583 	ARG_UNUSED(argc);
584 	ARG_UNUSED(argv);
585 
586 	ret = coredump_cmd(COREDUMP_CMD_ERASE_STORED_DUMP, NULL);
587 	if (ret == 0) {
588 		shell_print(sh, "Stored coredump erased.");
589 	} else if (ret == -ENOTSUP) {
590 		shell_print(sh, "Unsupported command from the backend");
591 	} else {
592 		shell_print(sh, "Failed to perform erase command: %d", ret);
593 	}
594 
595 	return 0;
596 }
597 
598 SHELL_STATIC_SUBCMD_SET_CREATE(sub_coredump_error,
599 	SHELL_CMD(clear, NULL, "Clear Coredump error",
600 		  cmd_coredump_error_clear),
601 	SHELL_CMD(get, NULL, "Get Coredump error", cmd_coredump_error_get),
602 	SHELL_SUBCMD_SET_END /* Array terminated. */
603 );
604 
605 SHELL_STATIC_SUBCMD_SET_CREATE(sub_coredump,
606 	SHELL_CMD(error, &sub_coredump_error,
607 		  "Get/clear backend error.", NULL),
608 	SHELL_CMD(erase, NULL,
609 		  "Erase stored coredump",
610 		  cmd_coredump_erase_stored_dump),
611 	SHELL_CMD(find, NULL,
612 		  "Query if there is a stored coredump",
613 		  cmd_coredump_has_stored_dump),
614 	SHELL_CMD(print, NULL,
615 		  "Print stored coredump to shell "
616 		  "(use option 'pretty' to get human readable output)",
617 		  cmd_coredump_print_stored_dump),
618 	SHELL_CMD(verify, NULL,
619 		  "Verify stored coredump",
620 		  cmd_coredump_verify_stored_dump),
621 	SHELL_SUBCMD_SET_END /* Array terminated. */
622 );
623 
624 SHELL_CMD_REGISTER(coredump, &sub_coredump,
625 		   "Coredump commands (" COREDUMP_BACKEND_STR " backend)",
626 		   NULL);
627