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