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