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