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