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
289 out:
290 #else
291 if (rcs == 0 && rca == 0 && img_mgmt_vercmp(&aver, &over) < 0) {
292 return_slot = other_slot;
293 }
294 #endif /* defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT) */
295
296 if (type != NULL) {
297 *type = lt;
298 }
299
300 return return_slot;
301 }
302 #endif /* !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \
303 * !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
304 */
305
306
307 /**
308 * Indicates whether any image slot is pending (i.e., whether a test swap will
309 * happen on the next reboot.
310 */
311 int
img_mgmt_state_any_pending(void)312 img_mgmt_state_any_pending(void)
313 {
314 return img_mgmt_state_flags(0) & IMG_MGMT_STATE_F_PENDING ||
315 img_mgmt_state_flags(1) & IMG_MGMT_STATE_F_PENDING;
316 }
317
318 /**
319 * Indicates whether the specified slot has any flags. If no flags are set,
320 * the slot can be freely erased.
321 */
322 int
img_mgmt_slot_in_use(int slot)323 img_mgmt_slot_in_use(int slot)
324 {
325 int image = img_mgmt_slot_to_image(slot);
326 int active_slot = img_mgmt_active_slot(image);
327
328 #if !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) && \
329 !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
330 enum img_mgmt_next_boot_type type = NEXT_BOOT_TYPE_NORMAL;
331 int nbs = img_mgmt_get_next_boot_slot(image, &type);
332
333 if (slot == nbs && type == NEXT_BOOT_TYPE_REVERT) {
334 LOG_DBG("(%d) Refused erase revert", slot);
335 return 1;
336 }
337
338 if ((slot == nbs && type == NEXT_BOOT_TYPE_TEST) ||
339 (active_slot != nbs && type == NEXT_BOOT_TYPE_NORMAL)) {
340 #if defined(CONFIG_MCUMGR_GRP_IMG_ALLOW_ERASE_PENDING)
341 LOG_DBG("(%d) Allowed erase pending", slot);
342 /* Pass through to return (active_slot == slot) */
343 #else
344 LOG_DBG("(%d) Refused erase pending", slot);
345 return 1;
346 #endif
347 }
348 #endif
349
350 return (active_slot == slot);
351 }
352
353 /**
354 * Sets the pending flag for the specified image slot. That is, the system
355 * will swap to the specified image on the next reboot. If the permanent
356 * argument is specified, the system doesn't require a confirm after the swap
357 * occurs.
358 */
359 int
img_mgmt_state_set_pending(int slot,int permanent)360 img_mgmt_state_set_pending(int slot, int permanent)
361 {
362 uint8_t state_flags;
363 int rc;
364
365 state_flags = img_mgmt_state_flags(slot);
366
367 /* Unconfirmed slots are always runnable. A confirmed slot can only be
368 * run if it is a loader in a split image setup.
369 */
370 if (state_flags & IMG_MGMT_STATE_F_CONFIRMED && slot != 0) {
371 rc = IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
372 goto done;
373 }
374
375 rc = img_mgmt_write_pending(slot, permanent);
376
377 done:
378
379 return rc;
380 }
381
382 /**
383 * Confirms the current image state. Prevents a fallback from occurring on the
384 * next reboot if the active image is currently being tested.
385 */
386 int
img_mgmt_state_confirm(void)387 img_mgmt_state_confirm(void)
388 {
389 int rc;
390
391 /* Confirm disallowed if a test is pending. */
392 if (img_mgmt_state_any_pending()) {
393 rc = IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
394 goto err;
395 }
396
397 rc = img_mgmt_write_confirmed();
398
399 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
400 if (!rc) {
401 int32_t err_rc;
402 uint16_t err_group;
403 struct img_mgmt_image_confirmed confirmed_data = {
404 .image = 0
405 };
406
407 (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED, &confirmed_data,
408 sizeof(confirmed_data), &err_rc, &err_group);
409 }
410 #endif
411
412 err:
413 return rc;
414 }
415
416 /* Return zcbor encoding result */
img_mgmt_state_encode_slot(struct smp_streamer * ctxt,uint32_t slot,int state_flags)417 static bool img_mgmt_state_encode_slot(struct smp_streamer *ctxt, uint32_t slot, int state_flags)
418 {
419 zcbor_state_t *zse = ctxt->writer->zs;
420 uint32_t flags;
421 char vers_str[IMG_MGMT_VER_MAX_STR_LEN];
422 uint8_t hash[IMAGE_HASH_LEN]; /* SHA256 hash */
423 struct zcbor_string zhash = { .value = hash, .len = IMAGE_HASH_LEN };
424 struct image_version ver;
425 bool ok;
426 int rc = img_mgmt_read_info(slot, &ver, hash, &flags);
427
428 #if defined(CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_HOOK)
429 int32_t err_rc;
430 uint16_t err_group;
431 struct img_mgmt_state_slot_encode slot_encode_data = {
432 .ok = &ok,
433 .zse = zse,
434 .slot = slot,
435 .version = vers_str,
436 .hash = hash,
437 .flags = flags,
438 };
439 #endif
440
441 if (rc != 0) {
442 /* zcbor encoding did not fail */
443 return true;
444 }
445
446 ok = zcbor_map_start_encode(zse, CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_STATES) &&
447 (CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER == 1 ||
448 (zcbor_tstr_put_lit(zse, "image") &&
449 zcbor_uint32_put(zse, slot >> 1))) &&
450 zcbor_tstr_put_lit(zse, "slot") &&
451 zcbor_uint32_put(zse, slot % 2) &&
452 zcbor_tstr_put_lit(zse, "version");
453
454 if (ok) {
455 if (img_mgmt_ver_str(&ver, vers_str) < 0) {
456 ok = zcbor_tstr_put_lit(zse, "<\?\?\?>");
457 } else {
458 vers_str[sizeof(vers_str) - 1] = '\0';
459 ok = zcbor_tstr_put_term(zse, vers_str, sizeof(vers_str));
460 }
461 }
462
463 ok = ok && zcbor_tstr_put_lit(zse, "hash") &&
464 zcbor_bstr_encode(zse, &zhash) &&
465 ZCBOR_ENCODE_FLAG(zse, "bootable", !(flags & IMAGE_F_NON_BOOTABLE)) &&
466 ZCBOR_ENCODE_FLAG(zse, "pending", state_flags & REPORT_SLOT_PENDING) &&
467 ZCBOR_ENCODE_FLAG(zse, "confirmed", state_flags & REPORT_SLOT_CONFIRMED) &&
468 ZCBOR_ENCODE_FLAG(zse, "active", state_flags & REPORT_SLOT_ACTIVE) &&
469 ZCBOR_ENCODE_FLAG(zse, "permanent", state_flags & REPORT_SLOT_PERMANENT);
470
471 if (!ok) {
472 goto failed;
473 }
474
475 #if defined(CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_HOOK)
476 /* Send notification to application to optionally append more fields */
477 (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_IMAGE_SLOT_STATE, &slot_encode_data,
478 sizeof(slot_encode_data), &err_rc, &err_group);
479 #endif
480
481 ok &= zcbor_map_end_encode(zse, CONFIG_MCUMGR_GRP_IMG_IMAGE_SLOT_STATE_STATES);
482
483 failed:
484 return ok;
485 }
486
487 /**
488 * Command handler: image state read
489 */
490 int
img_mgmt_state_read(struct smp_streamer * ctxt)491 img_mgmt_state_read(struct smp_streamer *ctxt)
492 {
493 zcbor_state_t *zse = ctxt->writer->zs;
494 uint32_t i;
495 bool ok;
496
497 ok = zcbor_tstr_put_lit(zse, "images") &&
498 zcbor_list_start_encode(zse, 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER);
499
500 img_mgmt_take_lock();
501
502 for (i = 0; ok && i < CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER; i++) {
503 /* _a is active slot, _o is opposite slot */
504 enum img_mgmt_next_boot_type type = NEXT_BOOT_TYPE_NORMAL;
505 int next_boot_slot = img_mgmt_get_next_boot_slot(i, &type);
506 int slot_a = img_mgmt_active_slot(i);
507 int slot_o = img_mgmt_get_opposite_slot(slot_a);
508 int flags_a = REPORT_SLOT_ACTIVE;
509 int flags_o = 0;
510
511 if (type != NEXT_BOOT_TYPE_REVERT) {
512 flags_a |= REPORT_SLOT_CONFIRMED;
513 }
514
515 if (next_boot_slot != slot_a) {
516 if (type == NEXT_BOOT_TYPE_NORMAL) {
517 flags_o = REPORT_SLOT_PENDING | REPORT_SLOT_PERMANENT;
518 } else if (type == NEXT_BOOT_TYPE_REVERT) {
519 flags_o = REPORT_SLOT_CONFIRMED;
520 } else if (type == NEXT_BOOT_TYPE_TEST) {
521 flags_o = REPORT_SLOT_PENDING;
522 }
523 }
524
525 /* Need to report slots in proper order */
526 if (slot_a < slot_o) {
527 ok = img_mgmt_state_encode_slot(ctxt, slot_a, flags_a) &&
528 img_mgmt_state_encode_slot(ctxt, slot_o, flags_o);
529 } else {
530 ok = img_mgmt_state_encode_slot(ctxt, slot_o, flags_o) &&
531 img_mgmt_state_encode_slot(ctxt, slot_a, flags_a);
532 }
533 }
534
535 /* Ending list encoding for two slots per image */
536 ok = ok && zcbor_list_end_encode(zse, 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER);
537 /* splitStatus is always 0 so in frugal list it is not present at all */
538 if (!IS_ENABLED(CONFIG_MCUMGR_GRP_IMG_FRUGAL_LIST) && ok) {
539 ok = zcbor_tstr_put_lit(zse, "splitStatus") &&
540 zcbor_int32_put(zse, 0);
541 }
542
543 img_mgmt_release_lock();
544
545 return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
546 }
547
img_mgmt_set_next_boot_slot_common(int slot,int active_slot,bool confirm)548 static int img_mgmt_set_next_boot_slot_common(int slot, int active_slot, bool confirm)
549 {
550 const struct flash_area *fa;
551 int area_id = img_mgmt_flash_area_id(slot);
552 int rc = 0;
553
554 if (flash_area_open(area_id, &fa) != 0) {
555 return IMG_MGMT_ERR_FLASH_OPEN_FAILED;
556 }
557
558 rc = boot_set_next(fa, slot == active_slot, confirm);
559 if (rc != 0) {
560 /* Failed to set next slot for boot as desired */
561 LOG_ERR("Faled boot_set_next with code %d, for slot %d,"
562 " with active slot %d and confirm %d",
563 rc, slot, active_slot, confirm);
564
565 /* Translate from boot util error code to IMG mgmt group error code */
566 if (rc == BOOT_EFLASH) {
567 rc = IMG_MGMT_ERR_FLASH_WRITE_FAILED;
568 } else if (rc == BOOT_EBADVECT) {
569 rc = IMG_MGMT_ERR_INVALID_IMAGE_VECTOR_TABLE;
570 } else if (rc == BOOT_EBADIMAGE) {
571 rc = IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC;
572 } else {
573 rc = IMG_MGMT_ERR_UNKNOWN;
574 }
575 }
576 flash_area_close(fa);
577
578 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
579 if (rc == 0 && slot == active_slot && confirm) {
580 /* Confirm event is only sent for active slot */
581 int32_t err_rc;
582 uint16_t err_group;
583 struct img_mgmt_image_confirmed confirmed_data = {
584 .image = img_mgmt_slot_to_image(slot)
585 };
586
587 (void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED, &confirmed_data,
588 sizeof(confirmed_data), &err_rc, &err_group);
589 }
590 #endif
591
592 return rc;
593 }
594
595 #ifndef CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT
img_mgmt_set_next_boot_slot(int slot,bool confirm)596 int img_mgmt_set_next_boot_slot(int slot, bool confirm)
597 {
598 /* image the requested slot is defined within */
599 int image = img_mgmt_slot_to_image(slot);
600 /* active_slot is slot that is considered active/primary/executing
601 * for a given image.
602 */
603 int active_slot = img_mgmt_active_slot(image);
604 enum img_mgmt_next_boot_type type = NEXT_BOOT_TYPE_NORMAL;
605 int next_boot_slot = img_mgmt_get_next_boot_slot(image, &type);
606
607 LOG_DBG("(%d, %s)", slot, confirm ? "confirm" : "test");
608 LOG_DBG("aimg = %d, img = %d, aslot = %d, slot = %d, nbs = %d",
609 img_mgmt_active_image(), image, active_slot, slot, next_boot_slot);
610
611 /* MCUmgr should not allow to confirm non-active image slots to prevent
612 * confirming something that might not have been verified to actually be bootable
613 * or have stuck in primary slot of other image. Unfortunately there was
614 * a bug in logic that always allowed to confirm secondary slot of any
615 * image. Now the behaviour is controlled via Kconfig options.
616 */
617 #ifndef CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_ANY
618 if (confirm && image != img_mgmt_active_image() &&
619 (!IS_ENABLED(CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_SECONDARY) ||
620 slot == active_slot)) {
621 LOG_DBG("Not allowed to confirm non-active images");
622 return IMG_MGMT_ERR_IMAGE_CONFIRMATION_DENIED;
623 }
624 #endif
625
626 /* Setting test to active slot is not allowed. */
627 if (!confirm && slot == active_slot) {
628 return IMG_MGMT_ERR_IMAGE_SETTING_TEST_TO_ACTIVE_DENIED;
629 }
630
631 if (type == NEXT_BOOT_TYPE_TEST) {
632 /* Do nothing when requested to test the slot already set for test. */
633 if (!confirm && slot == next_boot_slot) {
634 return 0;
635 }
636 /* Changing to other, for test or not, is not allowed/ */
637 return IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
638 }
639
640 /* Normal boot means confirmed boot to either active slot or the opposite slot. */
641 if (type == NEXT_BOOT_TYPE_NORMAL) {
642 /* Do nothing when attempting to confirm slot that will be boot next time. */
643 if (confirm && slot == next_boot_slot) {
644 return 0;
645 }
646
647 /* Can not change slot once other than running has been confirmed. */
648 if ((slot == active_slot && active_slot != next_boot_slot) ||
649 (!confirm && slot != active_slot && slot == next_boot_slot)) {
650 return IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
651 }
652 /* Allow selecting non-active slot for boot */
653 }
654
655 if (type == NEXT_BOOT_TYPE_REVERT) {
656 /* Nothing to do when requested to confirm the next boot slot,
657 * as it is already confirmed in this mode.
658 */
659 if (confirm && slot == next_boot_slot) {
660 return 0;
661 }
662 /* Trying to set any slot for test is an error */
663 if (!confirm) {
664 return IMG_MGMT_ERR_IMAGE_ALREADY_PENDING;
665 }
666 /* Allow confirming slot == active_slot */
667 }
668
669 return img_mgmt_set_next_boot_slot_common(slot, active_slot, confirm);
670 }
671 #else
img_mgmt_set_next_boot_slot(int slot,bool confirm)672 int img_mgmt_set_next_boot_slot(int slot, bool confirm)
673 {
674 int active_image = img_mgmt_active_image();
675 int active_slot = img_mgmt_active_slot(active_image);
676
677 LOG_DBG("(%d, %s)", slot, confirm ? "confirm" : "test");
678 LOG_DBG("aimg = %d, aslot = %d, slot = %d",
679 active_image, active_slot, slot);
680
681 if (slot == active_slot && !confirm) {
682 return IMG_MGMT_ERR_IMAGE_SETTING_TEST_TO_ACTIVE_DENIED;
683 }
684
685 return img_mgmt_set_next_boot_slot_common(slot, active_slot, confirm);
686 }
687 #endif
688
689 /**
690 * Command handler: image state write
691 */
692 int
img_mgmt_state_write(struct smp_streamer * ctxt)693 img_mgmt_state_write(struct smp_streamer *ctxt)
694 {
695 bool confirm = false;
696 int slot;
697 int rc;
698 size_t decoded = 0;
699 bool ok;
700 struct zcbor_string zhash = { 0 };
701
702 struct zcbor_map_decode_key_val image_list_decode[] = {
703 ZCBOR_MAP_DECODE_KEY_DECODER("hash", zcbor_bstr_decode, &zhash),
704 ZCBOR_MAP_DECODE_KEY_DECODER("confirm", zcbor_bool_decode, &confirm)
705 };
706
707 zcbor_state_t *zse = ctxt->writer->zs;
708 zcbor_state_t *zsd = ctxt->reader->zs;
709
710 ok = zcbor_map_decode_bulk(zsd, image_list_decode,
711 ARRAY_SIZE(image_list_decode), &decoded) == 0;
712
713 if (!ok) {
714 return MGMT_ERR_EINVAL;
715 }
716
717 img_mgmt_take_lock();
718
719 /* Determine which slot is being operated on. */
720 if (zhash.len == 0) {
721 if (confirm) {
722 slot = img_mgmt_active_slot(img_mgmt_active_image());
723 } else {
724 /* A 'test' without a hash is invalid. */
725 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE,
726 IMG_MGMT_ERR_INVALID_HASH);
727 goto end;
728 }
729 } else if (zhash.len != IMAGE_HASH_LEN) {
730 /* The img_mgmt_find_by_hash does exact length compare
731 * so just fail here.
732 */
733 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, IMG_MGMT_ERR_INVALID_HASH);
734 goto end;
735 } else {
736 uint8_t hash[IMAGE_HASH_LEN];
737
738 memcpy(hash, zhash.value, zhash.len);
739
740 slot = img_mgmt_find_by_hash(hash, NULL);
741 if (slot < 0) {
742 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE,
743 IMG_MGMT_ERR_HASH_NOT_FOUND);
744 goto end;
745 }
746 }
747
748 rc = img_mgmt_set_next_boot_slot(slot, confirm);
749 if (rc != 0) {
750 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
751 goto end;
752 }
753
754 /* Send the current image state in the response. */
755 rc = img_mgmt_state_read(ctxt);
756 if (rc != 0) {
757 img_mgmt_release_lock();
758 return rc;
759 }
760
761 end:
762 img_mgmt_release_lock();
763
764 if (!ok) {
765 return MGMT_ERR_EMSGSIZE;
766 }
767
768 return MGMT_ERR_EOK;
769 }
770