1 /*
2 * Copyright (c) 2020 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <zephyr/kernel.h>
9 #include <string.h>
10 #include <zephyr/toolchain.h>
11 #include <zephyr/storage/flash_map.h>
12 #include <zephyr/storage/stream_flash.h>
13 #include <zephyr/sys/util.h>
14
15 #include <zephyr/debug/coredump.h>
16 #include "coredump_internal.h"
17
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(coredump, CONFIG_KERNEL_LOG_LEVEL);
20
21 /**
22 * @file
23 * @brief Simple coredump backend to store data in flash partition.
24 *
25 * This provides a simple backend to store coredump data in a flash
26 * partition, labeled "coredump-partition" in devicetree.
27 *
28 * On the partition, a header is stored at the beginning with padding
29 * at the end to align with flash write size. Then the actual
30 * coredump data follows. The padding is to simplify the data read
31 * function so that the first read of a data stream is always
32 * aligned to flash write size.
33 */
34 #define FLASH_PARTITION coredump_partition
35 #define FLASH_PARTITION_ID FIXED_PARTITION_ID(FLASH_PARTITION)
36
37 #if !FIXED_PARTITION_EXISTS(FLASH_PARTITION)
38 #error "Need a fixed partition named 'coredump-partition'!"
39
40 #else
41
42 #define FLASH_CONTROLLER \
43 DT_PARENT(DT_PARENT(DT_NODELABEL(FLASH_PARTITION)))
44
45 #define FLASH_WRITE_SIZE DT_PROP(FLASH_CONTROLLER, write_block_size)
46 #define FLASH_BUF_SIZE FLASH_WRITE_SIZE
47 #define FLASH_ERASE_SIZE DT_PROP(FLASH_CONTROLLER, erase_block_size)
48
49 #define HDR_VER 1
50
51 #define FLASH_BACKEND_SEM_TIMEOUT (k_is_in_isr() ? K_NO_WAIT : K_FOREVER)
52
53 typedef int (*data_read_cb_t)(void *arg, uint8_t *buf, size_t len);
54
55 static struct {
56 /* For use with flash map */
57 const struct flash_area *flash_area;
58
59 /* For use with streaming flash */
60 struct stream_flash_ctx stream_ctx;
61
62 /* Checksum of data so far */
63 uint16_t checksum;
64
65 /* Error encountered */
66 int error;
67 } backend_ctx;
68
69 /* Buffer used in stream flash context */
70 static uint8_t stream_flash_buf[FLASH_BUF_SIZE];
71
72 /* Buffer used in data_read() */
73 static uint8_t data_read_buf[FLASH_BUF_SIZE];
74
75 /* Semaphore for exclusive flash access */
76 K_SEM_DEFINE(flash_sem, 1, 1);
77
78
79 struct flash_hdr_t {
80 /* 'C', 'D' */
81 char id[2];
82
83 /* Header version */
84 uint16_t hdr_version;
85
86 /* Coredump size, excluding this header */
87 size_t size;
88
89 /* Flags */
90 uint16_t flags;
91
92 /* Checksum */
93 uint16_t checksum;
94
95 /* Error */
96 int error;
97 } __packed;
98
99
100 /**
101 * @brief Open the flash partition.
102 *
103 * @return Same as flash_area_open().
104 */
partition_open(void)105 static int partition_open(void)
106 {
107 int ret;
108
109 (void)k_sem_take(&flash_sem, FLASH_BACKEND_SEM_TIMEOUT);
110
111 ret = flash_area_open(FLASH_PARTITION_ID, &backend_ctx.flash_area);
112 if (ret != 0) {
113 LOG_ERR("Error opening flash partition for coredump!");
114
115 backend_ctx.flash_area = NULL;
116 k_sem_give(&flash_sem);
117 }
118
119 return ret;
120 }
121
122 /**
123 * @brief Close the flash partition.
124 */
partition_close(void)125 static void partition_close(void)
126 {
127 if (backend_ctx.flash_area == NULL) {
128 return;
129 }
130
131 flash_area_close(backend_ctx.flash_area);
132 backend_ctx.flash_area = NULL;
133
134 k_sem_give(&flash_sem);
135 }
136
137 /**
138 * @brief Read data from flash partition.
139 *
140 * This reads @p len bytes in the flash partition starting
141 * from @p off and put into buffer pointed by @p dst if
142 * @p dst is not NULL.
143 *
144 * If @p cb is not NULL, data being read are passed to
145 * the callback for processing. Note that the data being
146 * passed to callback may only be part of the data requested.
147 * The end of read is signaled to the callback with NULL
148 * buffer pointer and zero length as arguments.
149 *
150 * @param off offset of partition to begin reading
151 * @param dst buffer to read data into (can be NULL)
152 * @param len number of bytes to read
153 * @param cb callback to process read data (can be NULL)
154 * @param cb_arg argument passed to callback
155 * @return 0 if successful, error otherwise.
156 */
data_read(off_t off,uint8_t * dst,size_t len,data_read_cb_t cb,void * cb_arg)157 static int data_read(off_t off, uint8_t *dst, size_t len,
158 data_read_cb_t cb, void *cb_arg)
159 {
160 int ret = 0;
161 off_t offset = off;
162 size_t remaining = len;
163 size_t copy_sz;
164 uint8_t *ptr = dst;
165
166 if (backend_ctx.flash_area == NULL) {
167 return -ENODEV;
168 }
169
170 copy_sz = FLASH_BUF_SIZE;
171 while (remaining > 0) {
172 if (remaining < FLASH_BUF_SIZE) {
173 copy_sz = remaining;
174 }
175
176 ret = flash_area_read(backend_ctx.flash_area, offset,
177 data_read_buf, FLASH_BUF_SIZE);
178 if (ret != 0) {
179 break;
180 }
181
182 if (dst != NULL) {
183 (void)memcpy(ptr, data_read_buf, copy_sz);
184 }
185
186 if (cb != NULL) {
187 ret = (*cb)(cb_arg, data_read_buf, copy_sz);
188 if (ret != 0) {
189 break;
190 }
191 }
192
193 ptr += copy_sz;
194 offset += copy_sz;
195 remaining -= copy_sz;
196 }
197
198 if (cb != NULL) {
199 ret = (*cb)(cb_arg, NULL, 0);
200 }
201
202 return ret;
203 }
204
205 /**
206 * @brief Callback to calculate checksum.
207 *
208 * @param arg callback argument (not being used)
209 * @param buf data buffer
210 * @param len number of bytes in buffer to process
211 * @return 0
212 */
cb_calc_buf_checksum(void * arg,uint8_t * buf,size_t len)213 static int cb_calc_buf_checksum(void *arg, uint8_t *buf, size_t len)
214 {
215 int i;
216
217 ARG_UNUSED(arg);
218
219 for (i = 0; i < len; i++) {
220 backend_ctx.checksum += buf[i];
221 }
222
223 return 0;
224 }
225
226 /**
227 * @brief Process the stored coredump in flash partition.
228 *
229 * This reads the stored coredump data and processes it via
230 * the callback function.
231 *
232 * @param cb callback to process the stored coredump data
233 * @param cb_arg argument passed to callback
234 * @return 1 if successful; 0 if stored coredump is not found
235 * or is not valid; error otherwise
236 */
process_stored_dump(data_read_cb_t cb,void * cb_arg)237 static int process_stored_dump(data_read_cb_t cb, void *cb_arg)
238 {
239 int ret;
240 struct flash_hdr_t hdr;
241 off_t offset;
242
243 ret = partition_open();
244 if (ret != 0) {
245 goto out;
246 }
247
248 /* Read header */
249 ret = data_read(0, (uint8_t *)&hdr, sizeof(hdr), NULL, NULL);
250
251 /* Verify header signature */
252 if ((hdr.id[0] != 'C') && (hdr.id[1] != 'D')) {
253 ret = 0;
254 goto out;
255 }
256
257 /* Error encountered while dumping, so non-existent */
258 if (hdr.error != 0) {
259 ret = 0;
260 goto out;
261 }
262
263 backend_ctx.checksum = 0;
264
265 offset = ROUND_UP(sizeof(struct flash_hdr_t), FLASH_WRITE_SIZE);
266 ret = data_read(offset, NULL, hdr.size, cb, cb_arg);
267
268 if (ret == 0) {
269 ret = (backend_ctx.checksum == hdr.checksum) ? 1 : 0;
270 }
271
272 out:
273 partition_close();
274
275 return ret;
276 }
277
278 /**
279 * @brief Get the stored coredump in flash partition.
280 *
281 * This reads the stored coredump data and copies the raw data
282 * to the destination buffer.
283 *
284 * If the destination buffer is NULL, the offset and length are
285 * ignored and the entire dump size is returned.
286 *
287 * @param off offset of partition to begin reading
288 * @param dst buffer to read data into (can be NULL)
289 * @param len number of bytes to read
290 * @return dump size if successful; 0 if stored coredump is not found
291 * or is not valid; error otherwise
292 */
get_stored_dump(off_t off,uint8_t * dst,size_t len)293 static int get_stored_dump(off_t off, uint8_t *dst, size_t len)
294 {
295 int ret;
296 struct flash_hdr_t hdr;
297
298 ret = partition_open();
299 if (ret != 0) {
300 goto out;
301 }
302
303 /* Read header */
304 ret = data_read(0, (uint8_t *)&hdr, sizeof(hdr), NULL, NULL);
305 if (ret != 0) {
306 goto out;
307 }
308
309 /* Verify header signature */
310 if ((hdr.id[0] != 'C') && (hdr.id[1] != 'D')) {
311 ret = 0;
312 goto out;
313 }
314
315 /* Error encountered while dumping, so non-existent */
316 if (hdr.error != 0) {
317 ret = 0;
318 goto out;
319 }
320
321 /* Return the dump size if no destination buffer available */
322 if (!dst) {
323 ret = (int)hdr.size;
324 goto out;
325 }
326
327 /* Offset larger than dump size */
328 if (off >= hdr.size) {
329 ret = 0;
330 goto out;
331 }
332
333 /* Start reading the data, skip write-aligned header */
334 off += ROUND_UP(sizeof(struct flash_hdr_t), FLASH_WRITE_SIZE);
335
336 ret = data_read(off, dst, len, NULL, NULL);
337 if (ret == 0) {
338 ret = (int)len;
339 }
340 out:
341 partition_close();
342
343 return ret;
344 }
345
346 /**
347 * @brief Erase the stored coredump header from flash partition.
348 *
349 * This erases the stored coredump header from the flash partition,
350 * invalidating the coredump data.
351 *
352 * @return 0 if successful; error otherwise
353 */
erase_coredump_header(void)354 static int erase_coredump_header(void)
355 {
356 int ret;
357
358 ret = partition_open();
359 if (ret == 0) {
360 /* Erase header block */
361 ret = flash_area_erase(backend_ctx.flash_area, 0,
362 ROUND_UP(sizeof(struct flash_hdr_t),
363 FLASH_ERASE_SIZE));
364 }
365
366 partition_close();
367
368 return ret;
369 }
370
371 /**
372 * @brief Erase the stored coredump in flash partition.
373 *
374 * This erases the stored coredump data from the flash partition.
375 *
376 * @return 0 if successful; error otherwise
377 */
erase_flash_partition(void)378 static int erase_flash_partition(void)
379 {
380 int ret;
381
382 ret = partition_open();
383 if (ret == 0) {
384 /* Erase whole flash partition */
385 ret = flash_area_erase(backend_ctx.flash_area, 0,
386 backend_ctx.flash_area->fa_size);
387 }
388
389 partition_close();
390
391 return ret;
392 }
393
394 /**
395 * @brief Start of coredump session.
396 *
397 * This opens the flash partition for processing.
398 */
coredump_flash_backend_start(void)399 static void coredump_flash_backend_start(void)
400 {
401 const struct device *flash_dev;
402 size_t offset, header_size;
403 int ret;
404
405 ret = partition_open();
406
407 if (ret == 0) {
408 /* Erase whole flash partition */
409 ret = flash_area_erase(backend_ctx.flash_area, 0,
410 backend_ctx.flash_area->fa_size);
411 }
412
413 if (ret == 0) {
414 backend_ctx.checksum = 0;
415
416 flash_dev = flash_area_get_device(backend_ctx.flash_area);
417
418 /*
419 * Reserve space for header from beginning of flash device.
420 * The header size is rounded up so the beginning of coredump
421 * is aligned to write size (for easier read and seek).
422 */
423 header_size = ROUND_UP(sizeof(struct flash_hdr_t), FLASH_WRITE_SIZE);
424 offset = backend_ctx.flash_area->fa_off + header_size;
425
426 ret = stream_flash_init(&backend_ctx.stream_ctx, flash_dev,
427 stream_flash_buf,
428 sizeof(stream_flash_buf),
429 offset,
430 backend_ctx.flash_area->fa_size - header_size,
431 NULL);
432 }
433
434 if (ret != 0) {
435 LOG_ERR("Cannot start coredump!");
436 backend_ctx.error = ret;
437 partition_close();
438 }
439 }
440
441 /**
442 * @brief End of coredump session.
443 *
444 * This ends the coredump session by flushing coredump data
445 * flash, and writes the header in the beginning of flash
446 * related to the stored coredump data.
447 */
coredump_flash_backend_end(void)448 static void coredump_flash_backend_end(void)
449 {
450 int ret;
451
452 struct flash_hdr_t hdr = {
453 .id = {'C', 'D'},
454 .hdr_version = HDR_VER,
455 };
456
457 if (backend_ctx.flash_area == NULL) {
458 return;
459 }
460
461 /* Flush buffer */
462 backend_ctx.error = stream_flash_buffered_write(
463 &backend_ctx.stream_ctx,
464 stream_flash_buf, 0, true);
465
466 /* Write header */
467 hdr.size = stream_flash_bytes_written(&backend_ctx.stream_ctx);
468 hdr.checksum = backend_ctx.checksum;
469 hdr.error = backend_ctx.error;
470 hdr.flags = 0;
471
472 ret = flash_area_write(backend_ctx.flash_area, 0, (void *)&hdr, sizeof(hdr));
473 if (ret != 0) {
474 LOG_ERR("Cannot write coredump header!");
475 backend_ctx.error = ret;
476 }
477
478 if (backend_ctx.error != 0) {
479 LOG_ERR("Error in coredump backend (%d)!",
480 backend_ctx.error);
481 }
482
483 partition_close();
484 }
485
486 /**
487 * @brief Write a buffer to flash partition.
488 *
489 * This writes @p buf into the flash partition. Note that this is
490 * using the stream flash interface, so there is no need to keep
491 * track of where on flash to write next.
492 *
493 * @param buf buffer of data to write to flash
494 * @param buflen number of bytes to write
495 */
coredump_flash_backend_buffer_output(uint8_t * buf,size_t buflen)496 static void coredump_flash_backend_buffer_output(uint8_t *buf, size_t buflen)
497 {
498 int i;
499 size_t remaining = buflen;
500 size_t copy_sz;
501 uint8_t *ptr = buf;
502 uint8_t tmp_buf[FLASH_BUF_SIZE];
503
504 if ((backend_ctx.error != 0) || (backend_ctx.flash_area == NULL)) {
505 return;
506 }
507
508 /*
509 * Since the system is still running, memory content is constantly
510 * changing (e.g. stack of this thread). We need to make a copy of
511 * part of the buffer, so that the checksum corresponds to what is
512 * being written.
513 */
514 copy_sz = FLASH_BUF_SIZE;
515 while (remaining > 0) {
516 if (remaining < FLASH_BUF_SIZE) {
517 copy_sz = remaining;
518 }
519
520 (void)memcpy(tmp_buf, ptr, copy_sz);
521
522 for (i = 0; i < copy_sz; i++) {
523 backend_ctx.checksum += tmp_buf[i];
524 }
525
526 backend_ctx.error = stream_flash_buffered_write(
527 &backend_ctx.stream_ctx,
528 tmp_buf, copy_sz, false);
529 if (backend_ctx.error != 0) {
530 break;
531 }
532
533 ptr += copy_sz;
534 remaining -= copy_sz;
535 }
536 }
537
538 /**
539 * @brief Perform query on this backend.
540 *
541 * @param query_id ID of query
542 * @param arg argument of query
543 * @return depends on query
544 */
coredump_flash_backend_query(enum coredump_query_id query_id,void * arg)545 static int coredump_flash_backend_query(enum coredump_query_id query_id,
546 void *arg)
547 {
548 int ret;
549
550 switch (query_id) {
551 case COREDUMP_QUERY_GET_ERROR:
552 ret = backend_ctx.error;
553 break;
554 case COREDUMP_QUERY_HAS_STORED_DUMP:
555 ret = process_stored_dump(cb_calc_buf_checksum, NULL);
556 break;
557 case COREDUMP_QUERY_GET_STORED_DUMP_SIZE:
558 ret = get_stored_dump(0, NULL, 0);
559 break;
560 default:
561 ret = -ENOTSUP;
562 break;
563 }
564
565 return ret;
566 }
567
568 /**
569 * @brief Perform command on this backend.
570 *
571 * @param cmd_id command ID
572 * @param arg argument of command
573 * @return depends on query
574 */
coredump_flash_backend_cmd(enum coredump_cmd_id cmd_id,void * arg)575 static int coredump_flash_backend_cmd(enum coredump_cmd_id cmd_id,
576 void *arg)
577 {
578 int ret;
579
580 switch (cmd_id) {
581 case COREDUMP_CMD_CLEAR_ERROR:
582 ret = 0;
583 backend_ctx.error = 0;
584 break;
585 case COREDUMP_CMD_VERIFY_STORED_DUMP:
586 ret = process_stored_dump(cb_calc_buf_checksum, NULL);
587 break;
588 case COREDUMP_CMD_ERASE_STORED_DUMP:
589 ret = erase_flash_partition();
590 break;
591 case COREDUMP_CMD_COPY_STORED_DUMP:
592 if (arg) {
593 struct coredump_cmd_copy_arg *copy_arg
594 = (struct coredump_cmd_copy_arg *)arg;
595
596 ret = get_stored_dump(copy_arg->offset,
597 copy_arg->buffer,
598 copy_arg->length);
599 } else {
600 ret = -EINVAL;
601 }
602 break;
603 case COREDUMP_CMD_INVALIDATE_STORED_DUMP:
604 ret = erase_coredump_header();
605 break;
606 default:
607 ret = -ENOTSUP;
608 break;
609 }
610
611 return ret;
612 }
613
614
615 struct coredump_backend_api coredump_backend_flash_partition = {
616 .start = coredump_flash_backend_start,
617 .end = coredump_flash_backend_end,
618 .buffer_output = coredump_flash_backend_buffer_output,
619 .query = coredump_flash_backend_query,
620 .cmd = coredump_flash_backend_cmd,
621 };
622
623 #ifdef CONFIG_DEBUG_COREDUMP_SHELL
624 #include <zephyr/shell/shell.h>
625
626 /* Length of buffer of printable size */
627 #define PRINT_BUF_SZ 64
628
629 /* Length of buffer of printable size plus null character */
630 #define PRINT_BUF_SZ_RAW (PRINT_BUF_SZ + 1)
631
632 /* Print buffer */
633 static char print_buf[PRINT_BUF_SZ_RAW];
634 static off_t print_buf_ptr;
635
636 /**
637 * @brief Shell command to get backend error.
638 *
639 * @param sh shell instance
640 * @param argc (not used)
641 * @param argv (not used)
642 * @return 0
643 */
cmd_coredump_error_get(const struct shell * sh,size_t argc,char ** argv)644 static int cmd_coredump_error_get(const struct shell *sh,
645 size_t argc, char **argv)
646 {
647 ARG_UNUSED(argc);
648 ARG_UNUSED(argv);
649
650 if (backend_ctx.error == 0) {
651 shell_print(sh, "No error.");
652 } else {
653 shell_print(sh, "Error: %d", backend_ctx.error);
654 }
655
656 return 0;
657 }
658
659 /**
660 * @brief Shell command to clear backend error.
661 *
662 * @param sh shell instance
663 * @param argc (not used)
664 * @param argv (not used)
665 * @return 0
666 */
cmd_coredump_error_clear(const struct shell * sh,size_t argc,char ** argv)667 static int cmd_coredump_error_clear(const struct shell *sh,
668 size_t argc, char **argv)
669 {
670 backend_ctx.error = 0;
671
672 shell_print(sh, "Error cleared.");
673
674 return 0;
675 }
676
677 /**
678 * @brief Shell command to see if there is a stored coredump in flash.
679 *
680 * @param sh shell instance
681 * @param argc (not used)
682 * @param argv (not used)
683 * @return 0
684 */
cmd_coredump_has_stored_dump(const struct shell * sh,size_t argc,char ** argv)685 static int cmd_coredump_has_stored_dump(const struct shell *sh,
686 size_t argc, char **argv)
687 {
688 int ret;
689
690 ARG_UNUSED(argc);
691 ARG_UNUSED(argv);
692
693 ret = coredump_flash_backend_query(COREDUMP_QUERY_HAS_STORED_DUMP,
694 NULL);
695
696 if (ret == 1) {
697 shell_print(sh, "Stored coredump found.");
698 } else if (ret == 0) {
699 shell_print(sh, "Stored coredump NOT found.");
700 } else {
701 shell_print(sh, "Failed to perform query: %d", ret);
702 }
703
704 return 0;
705 }
706
707 /**
708 * @brief Shell command to verify if the stored coredump is valid.
709 *
710 * @param sh shell instance
711 * @param argc (not used)
712 * @param argv (not used)
713 * @return 0
714 */
cmd_coredump_verify_stored_dump(const struct shell * sh,size_t argc,char ** argv)715 static int cmd_coredump_verify_stored_dump(const struct shell *sh,
716 size_t argc, char **argv)
717 {
718 int ret;
719
720 ARG_UNUSED(argc);
721 ARG_UNUSED(argv);
722
723 ret = coredump_flash_backend_cmd(COREDUMP_CMD_VERIFY_STORED_DUMP,
724 NULL);
725
726 if (ret == 1) {
727 shell_print(sh, "Stored coredump verified.");
728 } else if (ret == 0) {
729 shell_print(sh, "Stored coredump verification failed "
730 "or there is no stored coredump.");
731 } else {
732 shell_print(sh, "Failed to perform verify command: %d",
733 ret);
734 }
735
736 return 0;
737 }
738
739 /**
740 * @brief Flush the print buffer to shell.
741 *
742 * This prints what is in the print buffer to the shell.
743 *
744 * @param sh shell instance.
745 */
flush_print_buf(const struct shell * sh)746 static void flush_print_buf(const struct shell *sh)
747 {
748 shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, print_buf);
749 print_buf_ptr = 0;
750 (void)memset(print_buf, 0, sizeof(print_buf));
751 }
752
753 /**
754 * @brief Callback to print stored coredump to shell
755 *
756 * This converts the binary data in @p buf to hexadecimal digits
757 * which can be printed to the shell.
758 *
759 * @param arg shell instance
760 * @param buf binary data buffer
761 * @param len number of bytes in buffer to be printed
762 * @return 0 if no issues; -EINVAL if error converting data
763 */
cb_print_stored_dump(void * arg,uint8_t * buf,size_t len)764 static int cb_print_stored_dump(void *arg, uint8_t *buf, size_t len)
765 {
766 int ret = 0;
767 size_t i = 0;
768 size_t remaining = len;
769 const struct shell *sh = (const struct shell *)arg;
770
771 if (len == 0) {
772 /* Flush print buffer */
773 flush_print_buf(sh);
774
775 goto out;
776 }
777
778 /* Do checksum for process_stored_dump() */
779 cb_calc_buf_checksum(arg, buf, len);
780
781 while (remaining > 0) {
782 if (hex2char(buf[i] >> 4, &print_buf[print_buf_ptr]) < 0) {
783 ret = -EINVAL;
784 break;
785 }
786 print_buf_ptr++;
787
788 if (hex2char(buf[i] & 0xf, &print_buf[print_buf_ptr]) < 0) {
789 ret = -EINVAL;
790 break;
791 }
792 print_buf_ptr++;
793
794 remaining--;
795 i++;
796
797 if (print_buf_ptr == PRINT_BUF_SZ) {
798 flush_print_buf(sh);
799 }
800 }
801
802 out:
803 return ret;
804 }
805
806 /**
807 * @brief Shell command to print stored coredump data to shell
808 *
809 * @param sh shell instance
810 * @param argc (not used)
811 * @param argv (not used)
812 * @return 0
813 */
cmd_coredump_print_stored_dump(const struct shell * sh,size_t argc,char ** argv)814 static int cmd_coredump_print_stored_dump(const struct shell *sh,
815 size_t argc, char **argv)
816 {
817 int ret;
818
819 ARG_UNUSED(argc);
820 ARG_UNUSED(argv);
821
822 /* Verify first to see if stored dump is valid */
823 ret = coredump_flash_backend_cmd(COREDUMP_CMD_VERIFY_STORED_DUMP,
824 NULL);
825
826 if (ret == 0) {
827 shell_print(sh, "Stored coredump verification failed "
828 "or there is no stored coredump.");
829 goto out;
830 } else if (ret != 1) {
831 shell_print(sh, "Failed to perform verify command: %d",
832 ret);
833 goto out;
834 }
835
836 /* If valid, start printing to shell */
837 print_buf_ptr = 0;
838 (void)memset(print_buf, 0, sizeof(print_buf));
839
840 shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, COREDUMP_BEGIN_STR);
841
842 ret = process_stored_dump(cb_print_stored_dump, (void *)sh);
843 if (print_buf_ptr != 0) {
844 shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, print_buf);
845 }
846
847 if (backend_ctx.error != 0) {
848 shell_print(sh, "%s%s", COREDUMP_PREFIX_STR,
849 COREDUMP_ERROR_STR);
850 }
851
852 shell_print(sh, "%s%s", COREDUMP_PREFIX_STR, COREDUMP_END_STR);
853
854 if (ret == 1) {
855 shell_print(sh, "Stored coredump printed.");
856 } else if (ret == 0) {
857 shell_print(sh, "Stored coredump verification failed "
858 "or there is no stored coredump.");
859 } else {
860 shell_print(sh, "Failed to print: %d", ret);
861 }
862
863 out:
864 return 0;
865 }
866
867 /**
868 * @brief Shell command to erase stored coredump.
869 *
870 * @param sh shell instance
871 * @param argc (not used)
872 * @param argv (not used)
873 * @return 0
874 */
cmd_coredump_erase_stored_dump(const struct shell * sh,size_t argc,char ** argv)875 static int cmd_coredump_erase_stored_dump(const struct shell *sh,
876 size_t argc, char **argv)
877 {
878 int ret;
879
880 ARG_UNUSED(argc);
881 ARG_UNUSED(argv);
882
883 ret = coredump_flash_backend_cmd(COREDUMP_CMD_ERASE_STORED_DUMP,
884 NULL);
885
886 if (ret == 0) {
887 shell_print(sh, "Stored coredump erased.");
888 } else {
889 shell_print(sh, "Failed to perform erase command: %d", ret);
890 }
891
892 return 0;
893 }
894
895 SHELL_STATIC_SUBCMD_SET_CREATE(sub_coredump_error,
896 SHELL_CMD(clear, NULL, "Clear Coredump error",
897 cmd_coredump_error_clear),
898 SHELL_CMD(get, NULL, "Get Coredump error", cmd_coredump_error_get),
899 SHELL_SUBCMD_SET_END /* Array terminated. */
900 );
901
902 SHELL_STATIC_SUBCMD_SET_CREATE(sub_coredump,
903 SHELL_CMD(error, &sub_coredump_error,
904 "Get/clear backend error.", NULL),
905 SHELL_CMD(erase, NULL,
906 "Erase stored coredump",
907 cmd_coredump_erase_stored_dump),
908 SHELL_CMD(find, NULL,
909 "Query if there is a stored coredump",
910 cmd_coredump_has_stored_dump),
911 SHELL_CMD(print, NULL,
912 "Print stored coredump to shell",
913 cmd_coredump_print_stored_dump),
914 SHELL_CMD(verify, NULL,
915 "Verify stored coredump",
916 cmd_coredump_verify_stored_dump),
917 SHELL_SUBCMD_SET_END /* Array terminated. */
918 );
919
920 SHELL_CMD_REGISTER(coredump, &sub_coredump,
921 "Coredump commands (flash partition backend)", NULL);
922
923 #endif /* CONFIG_DEBUG_COREDUMP_SHELL */
924
925 #endif /* FIXED_PARTITION_EXISTS(coredump_partition) */
926