1 /*
2 * Copyright (c) 2018-2021 mcumgr authors
3 * Copyright (c) 2022-2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/sys/util_macro.h>
9 #include <zephyr/toolchain.h>
10 #include <string.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/dfu/mcuboot.h>
13
14 #include <zcbor_common.h>
15 #include <zcbor_decode.h>
16 #include <zcbor_encode.h>
17
18 #include <bootutil/bootutil_public.h>
19
20 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
21 #include <zephyr/mgmt/mcumgr/smp/smp.h>
22 #include <zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h>
23
24 #include <mgmt/mcumgr/util/zcbor_bulk.h>
25 #include <mgmt/mcumgr/grp/img_mgmt/img_mgmt_priv.h>
26
27 #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
28 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
29 #endif
30
31 #ifdef CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_HOOK
32 #include <mgmt/mcumgr/transport/smp_internal.h>
33 #endif
34
35 LOG_MODULE_DECLARE(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL);
36
37 #ifndef CONFIG_MCUMGR_GRP_IMG_FRUGAL_LIST
38 #define ZCBOR_ENCODE_FLAG(zse, label, value) \
39 (zcbor_tstr_put_lit(zse, label) && zcbor_bool_put(zse, value))
40 #else
41 /* In "frugal" lists flags are added to response only when they evaluate to true */
42 /* Note that value is evaluated twice! */
43 #define ZCBOR_ENCODE_FLAG(zse, label, value) \
44 (!(value) || \
45 (zcbor_tstr_put_lit(zse, label) && zcbor_bool_put(zse, (value))))
46 #endif
47
48 /* Flags returned by img_mgmt_state_read() for queried slot */
49 #define REPORT_SLOT_ACTIVE BIT(0)
50 #define REPORT_SLOT_PENDING BIT(1)
51 #define REPORT_SLOT_CONFIRMED BIT(2)
52 #define REPORT_SLOT_PERMANENT BIT(3)
53
54 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
55 #define DIRECT_XIP_BOOT_UNSET 0
56 #define DIRECT_XIP_BOOT_ONCE 1
57 #define DIRECT_XIP_BOOT_REVERT 2
58 #define DIRECT_XIP_BOOT_FOREVER 3
59 #endif
60
61 /**
62 * Collects information about the specified image slot.
63 */
64 #ifndef CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP
65 uint8_t
img_mgmt_state_flags(int query_slot)66 img_mgmt_state_flags(int query_slot)
67 {
68 uint8_t flags;
69 int swap_type;
70 int image = query_slot / 2; /* We support max 2 images for now */
71 int active_slot = img_mgmt_active_slot(image);
72
73 flags = 0;
74
75 /* Determine if this is pending or confirmed (only applicable for
76 * unified images and loaders.
77 */
78 swap_type = img_mgmt_swap_type(query_slot);
79 switch (swap_type) {
80 case IMG_MGMT_SWAP_TYPE_NONE:
81 if (query_slot == active_slot) {
82 flags |= IMG_MGMT_STATE_F_CONFIRMED;
83 }
84 break;
85
86 case IMG_MGMT_SWAP_TYPE_TEST:
87 if (query_slot == active_slot) {
88 flags |= IMG_MGMT_STATE_F_CONFIRMED;
89 } else {
90 flags |= IMG_MGMT_STATE_F_PENDING;
91 }
92 break;
93
94 case IMG_MGMT_SWAP_TYPE_PERM:
95 if (query_slot == active_slot) {
96 flags |= IMG_MGMT_STATE_F_CONFIRMED;
97 } else {
98 flags |= IMG_MGMT_STATE_F_PENDING | IMG_MGMT_STATE_F_PERMANENT;
99 }
100 break;
101
102 case IMG_MGMT_SWAP_TYPE_REVERT:
103 if (query_slot != active_slot) {
104 flags |= IMG_MGMT_STATE_F_CONFIRMED;
105 }
106 break;
107 }
108
109 /* Only running application is active */
110 if (image == img_mgmt_active_image() && query_slot == active_slot) {
111 flags |= IMG_MGMT_STATE_F_ACTIVE;
112 }
113
114 return flags;
115 }
116 #else
117 uint8_t
img_mgmt_state_flags(int query_slot)118 img_mgmt_state_flags(int query_slot)
119 {
120 uint8_t flags = 0;
121 int image = query_slot / 2; /* We support max 2 images for now */
122 int active_slot = img_mgmt_active_slot(image);
123
124 /* In case when MCUboot is configured for DirectXIP slot may only be
125 * active or pending. Slot is marked pending only when version in that slot
126 * is higher than version of active slot.
127 */
128 if (image == img_mgmt_active_image() && query_slot == active_slot) {
129 flags = IMG_MGMT_STATE_F_ACTIVE;
130 } else {
131 struct image_version sver;
132 struct image_version aver;
133 int rcs = img_mgmt_read_info(query_slot, &sver, NULL, NULL);
134 int rca = img_mgmt_read_info(active_slot, &aver, NULL, NULL);
135
136 if (rcs == 0 && rca == 0 && img_mgmt_vercmp(&aver, &sver) < 0) {
137 flags = IMG_MGMT_STATE_F_PENDING | IMG_MGMT_STATE_F_PERMANENT;
138 }
139 }
140
141 return flags;
142 }
143 #endif
144
145 #if !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \
146 !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
img_mgmt_get_next_boot_slot(int image,enum img_mgmt_next_boot_type * type)147 int img_mgmt_get_next_boot_slot(int image, enum img_mgmt_next_boot_type *type)
148 {
149 const int active_slot = img_mgmt_active_slot(image);
150 const int state = mcuboot_swap_type_multi(image);
151 /* All cases except BOOT_SWAP_TYPE_NONE return opposite slot */
152 int slot = img_mgmt_get_opposite_slot(active_slot);
153 enum img_mgmt_next_boot_type lt = NEXT_BOOT_TYPE_NORMAL;
154
155 switch (state) {
156 case BOOT_SWAP_TYPE_NONE:
157 /* Booting to the same slot, keeping type to NEXT_BOOT_TYPE_NORMAL */
158 slot = active_slot;
159 break;
160 case BOOT_SWAP_TYPE_PERM:
161 /* For BOOT_SWAP_TYPE_PERM reported type will be NEXT_BOOT_TYPE_NORMAL,
162 * and only difference between this and BOOT_SWAP_TYPE_NONE is that
163 * the later boots to the application in currently active slot while the former
164 * to the application in the opposite to active slot.
165 * Normal here means that it is ordinary boot and slot has not been marked
166 * for revert or pending for test, and will change on reset.
167 */
168 break;
169 case BOOT_SWAP_TYPE_REVERT:
170 /* Application is in test mode and has not yet been confirmed,
171 * which means that on the next boot the application will revert to
172 * the copy from reported slot.
173 */
174 lt = NEXT_BOOT_TYPE_REVERT;
175 break;
176 case BOOT_SWAP_TYPE_TEST:
177 /* Reported next boot slot is set for one boot only and app needs to
178 * confirm itself or it will be reverted.
179 */
180 lt = NEXT_BOOT_TYPE_TEST;
181 break;
182 default:
183 /* Should never, ever happen */
184 LOG_DBG("Unexpected swap state %d", state);
185 return -1;
186 }
187 LOG_DBG("(%d, *) => slot = %d, type = %d", image, slot, lt);
188
189 if (type != NULL) {
190 *type = lt;
191 }
192 return slot;
193 }
194 #else
195
196 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
197
read_directxip_state(int slot)198 static int read_directxip_state(int slot)
199 {
200 struct boot_swap_state bss;
201 int fa_id = img_mgmt_flash_area_id(slot);
202 const struct flash_area *fa;
203 int rc = 0;
204
205 __ASSERT(fa_id != -1, "Could not map slot to area ID");
206
207 rc = flash_area_open(fa_id, &fa);
208 if (rc < 0) {
209 return rc;
210 }
211 rc = boot_read_swap_state(fa, &bss);
212 flash_area_close(fa);
213 if (rc != 0) {
214 LOG_ERR("Failed to read state of slot %d with error %d", slot, rc);
215 return -1;
216 }
217
218 if (bss.magic == BOOT_MAGIC_GOOD) {
219 if (bss.image_ok == BOOT_FLAG_SET) {
220 return DIRECT_XIP_BOOT_FOREVER;
221 } else if (bss.copy_done == BOOT_FLAG_SET) {
222 return DIRECT_XIP_BOOT_REVERT;
223 }
224 return DIRECT_XIP_BOOT_ONCE;
225 }
226 return DIRECT_XIP_BOOT_UNSET;
227 }
228 #endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */
229
img_mgmt_get_next_boot_slot(int image,enum img_mgmt_next_boot_type * type)230 int img_mgmt_get_next_boot_slot(int image, enum img_mgmt_next_boot_type *type)
231 {
232 struct image_version aver;
233 struct image_version over;
234 int active_slot = img_mgmt_active_slot(image);
235 int other_slot = img_mgmt_get_opposite_slot(active_slot);
236 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
237 int active_slot_state;
238 int other_slot_state;
239 #endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */
240 enum img_mgmt_next_boot_type lt = NEXT_BOOT_TYPE_NORMAL;
241 int return_slot = active_slot;
242
243
244 int rcs = img_mgmt_read_info(other_slot, &over, NULL, NULL);
245 int rca = img_mgmt_read_info(active_slot, &aver, NULL, NULL);
246
247 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
248 active_slot_state = read_directxip_state(active_slot);
249 other_slot_state = read_directxip_state(other_slot);
250 if (rca != 0 ||
251 (rcs != 0 && rcs != IMG_MGMT_ERR_NO_IMAGE)) {
252 /* We do not really know what will happen, as we can not
253 * read states from bootloader.
254 */
255 LOG_ERR("img_mgmt_read_info_failed rca = %d, rcs = %d",
256 rca, rcs);
257 goto out;
258 }
259 if (other_slot_state < 0 || active_slot_state < 0) {
260 LOG_ERR("Slot state read failed with status: active %d, other %d",
261 active_slot_state, other_slot_state);
262 /* We do not really know what will happen, as we can not
263 * read states from bootloader.
264 */
265 goto out;
266 }
267
268 /* There is not other image, the active one will boot next time */
269 if (rcs == IMG_MGMT_ERR_NO_IMAGE) {
270 goto out;
271 }
272
273 if (active_slot_state == DIRECT_XIP_BOOT_REVERT) {
274 lt = NEXT_BOOT_TYPE_REVERT;
275 return_slot = other_slot;
276 } else if (other_slot_state == DIRECT_XIP_BOOT_UNSET) {
277 if (active_slot_state == DIRECT_XIP_BOOT_ONCE) {
278 lt = NEXT_BOOT_TYPE_TEST;
279 }
280 } else if (img_mgmt_vercmp(&aver, &over) < 0) {
281 if (other_slot_state == DIRECT_XIP_BOOT_FOREVER) {
282 return_slot = other_slot;
283 } else if (other_slot_state == DIRECT_XIP_BOOT_ONCE) {
284 lt = NEXT_BOOT_TYPE_TEST;
285 return_slot = other_slot;
286 }
287 }
288 #else
289 if (rcs == 0 && rca == 0 && img_mgmt_vercmp(&aver, &over) < 0) {
290 return_slot = other_slot;
291 }
292 #endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */
293
294 out:
295 if (type != NULL) {
296 *type = lt;
297 }
298
299 return return_slot;
300 }
301 #endif /* !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \
302 * !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
303 */
304
305
306 /**
307 * Indicates whether any image slot is pending (i.e., whether a test swap will
308 * happen on the next reboot.
309 */
310 int
img_mgmt_state_any_pending(void)311 img_mgmt_state_any_pending(void)
312 {
313 return img_mgmt_state_flags(0) & IMG_MGMT_STATE_F_PENDING ||
314 img_mgmt_state_flags(1) & IMG_MGMT_STATE_F_PENDING;
315 }
316
317 /**
318 * Indicates whether the specified slot has any flags. If no flags are set,
319 * the slot can be freely erased.
320 */
321 int
img_mgmt_slot_in_use(int slot)322 img_mgmt_slot_in_use(int slot)
323 {
324 int image = img_mgmt_slot_to_image(slot);
325 int active_slot = img_mgmt_active_slot(image);
326
327 #if !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \
328 !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
329 enum img_mgmt_next_boot_type type = NEXT_BOOT_TYPE_NORMAL;
330 int nbs = img_mgmt_get_next_boot_slot(image, &type);
331
332 if (slot == nbs && type == NEXT_BOOT_TYPE_REVERT) {
333 LOG_DBG("(%d) Refused erase revert", slot);
334 return 1;
335 }
336
337 if ((slot == nbs && type == NEXT_BOOT_TYPE_TEST) ||
338 (active_slot != nbs && type == NEXT_BOOT_TYPE_NORMAL)) {
339 #if defined(CONFIG_MCUMGR_GRP_IMG_ALLOW_ERASE_PENDING)
340 LOG_DBG("(%d) Allowed erase pending", slot);
341 /* Pass through to return (active_slot == slot) */
342 #else
343 LOG_DBG("(%d) Refused erase pending", slot);
344 return 1;
345 #endif
346 }
347 #endif
348
349 return (active_slot == slot);
350 }
351
352 /**
353 * Sets the pending flag for the specified image slot. That is, the system
354 * will swap to the specified image on the next reboot. If the permanent
355 * argument is specified, the system doesn't require a confirm after the swap
356 * occurs.
357 */
358 int
img_mgmt_state_set_pending(int slot,int permanent)359 img_mgmt_state_set_pending(int slot, int permanent)
360 {
361 uint8_t state_flags;
362 int rc;
363
364 state_flags = img_mgmt_state_flags(slot);
365
366 /* Unconfirmed slots are always runnable. A confirmed slot can only be
367 * run if it is a loader in a split image setup.
368 */
369 if (state_flags & IMG_MGMT_STATE_F_CONFIRMED && slot != 0) {
370 rc = IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
371 goto done;
372 }
373
374 rc = img_mgmt_write_pending(slot, permanent);
375
376 done:
377
378 return rc;
379 }
380
381 /**
382 * Confirms the current image state. Prevents a fallback from occurring on the
383 * next reboot if the active image is currently being tested.
384 */
385 int
img_mgmt_state_confirm(void)386 img_mgmt_state_confirm(void)
387 {
388 int rc;
389
390 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
391 int32_t err_rc;
392 uint16_t err_group;
393 #endif
394
395 /* Confirm disallowed if a test is pending. */
396 if (img_mgmt_state_any_pending()) {
397 rc = IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
398 goto err;
399 }
400
401 rc = img_mgmt_write_confirmed();
402
403 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
404 (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED, NULL, 0, &err_rc,
405 &err_group);
406 #endif
407
408 err:
409 return rc;
410 }
411
412 /* Return zcbor encoding result */
img_mgmt_state_encode_slot(struct smp_streamer * ctxt,uint32_t slot,int state_flags)413 static bool img_mgmt_state_encode_slot(struct smp_streamer *ctxt, uint32_t slot, int state_flags)
414 {
415 zcbor_state_t *zse = ctxt->writer->zs;
416 uint32_t flags;
417 char vers_str[IMG_MGMT_VER_MAX_STR_LEN];
418 uint8_t hash[IMAGE_HASH_LEN]; /* SHA256 hash */
419 struct zcbor_string zhash = { .value = hash, .len = IMAGE_HASH_LEN };
420 struct image_version ver;
421 bool ok;
422 int rc = img_mgmt_read_info(slot, &ver, hash, &flags);
423
424 #if defined(CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_HOOK)
425 int32_t err_rc;
426 uint16_t err_group;
427 struct img_mgmt_state_slot_encode slot_encode_data = {
428 .ok = &ok,
429 .zse = zse,
430 .slot = slot,
431 .version = vers_str,
432 .hash = hash,
433 .flags = flags,
434 };
435 #endif
436
437 if (rc != 0) {
438 /* zcbor encoding did not fail */
439 return true;
440 }
441
442 ok = zcbor_map_start_encode(zse, CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_STATES) &&
443 (CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER == 1 ||
444 (zcbor_tstr_put_lit(zse, "image") &&
445 zcbor_uint32_put(zse, slot >> 1))) &&
446 zcbor_tstr_put_lit(zse, "slot") &&
447 zcbor_uint32_put(zse, slot % 2) &&
448 zcbor_tstr_put_lit(zse, "version");
449
450 if (ok) {
451 if (img_mgmt_ver_str(&ver, vers_str) < 0) {
452 ok = zcbor_tstr_put_lit(zse, "<\?\?\?>");
453 } else {
454 vers_str[sizeof(vers_str) - 1] = '\0';
455 ok = zcbor_tstr_put_term(zse, vers_str, sizeof(vers_str));
456 }
457 }
458
459 ok = ok && zcbor_tstr_put_lit(zse, "hash") &&
460 zcbor_bstr_encode(zse, &zhash) &&
461 ZCBOR_ENCODE_FLAG(zse, "bootable", !(flags & IMAGE_F_NON_BOOTABLE)) &&
462 ZCBOR_ENCODE_FLAG(zse, "pending", state_flags & REPORT_SLOT_PENDING) &&
463 ZCBOR_ENCODE_FLAG(zse, "confirmed", state_flags & REPORT_SLOT_CONFIRMED) &&
464 ZCBOR_ENCODE_FLAG(zse, "active", state_flags & REPORT_SLOT_ACTIVE) &&
465 ZCBOR_ENCODE_FLAG(zse, "permanent", state_flags & REPORT_SLOT_PERMANENT);
466
467 if (!ok) {
468 goto failed;
469 }
470
471 #if defined(CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_HOOK)
472 /* Send notification to application to optionally append more fields */
473 (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_IMAGE_SLOT_STATE, &slot_encode_data,
474 sizeof(slot_encode_data), &err_rc, &err_group);
475 #endif
476
477 ok &= zcbor_map_end_encode(zse, CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_STATES);
478
479 failed:
480 return ok;
481 }
482
483 /**
484 * Command handler: image state read
485 */
486 int
img_mgmt_state_read(struct smp_streamer * ctxt)487 img_mgmt_state_read(struct smp_streamer *ctxt)
488 {
489 zcbor_state_t *zse = ctxt->writer->zs;
490 uint32_t i;
491 bool ok;
492
493 ok = zcbor_tstr_put_lit(zse, "images") &&
494 zcbor_list_start_encode(zse, 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER);
495
496 img_mgmt_take_lock();
497
498 for (i = 0; ok && i < CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER; i++) {
499 /* _a is active slot, _o is opposite slot */
500 enum img_mgmt_next_boot_type type = NEXT_BOOT_TYPE_NORMAL;
501 int next_boot_slot = img_mgmt_get_next_boot_slot(i, &type);
502 int slot_a = img_mgmt_active_slot(i);
503 int slot_o = img_mgmt_get_opposite_slot(slot_a);
504 int flags_a = REPORT_SLOT_ACTIVE;
505 int flags_o = 0;
506
507 if (type != NEXT_BOOT_TYPE_REVERT) {
508 flags_a |= REPORT_SLOT_CONFIRMED;
509 }
510
511 if (next_boot_slot != slot_a) {
512 if (type == NEXT_BOOT_TYPE_NORMAL) {
513 flags_o = REPORT_SLOT_PENDING | REPORT_SLOT_PERMANENT;
514 } else if (type == NEXT_BOOT_TYPE_REVERT) {
515 flags_o = REPORT_SLOT_CONFIRMED;
516 } else if (type == NEXT_BOOT_TYPE_TEST) {
517 flags_o = REPORT_SLOT_PENDING;
518 }
519 }
520
521 /* Need to report slots in proper order */
522 if (slot_a < slot_o) {
523 ok = img_mgmt_state_encode_slot(ctxt, slot_a, flags_a) &&
524 img_mgmt_state_encode_slot(ctxt, slot_o, flags_o);
525 } else {
526 ok = img_mgmt_state_encode_slot(ctxt, slot_o, flags_o) &&
527 img_mgmt_state_encode_slot(ctxt, slot_a, flags_a);
528 }
529 }
530
531 /* Ending list encoding for two slots per image */
532 ok = ok && zcbor_list_end_encode(zse, 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER);
533 /* splitStatus is always 0 so in frugal list it is not present at all */
534 if (!IS_ENABLED(CONFIG_MCUMGR_GRP_IMG_FRUGAL_LIST) && ok) {
535 ok = zcbor_tstr_put_lit(zse, "splitStatus") &&
536 zcbor_int32_put(zse, 0);
537 }
538
539 img_mgmt_release_lock();
540
541 return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
542 }
543
img_mgmt_set_next_boot_slot_common(int slot,int active_slot,bool confirm)544 static int img_mgmt_set_next_boot_slot_common(int slot, int active_slot, bool confirm)
545 {
546 const struct flash_area *fa;
547 int area_id = img_mgmt_flash_area_id(slot);
548 int rc = 0;
549
550 if (flash_area_open(area_id, &fa) != 0) {
551 return IMG_MGMT_ERR_FLASH_OPEN_FAILED;
552 }
553
554 rc = boot_set_next(fa, slot == active_slot, confirm);
555 if (rc != 0) {
556 /* Failed to set next slot for boot as desired */
557 LOG_ERR("Faled boot_set_next with code %d, for slot %d,"
558 " with active slot %d and confirm %d",
559 rc, slot, active_slot, confirm);
560
561 /* Translate from boot util error code to IMG mgmt group error code */
562 if (rc == BOOT_EFLASH) {
563 rc = IMG_MGMT_ERR_FLASH_WRITE_FAILED;
564 } else if (rc == BOOT_EBADVECT) {
565 rc = IMG_MGMT_ERR_INVALID_IMAGE_VECTOR_TABLE;
566 } else if (rc == BOOT_EBADIMAGE) {
567 rc = IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC;
568 } else {
569 rc = IMG_MGMT_ERR_UNKNOWN;
570 }
571 }
572 flash_area_close(fa);
573
574 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
575 if (rc == 0 && slot == active_slot && confirm) {
576 int32_t err_rc;
577 uint16_t err_group;
578
579 /* Confirm event is only sent for active slot */
580 (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED, NULL, 0, &err_rc,
581 &err_group);
582 }
583 #endif
584
585 return rc;
586 }
587
588 #ifndef CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT
img_mgmt_set_next_boot_slot(int slot,bool confirm)589 int img_mgmt_set_next_boot_slot(int slot, bool confirm)
590 {
591 /* image the requested slot is defined within */
592 int image = img_mgmt_slot_to_image(slot);
593 /* active_slot is slot that is considered active/primary/executing
594 * for a given image.
595 */
596 int active_slot = img_mgmt_active_slot(image);
597 enum img_mgmt_next_boot_type type = NEXT_BOOT_TYPE_NORMAL;
598 int next_boot_slot = img_mgmt_get_next_boot_slot(image, &type);
599
600 LOG_DBG("(%d, %s)", slot, confirm ? "confirm" : "test");
601 LOG_DBG("aimg = %d, img = %d, aslot = %d, slot = %d, nbs = %d",
602 img_mgmt_active_image(), image, active_slot, slot, next_boot_slot);
603
604 /* MCUmgr should not allow to confirm non-active image slots to prevent
605 * confirming something that might not have been verified to actually be bootable
606 * or have stuck in primary slot of other image. Unfortunately there was
607 * a bug in logic that always allowed to confirm secondary slot of any
608 * image. Now the behaviour is controlled via Kconfig options.
609 */
610 #ifndef CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_ANY
611 if (confirm && image != img_mgmt_active_image() &&
612 (!IS_ENABLED(CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_SECONDARY) ||
613 slot == active_slot)) {
614 LOG_DBG("Not allowed to confirm non-active images");
615 return IMG_MGMT_ERR_IMAGE_CONFIRMATION_DENIED;
616 }
617 #endif
618
619 /* Setting test to active slot is not allowed. */
620 if (!confirm && slot == active_slot) {
621 return IMG_MGMT_ERR_IMAGE_SETTING_TEST_TO_ACTIVE_DENIED;
622 }
623
624 if (type == NEXT_BOOT_TYPE_TEST) {
625 /* Do nothing when requested to test the slot already set for test. */
626 if (!confirm && slot == next_boot_slot) {
627 return 0;
628 }
629 /* Changing to other, for test or not, is not allowed/ */
630 return IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
631 }
632
633 /* Normal boot means confirmed boot to either active slot or the opposite slot. */
634 if (type == NEXT_BOOT_TYPE_NORMAL) {
635 /* Do nothing when attempting to confirm slot that will be boot next time. */
636 if (confirm && slot == next_boot_slot) {
637 return 0;
638 }
639
640 /* Can not change slot once other than running has been confirmed. */
641 if ((slot == active_slot && active_slot != next_boot_slot) ||
642 (!confirm && slot != active_slot && slot == next_boot_slot)) {
643 return IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
644 }
645 /* Allow selecting non-active slot for boot */
646 }
647
648 if (type == NEXT_BOOT_TYPE_REVERT) {
649 /* Nothing to do when requested to confirm the next boot slot,
650 * as it is already confirmed in this mode.
651 */
652 if (confirm && slot == next_boot_slot) {
653 return 0;
654 }
655 /* Trying to set any slot for test is an error */
656 if (!confirm) {
657 return IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
658 }
659 /* Allow confirming slot == active_slot */
660 }
661
662 return img_mgmt_set_next_boot_slot_common(slot, active_slot, confirm);
663 }
664 #else
img_mgmt_set_next_boot_slot(int slot,bool confirm)665 int img_mgmt_set_next_boot_slot(int slot, bool confirm)
666 {
667 int active_image = img_mgmt_active_image();
668 int active_slot = img_mgmt_active_slot(active_image);
669
670 LOG_DBG("(%d, %s)", slot, confirm ? "confirm" : "test");
671 LOG_DBG("aimg = %d, aslot = %d, slot = %d",
672 active_image, active_slot, slot);
673
674 if (slot == active_slot && !confirm) {
675 return IMG_MGMT_ERR_IMAGE_SETTING_TEST_TO_ACTIVE_DENIED;
676 }
677
678 return img_mgmt_set_next_boot_slot_common(slot, active_slot, confirm);
679 }
680 #endif
681
682 /**
683 * Command handler: image state write
684 */
685 int
img_mgmt_state_write(struct smp_streamer * ctxt)686 img_mgmt_state_write(struct smp_streamer *ctxt)
687 {
688 bool confirm = false;
689 int slot;
690 int rc;
691 size_t decoded = 0;
692 bool ok;
693 struct zcbor_string zhash = { 0 };
694
695 struct zcbor_map_decode_key_val image_list_decode[] = {
696 ZCBOR_MAP_DECODE_KEY_DECODER("hash", zcbor_bstr_decode, &zhash),
697 ZCBOR_MAP_DECODE_KEY_DECODER("confirm", zcbor_bool_decode, &confirm)
698 };
699
700 zcbor_state_t *zse = ctxt->writer->zs;
701 zcbor_state_t *zsd = ctxt->reader->zs;
702
703 ok = zcbor_map_decode_bulk(zsd, image_list_decode,
704 ARRAY_SIZE(image_list_decode), &decoded) == 0;
705
706 if (!ok) {
707 return MGMT_ERR_EINVAL;
708 }
709
710 img_mgmt_take_lock();
711
712 /* Determine which slot is being operated on. */
713 if (zhash.len == 0) {
714 if (confirm) {
715 slot = img_mgmt_active_slot(img_mgmt_active_image());
716 } else {
717 /* A 'test' without a hash is invalid. */
718 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE,
719 IMG_MGMT_ERR_INVALID_HASH);
720 goto end;
721 }
722 } else if (zhash.len != IMAGE_HASH_LEN) {
723 /* The img_mgmt_find_by_hash does exact length compare
724 * so just fail here.
725 */
726 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, IMG_MGMT_ERR_INVALID_HASH);
727 goto end;
728 } else {
729 uint8_t hash[IMAGE_HASH_LEN];
730
731 memcpy(hash, zhash.value, zhash.len);
732
733 slot = img_mgmt_find_by_hash(hash, NULL);
734 if (slot < 0) {
735 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE,
736 IMG_MGMT_ERR_HASH_NOT_FOUND);
737 goto end;
738 }
739 }
740
741 rc = img_mgmt_set_next_boot_slot(slot, confirm);
742 if (rc != 0) {
743 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
744 goto end;
745 }
746
747 /* Send the current image state in the response. */
748 rc = img_mgmt_state_read(ctxt);
749 if (rc != 0) {
750 img_mgmt_release_lock();
751 return rc;
752 }
753
754 end:
755 img_mgmt_release_lock();
756
757 if (!ok) {
758 return MGMT_ERR_EMSGSIZE;
759 }
760
761 return MGMT_ERR_EOK;
762 }
763