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