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