1 /*
2  * Copyright (c) 2020 Grinn
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "bootutil/bootutil_public.h"
8 #include <zephyr/dfu/mcuboot.h>
9 #include <zephyr/init.h>
10 #include <zephyr/shell/shell.h>
11 #include <stdlib.h>
12 
13 #include "mcuboot_priv.h"
14 
15 #ifdef CONFIG_RETENTION_BOOT_MODE
16 #include <zephyr/retention/bootmode.h>
17 #ifdef CONFIG_REBOOT
18 #include <zephyr/sys/reboot.h>
19 #endif
20 #endif
21 
22 struct area_desc {
23 	const char *name;
24 	unsigned int id;
25 };
26 
27 static const struct area_desc areas[] = {
28 	{"primary", FLASH_AREA_IMAGE_PRIMARY},
29 #ifdef FLASH_AREA_IMAGE_SECONDARY
30 	{"secondary", FLASH_AREA_IMAGE_SECONDARY},
31 #endif
32 };
33 
swap_state_magic_str(uint8_t magic)34 static const char *swap_state_magic_str(uint8_t magic)
35 {
36 	switch (magic) {
37 	case BOOT_MAGIC_GOOD:
38 		return "good";
39 	case BOOT_MAGIC_BAD:
40 		return "bad";
41 	case BOOT_MAGIC_UNSET:
42 		return "unset";
43 	case BOOT_MAGIC_ANY:
44 		return "any";
45 	case BOOT_MAGIC_NOTGOOD:
46 		return "notgood";
47 	}
48 
49 	return "unknown";
50 }
51 
swap_type_str(uint8_t type)52 static const char *swap_type_str(uint8_t type)
53 {
54 	switch (type) {
55 	case BOOT_SWAP_TYPE_NONE:
56 		return "none";
57 	case BOOT_SWAP_TYPE_TEST:
58 		return "test";
59 	case BOOT_SWAP_TYPE_PERM:
60 		return "perm";
61 	case BOOT_SWAP_TYPE_REVERT:
62 		return "revert";
63 	case BOOT_SWAP_TYPE_FAIL:
64 		return "fail";
65 	}
66 
67 	return "unknown";
68 }
69 
swap_state_flag_str(uint8_t flag)70 static const char *swap_state_flag_str(uint8_t flag)
71 {
72 	switch (flag) {
73 	case BOOT_FLAG_SET:
74 		return "set";
75 	case BOOT_FLAG_BAD:
76 		return "bad";
77 	case BOOT_FLAG_UNSET:
78 		return "unset";
79 	case BOOT_FLAG_ANY:
80 		return "any";
81 	}
82 
83 	return "unknown";
84 }
85 
cmd_mcuboot_erase(const struct shell * sh,size_t argc,char ** argv)86 static int cmd_mcuboot_erase(const struct shell *sh, size_t argc,
87 			     char **argv)
88 {
89 	unsigned int id;
90 	int err;
91 
92 	id = strtoul(argv[1], NULL, 0);
93 
94 	err = boot_erase_img_bank(id);
95 	if (err) {
96 		shell_error(sh, "failed to erase bank %u", id);
97 		return err;
98 	}
99 
100 	return 0;
101 }
102 
cmd_mcuboot_confirm(const struct shell * sh,size_t argc,char ** argv)103 static int cmd_mcuboot_confirm(const struct shell *sh, size_t argc,
104 			       char **argv)
105 {
106 	int err;
107 
108 	err = boot_write_img_confirmed();
109 	if (err) {
110 		shell_error(sh, "failed to confirm: %d", err);
111 	}
112 
113 	return err;
114 }
115 
cmd_mcuboot_request_upgrade(const struct shell * sh,size_t argc,char ** argv)116 static int cmd_mcuboot_request_upgrade(const struct shell *sh, size_t argc,
117 				       char **argv)
118 {
119 	int permanent = 0;
120 	int err;
121 
122 	if (argc > 1) {
123 		if (!strcmp(argv[1], "permanent")) {
124 			permanent = 1;
125 		} else {
126 			shell_warn(sh, "invalid argument!");
127 			return -EINVAL;
128 		}
129 	}
130 
131 	err = boot_request_upgrade(permanent);
132 	if (err) {
133 		shell_error(sh, "failed to request upgrade: %d", err);
134 	}
135 
136 	return err;
137 }
138 
139 #ifdef CONFIG_RETENTION_BOOT_MODE
cmd_mcuboot_serial_recovery(const struct shell * sh,size_t argc,char ** argv)140 static int cmd_mcuboot_serial_recovery(const struct shell *sh, size_t argc,
141 				       char **argv)
142 {
143 	int rc;
144 
145 	rc = bootmode_set(BOOT_MODE_TYPE_BOOTLOADER);
146 
147 	if (rc) {
148 		shell_error(sh, "Failed to set serial recovery mode: %d", rc);
149 
150 		return rc;
151 	}
152 
153 #ifdef CONFIG_REBOOT
154 	sys_reboot(SYS_REBOOT_COLD);
155 #else
156 	shell_error(sh, "mcuboot serial recovery mode set, please reboot your device");
157 #endif
158 
159 	return rc;
160 }
161 #endif
162 
cmd_mcuboot_info_area(const struct shell * sh,const struct area_desc * area)163 static int cmd_mcuboot_info_area(const struct shell *sh,
164 				 const struct area_desc *area)
165 {
166 	struct mcuboot_img_header hdr;
167 	struct boot_swap_state swap_state;
168 	int err;
169 
170 	err = boot_read_bank_header(area->id, &hdr, sizeof(hdr));
171 	if (err) {
172 		shell_error(sh, "failed to read %s area (%u) %s: %d",
173 			    area->name, area->id, "header", err);
174 		return err;
175 	}
176 
177 	shell_print(sh, "%s area (%u):", area->name, area->id);
178 	shell_print(sh, "  version: %u.%u.%u+%u",
179 		    (unsigned int) hdr.h.v1.sem_ver.major,
180 		    (unsigned int) hdr.h.v1.sem_ver.minor,
181 		    (unsigned int) hdr.h.v1.sem_ver.revision,
182 		    (unsigned int) hdr.h.v1.sem_ver.build_num);
183 	shell_print(sh, "  image size: %u",
184 		    (unsigned int) hdr.h.v1.image_size);
185 
186 	err = boot_read_swap_state_by_id(area->id, &swap_state);
187 	if (err) {
188 		shell_error(sh, "failed to read %s area (%u) %s: %d",
189 			    area->name, area->id, "swap state", err);
190 		return err;
191 	}
192 
193 	shell_print(sh, "  magic: %s",
194 		    swap_state_magic_str(swap_state.magic));
195 
196 	if (IS_ENABLED(CONFIG_MCUBOOT_TRAILER_SWAP_TYPE)) {
197 		shell_print(sh, "  swap type: %s",
198 			    swap_type_str(swap_state.swap_type));
199 	}
200 
201 	shell_print(sh, "  copy done: %s",
202 		    swap_state_flag_str(swap_state.copy_done));
203 	shell_print(sh, "  image ok: %s",
204 		    swap_state_flag_str(swap_state.image_ok));
205 
206 	return 0;
207 }
208 
cmd_mcuboot_info(const struct shell * sh,size_t argc,char ** argv)209 static int cmd_mcuboot_info(const struct shell *sh, size_t argc,
210 			    char **argv)
211 {
212 	int i;
213 
214 	shell_print(sh, "swap type: %s", swap_type_str(mcuboot_swap_type()));
215 	shell_print(sh, "confirmed: %d", boot_is_img_confirmed());
216 
217 	for (i = 0; i < ARRAY_SIZE(areas); i++) {
218 		shell_print(sh, "");
219 		cmd_mcuboot_info_area(sh, &areas[i]);
220 	}
221 
222 	return 0;
223 }
224 
225 SHELL_STATIC_SUBCMD_SET_CREATE(mcuboot_cmds,
226 	SHELL_CMD_ARG(confirm, NULL, "confirm", cmd_mcuboot_confirm, 1, 0),
227 	SHELL_CMD_ARG(erase, NULL, "erase <area_id>", cmd_mcuboot_erase, 2, 0),
228 	SHELL_CMD_ARG(request_upgrade, NULL, "request_upgrade [permanent]",
229 		      cmd_mcuboot_request_upgrade, 1, 1),
230 #ifdef CONFIG_RETENTION_BOOT_MODE
231 	SHELL_CMD_ARG(serial_recovery, NULL, "serial_recovery", cmd_mcuboot_serial_recovery, 1, 0),
232 #endif
233 	SHELL_SUBCMD_SET_END /* Array terminated. */
234 );
235 
236 SHELL_CMD_REGISTER(mcuboot, &mcuboot_cmds, "MCUboot commands",
237 		   cmd_mcuboot_info);
238