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, ©, 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, ©, 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