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