1 /*
2  * Copyright (c) 2018-2021 mcumgr authors
3  * Copyright (c) 2022-2024 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/sys/util.h>
9 #include <limits.h>
10 #include <assert.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <zephyr/toolchain.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/storage/flash_map.h>
16 
17 #include <zcbor_common.h>
18 #include <zcbor_decode.h>
19 #include <zcbor_encode.h>
20 
21 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
22 #include <zephyr/mgmt/mcumgr/smp/smp.h>
23 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
24 #include <zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h>
25 
26 #include <mgmt/mcumgr/util/zcbor_bulk.h>
27 #include <mgmt/mcumgr/grp/img_mgmt/img_mgmt_priv.h>
28 
29 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
30 #include <zephyr/dfu/flash_img.h>
31 #endif
32 
33 #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
34 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
35 #include <mgmt/mcumgr/transport/smp_internal.h>
36 #endif
37 
38 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
39 #include <bootutil/boot_status.h>
40 #include <zephyr/retention/blinfo.h>
41 #endif
42 
43 #if !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
44 
45 #ifndef CONFIG_FLASH_LOAD_OFFSET
46 #error MCUmgr requires application to be built with CONFIG_FLASH_LOAD_OFFSET set \
47 	to be able to figure out application running slot.
48 #endif
49 
50 #define FIXED_PARTITION_IS_RUNNING_APP_PARTITION(label)	\
51 	 (FIXED_PARTITION_OFFSET(label) == CONFIG_FLASH_LOAD_OFFSET)
52 
53 BUILD_ASSERT(sizeof(struct image_header) == IMAGE_HEADER_SIZE,
54 	     "struct image_header not required size");
55 
56 #if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER >= 2
57 #if FIXED_PARTITION_EXISTS(slot0_ns_partition) &&			\
58 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_ns_partition)
59 #define ACTIVE_IMAGE_IS 0
60 #elif FIXED_PARTITION_EXISTS(slot0_partition) &&			\
61 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_partition)
62 #define ACTIVE_IMAGE_IS 0
63 #elif FIXED_PARTITION_EXISTS(slot1_partition) &&			\
64 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot1_partition)
65 #define ACTIVE_IMAGE_IS 0
66 #elif FIXED_PARTITION_EXISTS(slot2_partition) &&			\
67 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot2_partition)
68 #define ACTIVE_IMAGE_IS 1
69 #elif FIXED_PARTITION_EXISTS(slot3_partition) &&			\
70 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot3_partition)
71 #define ACTIVE_IMAGE_IS 1
72 #elif FIXED_PARTITION_EXISTS(slot4_partition) &&			\
73 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot4_partition)
74 #define ACTIVE_IMAGE_IS 2
75 #elif FIXED_PARTITION_EXISTS(slot5_partition) &&			\
76 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot5_partition)
77 #define ACTIVE_IMAGE_IS 2
78 #else
79 #define ACTIVE_IMAGE_IS 0
80 #endif
81 #else
82 #define ACTIVE_IMAGE_IS 0
83 #endif
84 
85 #else
86 #define ACTIVE_IMAGE_IS 0
87 #endif
88 
89 #define SLOTS_PER_IMAGE 2
90 
91 LOG_MODULE_REGISTER(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL);
92 
93 struct img_mgmt_state g_img_mgmt_state;
94 
95 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
96 static K_MUTEX_DEFINE(img_mgmt_mutex);
97 #endif
98 
99 #ifdef CONFIG_MCUMGR_GRP_IMG_VERBOSE_ERR
100 const char *img_mgmt_err_str_app_reject = "app reject";
101 const char *img_mgmt_err_str_hdr_malformed = "header malformed";
102 const char *img_mgmt_err_str_magic_mismatch = "magic mismatch";
103 const char *img_mgmt_err_str_no_slot = "no slot";
104 const char *img_mgmt_err_str_flash_open_failed = "fa open fail";
105 const char *img_mgmt_err_str_flash_erase_failed = "fa erase fail";
106 const char *img_mgmt_err_str_flash_write_failed = "fa write fail";
107 const char *img_mgmt_err_str_downgrade = "downgrade";
108 const char *img_mgmt_err_str_image_bad_flash_addr = "img addr mismatch";
109 const char *img_mgmt_err_str_image_too_large = "img too large";
110 const char *img_mgmt_err_str_data_overrun = "data overrun";
111 #endif
112 
img_mgmt_take_lock(void)113 void img_mgmt_take_lock(void)
114 {
115 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
116 	k_mutex_lock(&img_mgmt_mutex, K_FOREVER);
117 #endif
118 }
119 
img_mgmt_release_lock(void)120 void img_mgmt_release_lock(void)
121 {
122 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
123 	k_mutex_unlock(&img_mgmt_mutex);
124 #endif
125 }
126 
127 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
img_mgmt_reset_zse(struct smp_streamer * ctxt)128 static bool img_mgmt_reset_zse(struct smp_streamer *ctxt)
129 {
130 	zcbor_state_t *zse = ctxt->writer->zs;
131 
132 	/* Because there is already data in the buffer, it must be cleared first */
133 	net_buf_reset(ctxt->writer->nb);
134 	ctxt->writer->nb->len = sizeof(struct smp_hdr);
135 	zcbor_new_encode_state(zse, ARRAY_SIZE(ctxt->writer->zs),
136 			       ctxt->writer->nb->data + sizeof(struct smp_hdr),
137 			       net_buf_tailroom(ctxt->writer->nb), 0);
138 
139 	return zcbor_map_start_encode(zse, CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES);
140 }
141 #endif
142 
143 #if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD)
img_mgmt_slot_max_size(size_t * area_sizes,zcbor_state_t * zse)144 static bool img_mgmt_slot_max_size(size_t *area_sizes, zcbor_state_t *zse)
145 {
146 	bool ok = true;
147 
148 	if (area_sizes[0] > 0 && area_sizes[1] > 0) {
149 		/* Calculate maximum image size */
150 		size_t area_size_difference = (size_t)abs((ssize_t)area_sizes[1] -
151 							  (ssize_t)area_sizes[0]);
152 
153 		if (CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE >= area_size_difference) {
154 			ok = zcbor_tstr_put_lit(zse, "max_image_size") &&
155 			     zcbor_uint32_put(zse, (uint32_t)(area_sizes[0] -
156 					      CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE));		}
157 	}
158 
159 	return ok;
160 }
161 #elif defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_BOOTLOADER_INFO)
img_mgmt_slot_max_size(size_t * area_sizes,zcbor_state_t * zse)162 static bool img_mgmt_slot_max_size(size_t *area_sizes, zcbor_state_t *zse)
163 {
164 	bool ok = true;
165 	int rc;
166 	int max_app_size;
167 
168 	ARG_UNUSED(area_sizes);
169 
170 	rc = blinfo_lookup(BLINFO_MAX_APPLICATION_SIZE, &max_app_size, sizeof(max_app_size))
171 
172 	if (rc < 0) {
173 		LOG_ERR("Failed to lookup max application size: %d", rc);
174 	} else if (rc > 0) {
175 		ok = zcbor_tstr_put_lit(zse, "max_image_size") &&
176 		     zcbor_uint32_put(zse, (uint32_t)max_app_size);
177 	}
178 
179 	return ok;
180 }
181 #endif
182 
183 /**
184  * Finds the TLVs in the specified image slot, if any.
185  */
img_mgmt_find_tlvs(int slot,size_t * start_off,size_t * end_off,uint16_t magic)186 static int img_mgmt_find_tlvs(int slot, size_t *start_off, size_t *end_off, uint16_t magic)
187 {
188 	struct image_tlv_info tlv_info;
189 	int rc;
190 
191 	rc = img_mgmt_read(slot, *start_off, &tlv_info, sizeof(tlv_info));
192 	if (rc != 0) {
193 		/* Read error. */
194 		return rc;
195 	}
196 
197 	if (tlv_info.it_magic != magic) {
198 		/* No TLVs. */
199 		return IMG_MGMT_ERR_NO_TLVS;
200 	}
201 
202 	*start_off += sizeof(tlv_info);
203 	*end_off = *start_off + tlv_info.it_tlv_tot;
204 
205 	return IMG_MGMT_ERR_OK;
206 }
207 
img_mgmt_active_slot(int image)208 int img_mgmt_active_slot(int image)
209 {
210 	int slot = 0;
211 
212 	/* Multi image does not support DirectXIP or RAM load currently */
213 #if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER > 1
214 	slot = (image << 1);
215 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
216 	/* RAM load requires querying bootloader */
217 	int rc;
218 	uint8_t temp_slot;
219 
220 	rc = blinfo_lookup(BLINFO_RUNNING_SLOT, &temp_slot, sizeof(temp_slot));
221 
222 	if (rc <= 0) {
223 		LOG_ERR("Failed to fetch active slot: %d", rc);
224 
225 		return 255;
226 	}
227 
228 	slot = (int)temp_slot;
229 #else
230 	/* This covers single image, including DirectXiP */
231 	if (FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot1_partition)) {
232 		slot = 1;
233 	}
234 #endif
235 	LOG_DBG("(%d) => %d", image, slot);
236 
237 	return slot;
238 }
239 
img_mgmt_active_image(void)240 int img_mgmt_active_image(void)
241 {
242 	return ACTIVE_IMAGE_IS;
243 }
244 
245 /*
246  * Reads the version and build hash from the specified image slot.
247  */
img_mgmt_read_info(int image_slot,struct image_version * ver,uint8_t * hash,uint32_t * flags)248 int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
249 				   uint32_t *flags)
250 {
251 	struct image_header hdr;
252 	struct image_tlv tlv;
253 	size_t data_off;
254 	size_t data_end;
255 	bool hash_found;
256 	uint8_t erased_val;
257 	uint32_t erased_val_32;
258 	int rc;
259 
260 	rc = img_mgmt_erased_val(image_slot, &erased_val);
261 	if (rc != 0) {
262 		return IMG_MGMT_ERR_FLASH_CONFIG_QUERY_FAIL;
263 	}
264 
265 	rc = img_mgmt_read(image_slot, 0, &hdr, sizeof(hdr));
266 	if (rc != 0) {
267 		return rc;
268 	}
269 
270 	if (ver != NULL) {
271 		memset(ver, erased_val, sizeof(*ver));
272 	}
273 	erased_val_32 = ERASED_VAL_32(erased_val);
274 	if (hdr.ih_magic == IMAGE_MAGIC) {
275 		if (ver != NULL) {
276 			memcpy(ver, &hdr.ih_ver, sizeof(*ver));
277 		}
278 	} else if (hdr.ih_magic == erased_val_32) {
279 		return IMG_MGMT_ERR_NO_IMAGE;
280 	} else {
281 		return IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC;
282 	}
283 
284 	if (flags != NULL) {
285 		*flags = hdr.ih_flags;
286 	}
287 
288 	/* Read the image's TLVs. We first try to find the protected TLVs, if the protected
289 	 * TLV does not exist, we try to find non-protected TLV which also contains the hash
290 	 * TLV. All images are required to have a hash TLV.  If the hash is missing, the image
291 	 * is considered invalid.
292 	 */
293 	data_off = hdr.ih_hdr_size + hdr.ih_img_size;
294 
295 	rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_PROT_INFO_MAGIC);
296 	if (!rc) {
297 		/* The data offset should start after the header bytes after the end of
298 		 * the protected TLV, if one exists.
299 		 */
300 		data_off = data_end - sizeof(struct image_tlv_info);
301 	}
302 
303 	rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_INFO_MAGIC);
304 	if (rc != 0) {
305 		return IMG_MGMT_ERR_NO_TLVS;
306 	}
307 
308 	hash_found = false;
309 	while (data_off + sizeof(tlv) <= data_end) {
310 		rc = img_mgmt_read(image_slot, data_off, &tlv, sizeof(tlv));
311 		if (rc != 0) {
312 			return rc;
313 		}
314 		if (tlv.it_type == 0xff && tlv.it_len == 0xffff) {
315 			return IMG_MGMT_ERR_INVALID_TLV;
316 		}
317 		if (tlv.it_type != IMAGE_TLV_SHA256 || tlv.it_len != IMAGE_HASH_LEN) {
318 			/* Non-hash TLV.  Skip it. */
319 			data_off += sizeof(tlv) + tlv.it_len;
320 			continue;
321 		}
322 
323 		if (hash_found) {
324 			/* More than one hash. */
325 			return IMG_MGMT_ERR_TLV_MULTIPLE_HASHES_FOUND;
326 		}
327 		hash_found = true;
328 
329 		data_off += sizeof(tlv);
330 		if (hash != NULL) {
331 			if (data_off + IMAGE_HASH_LEN > data_end) {
332 				return IMG_MGMT_ERR_TLV_INVALID_SIZE;
333 			}
334 			rc = img_mgmt_read(image_slot, data_off, hash, IMAGE_HASH_LEN);
335 			if (rc != 0) {
336 				return rc;
337 			}
338 		}
339 	}
340 
341 	if (!hash_found) {
342 		return IMG_MGMT_ERR_HASH_NOT_FOUND;
343 	}
344 
345 	return 0;
346 }
347 
348 /*
349  * Finds image given version number. Returns the slot number image is in,
350  * or -1 if not found.
351  */
352 int
img_mgmt_find_by_ver(struct image_version * find,uint8_t * hash)353 img_mgmt_find_by_ver(struct image_version *find, uint8_t *hash)
354 {
355 	int i;
356 	struct image_version ver;
357 
358 	for (i = 0; i < 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER; i++) {
359 		if (img_mgmt_read_info(i, &ver, hash, NULL) != 0) {
360 			continue;
361 		}
362 		if (!memcmp(find, &ver, sizeof(ver))) {
363 			return i;
364 		}
365 	}
366 	return -1;
367 }
368 
369 /*
370  * Finds image given hash of the image. Returns the slot number image is in,
371  * or -1 if not found.
372  */
373 int
img_mgmt_find_by_hash(uint8_t * find,struct image_version * ver)374 img_mgmt_find_by_hash(uint8_t *find, struct image_version *ver)
375 {
376 	int i;
377 	uint8_t hash[IMAGE_HASH_LEN];
378 
379 	for (i = 0; i < 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER; i++) {
380 		if (img_mgmt_read_info(i, ver, hash, NULL) != 0) {
381 			continue;
382 		}
383 		if (!memcmp(hash, find, IMAGE_HASH_LEN)) {
384 			return i;
385 		}
386 	}
387 	return -1;
388 }
389 
390 /*
391  * Resets upload status to defaults (no upload in progress)
392  */
393 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
img_mgmt_reset_upload(void)394 void img_mgmt_reset_upload(void)
395 #else
396 static void img_mgmt_reset_upload(void)
397 #endif
398 {
399 	img_mgmt_take_lock();
400 	memset(&g_img_mgmt_state, 0, sizeof(g_img_mgmt_state));
401 	g_img_mgmt_state.area_id = -1;
402 	img_mgmt_release_lock();
403 }
404 
405 /**
406  * Command handler: image erase
407  */
408 static int
img_mgmt_erase(struct smp_streamer * ctxt)409 img_mgmt_erase(struct smp_streamer *ctxt)
410 {
411 	struct image_version ver;
412 	int rc;
413 	zcbor_state_t *zse = ctxt->writer->zs;
414 	zcbor_state_t *zsd = ctxt->reader->zs;
415 	bool ok;
416 	uint32_t slot = img_mgmt_get_opposite_slot(img_mgmt_active_slot(img_mgmt_active_image()));
417 	size_t decoded = 0;
418 
419 	struct zcbor_map_decode_key_val image_erase_decode[] = {
420 		ZCBOR_MAP_DECODE_KEY_DECODER("slot", zcbor_uint32_decode, &slot),
421 	};
422 
423 	ok = zcbor_map_decode_bulk(zsd, image_erase_decode,
424 		ARRAY_SIZE(image_erase_decode), &decoded) == 0;
425 
426 	if (!ok) {
427 		return MGMT_ERR_EINVAL;
428 	}
429 
430 	img_mgmt_take_lock();
431 
432 	/*
433 	 * First check if image info is valid.
434 	 * This check is done incase the flash area has a corrupted image.
435 	 */
436 	rc = img_mgmt_read_info(slot, &ver, NULL, NULL);
437 
438 	if (rc == 0) {
439 		/* Image info is valid. */
440 		if (img_mgmt_slot_in_use(slot)) {
441 			/* No free slot. */
442 			ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE,
443 					     IMG_MGMT_ERR_NO_FREE_SLOT);
444 			goto end;
445 		}
446 	}
447 
448 	rc = img_mgmt_erase_slot(slot);
449 	img_mgmt_reset_upload();
450 
451 	if (rc != 0) {
452 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
453 		int32_t err_rc;
454 		uint16_t err_group;
455 
456 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED, NULL, 0, &err_rc,
457 					   &err_group);
458 #endif
459 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
460 		goto end;
461 	}
462 
463 	if (IS_ENABLED(CONFIG_MCUMGR_SMP_LEGACY_RC_BEHAVIOUR)) {
464 		if (!zcbor_tstr_put_lit(zse, "rc") || !zcbor_int32_put(zse, 0)) {
465 			img_mgmt_release_lock();
466 			return MGMT_ERR_EMSGSIZE;
467 		}
468 	}
469 
470 end:
471 	img_mgmt_release_lock();
472 
473 	return MGMT_ERR_EOK;
474 }
475 
476 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO)
477 /**
478  * Command handler: image slot info
479  */
img_mgmt_slot_info(struct smp_streamer * ctxt)480 static int img_mgmt_slot_info(struct smp_streamer *ctxt)
481 {
482 	int rc;
483 	zcbor_state_t *zse = ctxt->writer->zs;
484 	bool ok;
485 	uint8_t i = 0;
486 	size_t area_sizes[SLOTS_PER_IMAGE];
487 
488 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
489 	int32_t err_rc;
490 	uint16_t err_group;
491 	enum mgmt_cb_return status;
492 #endif
493 
494 	img_mgmt_take_lock();
495 
496 	ok = zcbor_tstr_put_lit(zse, "images") &&
497 	     zcbor_list_start_encode(zse, 10);
498 
499 	while (i < CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER * SLOTS_PER_IMAGE) {
500 		const struct flash_area *fa;
501 		int area_id = img_mgmt_flash_area_id(i);
502 
503 		if ((i % SLOTS_PER_IMAGE) == 0) {
504 			memset(area_sizes, 0, sizeof(area_sizes));
505 
506 			ok = zcbor_map_start_encode(zse, 4) &&
507 			     zcbor_tstr_put_lit(zse, "image") &&
508 			     zcbor_uint32_put(zse, (uint32_t)(i / SLOTS_PER_IMAGE)) &&
509 			     zcbor_tstr_put_lit(zse, "slots") &&
510 			     zcbor_list_start_encode(zse, 4);
511 
512 			if (!ok) {
513 				goto finish;
514 			}
515 		}
516 
517 		ok = zcbor_map_start_encode(zse, 4) &&
518 		     zcbor_tstr_put_lit(zse, "slot") &&
519 		     zcbor_uint32_put(zse, (uint32_t)(i % SLOTS_PER_IMAGE));
520 
521 		if (!ok) {
522 			goto finish;
523 		}
524 
525 		rc = flash_area_open(area_id, &fa);
526 
527 		if (rc) {
528 			/* Failed opening slot, mark as error */
529 			ok = zcbor_tstr_put_lit(zse, "rc") &&
530 			     zcbor_int32_put(zse, rc);
531 
532 			LOG_ERR("Failed to open slot %d for information fetching: %d", area_id, rc);
533 		} else {
534 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
535 			struct img_mgmt_slot_info_slot slot_info_data = {
536 				.image = (i / SLOTS_PER_IMAGE),
537 				.slot = (i % SLOTS_PER_IMAGE),
538 				.fa = fa,
539 				.zse = zse,
540 			};
541 #endif
542 
543 			if (sizeof(fa->fa_size) == sizeof(uint64_t)) {
544 				ok = zcbor_tstr_put_lit(zse, "size") &&
545 				     zcbor_uint64_put(zse, fa->fa_size);
546 			} else {
547 				ok = zcbor_tstr_put_lit(zse, "size") &&
548 				     zcbor_uint32_put(zse, fa->fa_size);
549 			}
550 
551 			area_sizes[(i % SLOTS_PER_IMAGE)] = fa->fa_size;
552 
553 			if (!ok) {
554 				goto finish;
555 			}
556 
557 			/*
558 			 * Check if we support uploading to this slot and if so, return the
559 			 * image ID
560 			 */
561 #if defined(CONFIG_MCUMGR_GRP_IMG_DIRECT_UPLOAD)
562 			ok = zcbor_tstr_put_lit(zse, "upload_image_id") &&
563 			     zcbor_uint32_put(zse, (i + 1));
564 #else
565 			if (img_mgmt_active_slot((i / SLOTS_PER_IMAGE)) != i) {
566 				ok = zcbor_tstr_put_lit(zse, "upload_image_id") &&
567 				     zcbor_uint32_put(zse, (i / SLOTS_PER_IMAGE));
568 			}
569 #endif
570 
571 			if (!ok) {
572 				goto finish;
573 			}
574 
575 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
576 			status = mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_SLOT,
577 						      &slot_info_data, sizeof(slot_info_data),
578 						      &err_rc, &err_group);
579 #endif
580 
581 			flash_area_close(fa);
582 
583 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
584 			if (status != MGMT_CB_OK) {
585 				if (status == MGMT_CB_ERROR_RC) {
586 					img_mgmt_release_lock();
587 					return err_rc;
588 				}
589 
590 				ok = img_mgmt_reset_zse(ctxt) &&
591 				     smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
592 
593 				goto finish;
594 			}
595 #endif
596 		}
597 
598 		ok &= zcbor_map_end_encode(zse, 4);
599 
600 		if (!ok) {
601 			goto finish;
602 		}
603 
604 		if ((i % SLOTS_PER_IMAGE) == (SLOTS_PER_IMAGE - 1)) {
605 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
606 			struct img_mgmt_slot_info_image image_info_data = {
607 				.image = (i / SLOTS_PER_IMAGE),
608 				.zse = zse,
609 			};
610 #endif
611 
612 			ok = zcbor_list_end_encode(zse, 4);
613 
614 			if (!ok) {
615 				goto finish;
616 			}
617 
618 #if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD) || \
619 	defined(MCUMGR_GRP_IMG_TOO_LARGE_BOOTLOADER_INFO)
620 			ok = img_mgmt_slot_max_size(area_sizes, zse);
621 
622 			if (!ok) {
623 				goto finish;
624 			}
625 #endif
626 
627 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO_HOOKS)
628 			status = mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_SLOT_INFO_IMAGE,
629 						      &image_info_data, sizeof(image_info_data),
630 						      &err_rc, &err_group);
631 
632 			if (status != MGMT_CB_OK) {
633 				if (status == MGMT_CB_ERROR_RC) {
634 					img_mgmt_release_lock();
635 					return err_rc;
636 				}
637 
638 				ok = img_mgmt_reset_zse(ctxt) &&
639 				     smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
640 
641 				goto finish;
642 			}
643 #endif
644 
645 			ok = zcbor_map_end_encode(zse, 4);
646 
647 			if (!ok) {
648 				goto finish;
649 			}
650 		}
651 
652 		++i;
653 	}
654 
655 	ok = zcbor_list_end_encode(zse, 10);
656 
657 finish:
658 	img_mgmt_release_lock();
659 
660 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
661 }
662 #endif
663 
664 static int
img_mgmt_upload_good_rsp(struct smp_streamer * ctxt)665 img_mgmt_upload_good_rsp(struct smp_streamer *ctxt)
666 {
667 	zcbor_state_t *zse = ctxt->writer->zs;
668 	bool ok = true;
669 
670 	if (IS_ENABLED(CONFIG_MCUMGR_SMP_LEGACY_RC_BEHAVIOUR)) {
671 		ok = zcbor_tstr_put_lit(zse, "rc")		&&
672 		     zcbor_int32_put(zse, MGMT_ERR_EOK);
673 	}
674 
675 	ok = ok && zcbor_tstr_put_lit(zse, "off")		&&
676 		   zcbor_size_put(zse, g_img_mgmt_state.off);
677 
678 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
679 }
680 
681 /**
682  * Logs an upload request if necessary.
683  *
684  * @param is_first	Whether the request includes the first chunk of the image.
685  * @param is_last	Whether the request includes the last chunk of the image.
686  * @param status	The result of processing the upload request (MGMT_ERR code).
687  *
688  * @return 0 on success; nonzero on failure.
689  */
690 static int
img_mgmt_upload_log(bool is_first,bool is_last,int status)691 img_mgmt_upload_log(bool is_first, bool is_last, int status)
692 {
693 	uint8_t hash[IMAGE_HASH_LEN];
694 	const uint8_t *hashp;
695 	int rc;
696 
697 	if (is_last || status != 0) {
698 		/* Log the image hash if we know it. */
699 		rc = img_mgmt_read_info(1, NULL, hash, NULL);
700 		if (rc != 0) {
701 			hashp = NULL;
702 		} else {
703 			hashp = hash;
704 		}
705 	}
706 
707 	return 0;
708 }
709 
710 /**
711  * Command handler: image upload
712  */
713 static int
img_mgmt_upload(struct smp_streamer * ctxt)714 img_mgmt_upload(struct smp_streamer *ctxt)
715 {
716 	zcbor_state_t *zse = ctxt->writer->zs;
717 	zcbor_state_t *zsd = ctxt->reader->zs;
718 	bool ok;
719 	size_t decoded = 0;
720 	struct img_mgmt_upload_req req = {
721 		.off = SIZE_MAX,
722 		.size = SIZE_MAX,
723 		.img_data = { 0 },
724 		.data_sha = { 0 },
725 		.upgrade = false,
726 		.image = 0,
727 	};
728 	int rc;
729 	struct img_mgmt_upload_action action;
730 	bool last = false;
731 	bool reset = false;
732 
733 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
734 	bool data_match = false;
735 #endif
736 
737 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK)
738 	enum mgmt_cb_return status;
739 #endif
740 
741 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK) ||	\
742 defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS) ||		\
743 defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
744 	int32_t err_rc;
745 	uint16_t err_group;
746 #endif
747 
748 	struct zcbor_map_decode_key_val image_upload_decode[] = {
749 		ZCBOR_MAP_DECODE_KEY_DECODER("image", zcbor_uint32_decode, &req.image),
750 		ZCBOR_MAP_DECODE_KEY_DECODER("data", zcbor_bstr_decode, &req.img_data),
751 		ZCBOR_MAP_DECODE_KEY_DECODER("len", zcbor_size_decode, &req.size),
752 		ZCBOR_MAP_DECODE_KEY_DECODER("off", zcbor_size_decode, &req.off),
753 		ZCBOR_MAP_DECODE_KEY_DECODER("sha", zcbor_bstr_decode, &req.data_sha),
754 		ZCBOR_MAP_DECODE_KEY_DECODER("upgrade", zcbor_bool_decode, &req.upgrade)
755 	};
756 
757 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
758 	struct mgmt_evt_op_cmd_arg cmd_status_arg = {
759 		.group = MGMT_GROUP_ID_IMAGE,
760 		.id = IMG_MGMT_ID_UPLOAD,
761 	};
762 #endif
763 
764 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK)
765 	struct img_mgmt_upload_check upload_check_data = {
766 		.action = &action,
767 		.req = &req,
768 	};
769 #endif
770 
771 	ok = zcbor_map_decode_bulk(zsd, image_upload_decode,
772 		ARRAY_SIZE(image_upload_decode), &decoded) == 0;
773 
774 	IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action, NULL);
775 
776 	if (!ok) {
777 		return MGMT_ERR_EINVAL;
778 	}
779 
780 	img_mgmt_take_lock();
781 
782 	/* Determine what actions to take as a result of this request. */
783 	rc = img_mgmt_upload_inspect(&req, &action);
784 	if (rc != 0) {
785 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
786 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED, NULL, 0, &err_rc,
787 					   &err_group);
788 #endif
789 
790 		MGMT_CTXT_SET_RC_RSN(ctxt, IMG_MGMT_UPLOAD_ACTION_RC_RSN(&action));
791 		LOG_ERR("Image upload inspect failed: %d", rc);
792 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
793 		goto end;
794 	}
795 
796 	if (!action.proceed) {
797 		/* Request specifies incorrect offset.  Respond with a success code and
798 		 * the correct offset.
799 		 */
800 		rc = img_mgmt_upload_good_rsp(ctxt);
801 		img_mgmt_release_lock();
802 		return rc;
803 	}
804 
805 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK)
806 	/* Request is valid.  Give the application a chance to reject this upload
807 	 * request.
808 	 */
809 	status = mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK, &upload_check_data,
810 				      sizeof(upload_check_data), &err_rc, &err_group);
811 
812 	if (status != MGMT_CB_OK) {
813 		IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action, img_mgmt_err_str_app_reject);
814 
815 		if (status == MGMT_CB_ERROR_RC) {
816 			rc = err_rc;
817 			ok = zcbor_tstr_put_lit(zse, "rc")	&&
818 			     zcbor_int32_put(zse, rc);
819 		} else {
820 			ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
821 		}
822 
823 		goto end;
824 	}
825 #endif
826 
827 	/* Remember flash area ID and image size for subsequent upload requests. */
828 	g_img_mgmt_state.area_id = action.area_id;
829 	g_img_mgmt_state.size = action.size;
830 
831 	if (req.off == 0) {
832 		/*
833 		 * New upload.
834 		 */
835 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
836 		struct flash_img_context ctx;
837 		struct flash_img_check fic;
838 #endif
839 
840 		g_img_mgmt_state.off = 0;
841 
842 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
843 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STARTED, NULL, 0, &err_rc,
844 					   &err_group);
845 #endif
846 
847 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
848 		cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_START;
849 #endif
850 
851 		/*
852 		 * We accept SHA trimmed to any length by client since it's up to client
853 		 * to make sure provided data are good enough to avoid collisions when
854 		 * resuming upload.
855 		 */
856 		g_img_mgmt_state.data_sha_len = req.data_sha.len;
857 		memcpy(g_img_mgmt_state.data_sha, req.data_sha.value, req.data_sha.len);
858 		memset(&g_img_mgmt_state.data_sha[req.data_sha.len], 0,
859 			   IMG_MGMT_DATA_SHA_LEN - req.data_sha.len);
860 
861 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
862 		/* Check if the existing image hash matches the hash of the underlying data,
863 		 * this check can only be performed if the provided hash is a full SHA256 hash
864 		 * of the file that is being uploaded, do not attempt the check if the length
865 		 * of the provided hash is less.
866 		 */
867 		if (g_img_mgmt_state.data_sha_len == IMG_MGMT_DATA_SHA_LEN) {
868 			fic.match = g_img_mgmt_state.data_sha;
869 			fic.clen = g_img_mgmt_state.size;
870 
871 			if (flash_img_check(&ctx, &fic, g_img_mgmt_state.area_id) == 0) {
872 				/* Underlying data already matches, no need to upload any more,
873 				 * set offset to image size so client knows upload has finished.
874 				 */
875 				g_img_mgmt_state.off = g_img_mgmt_state.size;
876 				reset = true;
877 				last = true;
878 				data_match = true;
879 
880 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
881 				cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_COMPLETE;
882 #endif
883 
884 				goto end;
885 			}
886 		}
887 #endif
888 
889 #ifndef CONFIG_IMG_ERASE_PROGRESSIVELY
890 		/* erase the entire req.size all at once */
891 		if (action.erase) {
892 			rc = img_mgmt_erase_image_data(0, req.size);
893 			if (rc != 0) {
894 				IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action,
895 					img_mgmt_err_str_flash_erase_failed);
896 				ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
897 				goto end;
898 			}
899 		}
900 #endif
901 	} else {
902 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
903 		cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_ONGOING;
904 #endif
905 	}
906 
907 	/* Write the image data to flash. */
908 	if (req.img_data.len != 0) {
909 		/* If this is the last chunk */
910 		if (g_img_mgmt_state.off + req.img_data.len == g_img_mgmt_state.size) {
911 			last = true;
912 		}
913 
914 		rc = img_mgmt_write_image_data(req.off, req.img_data.value, action.write_bytes,
915 						    last);
916 		if (rc == 0) {
917 			g_img_mgmt_state.off += action.write_bytes;
918 		} else {
919 			/* Write failed, currently not able to recover from this */
920 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
921 			cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_COMPLETE;
922 #endif
923 
924 			IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action,
925 				img_mgmt_err_str_flash_write_failed);
926 			reset = true;
927 			IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action,
928 				img_mgmt_err_str_flash_write_failed);
929 
930 			LOG_ERR("Irrecoverable error: flash write failed: %d", rc);
931 
932 			ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
933 			goto end;
934 		}
935 
936 		if (g_img_mgmt_state.off == g_img_mgmt_state.size) {
937 			/* Done */
938 			reset = true;
939 
940 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
941 			static struct flash_img_context ctx;
942 
943 			if (flash_img_init_id(&ctx, g_img_mgmt_state.area_id) == 0) {
944 				struct flash_img_check fic = {
945 					.match = g_img_mgmt_state.data_sha,
946 					.clen = g_img_mgmt_state.size,
947 				};
948 
949 				if (flash_img_check(&ctx, &fic, g_img_mgmt_state.area_id) == 0) {
950 					data_match = true;
951 				} else {
952 					LOG_ERR("Uploaded image sha256 hash verification failed");
953 				}
954 			} else {
955 				LOG_ERR("Uploaded image sha256 could not be checked");
956 			}
957 #endif
958 
959 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
960 			(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_PENDING, NULL, 0,
961 						   &err_rc, &err_group);
962 		} else {
963 			/* Notify that the write has completed */
964 			(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE,
965 						   NULL, 0, &err_rc, &err_group);
966 #endif
967 		}
968 	}
969 end:
970 
971 	img_mgmt_upload_log(req.off == 0, g_img_mgmt_state.off == g_img_mgmt_state.size, rc);
972 
973 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
974 	(void)mgmt_callback_notify(MGMT_EVT_OP_CMD_STATUS, &cmd_status_arg,
975 				   sizeof(cmd_status_arg), &err_rc, &err_group);
976 #endif
977 
978 	if (rc != 0) {
979 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
980 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED, NULL, 0, &err_rc,
981 					   &err_group);
982 #endif
983 
984 		img_mgmt_reset_upload();
985 	} else {
986 		rc = img_mgmt_upload_good_rsp(ctxt);
987 
988 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
989 		if (last && rc == MGMT_ERR_EOK) {
990 			/* Append status to last packet */
991 			ok = zcbor_tstr_put_lit(zse, "match")	&&
992 			     zcbor_bool_put(zse, data_match);
993 		}
994 #endif
995 
996 		if (reset) {
997 			/* Reset the upload state struct back to default */
998 			img_mgmt_reset_upload();
999 		}
1000 	}
1001 
1002 	img_mgmt_release_lock();
1003 
1004 	if (!ok) {
1005 		return MGMT_ERR_EMSGSIZE;
1006 	}
1007 
1008 	return MGMT_ERR_EOK;
1009 }
1010 
img_mgmt_my_version(struct image_version * ver)1011 int img_mgmt_my_version(struct image_version *ver)
1012 {
1013 	return img_mgmt_read_info(img_mgmt_active_slot(img_mgmt_active_image()),
1014 				  ver, NULL, NULL);
1015 }
1016 
1017 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
1018 /*
1019  * @brief	Translate IMG mgmt group error code into MCUmgr error code
1020  *
1021  * @param ret	#img_mgmt_err_code_t error code
1022  *
1023  * @return	#mcumgr_err_t error code
1024  */
img_mgmt_translate_error_code(uint16_t err)1025 static int img_mgmt_translate_error_code(uint16_t err)
1026 {
1027 	int rc;
1028 
1029 	switch (err) {
1030 	case IMG_MGMT_ERR_NO_IMAGE:
1031 	case IMG_MGMT_ERR_NO_TLVS:
1032 		rc = MGMT_ERR_ENOENT;
1033 		break;
1034 
1035 	case IMG_MGMT_ERR_NO_FREE_SLOT:
1036 	case IMG_MGMT_ERR_CURRENT_VERSION_IS_NEWER:
1037 	case IMG_MGMT_ERR_IMAGE_ALREADY_PENDING:
1038 		rc = MGMT_ERR_EBADSTATE;
1039 		break;
1040 
1041 	case IMG_MGMT_ERR_NO_FREE_MEMORY:
1042 		rc = MGMT_ERR_ENOMEM;
1043 		break;
1044 
1045 	case IMG_MGMT_ERR_INVALID_SLOT:
1046 	case IMG_MGMT_ERR_INVALID_PAGE_OFFSET:
1047 	case IMG_MGMT_ERR_INVALID_OFFSET:
1048 	case IMG_MGMT_ERR_INVALID_LENGTH:
1049 	case IMG_MGMT_ERR_INVALID_IMAGE_HEADER:
1050 	case IMG_MGMT_ERR_INVALID_HASH:
1051 	case IMG_MGMT_ERR_INVALID_FLASH_ADDRESS:
1052 		rc = MGMT_ERR_EINVAL;
1053 		break;
1054 
1055 	case IMG_MGMT_ERR_FLASH_CONFIG_QUERY_FAIL:
1056 	case IMG_MGMT_ERR_VERSION_GET_FAILED:
1057 	case IMG_MGMT_ERR_TLV_MULTIPLE_HASHES_FOUND:
1058 	case IMG_MGMT_ERR_TLV_INVALID_SIZE:
1059 	case IMG_MGMT_ERR_HASH_NOT_FOUND:
1060 	case IMG_MGMT_ERR_INVALID_TLV:
1061 	case IMG_MGMT_ERR_FLASH_OPEN_FAILED:
1062 	case IMG_MGMT_ERR_FLASH_READ_FAILED:
1063 	case IMG_MGMT_ERR_FLASH_WRITE_FAILED:
1064 	case IMG_MGMT_ERR_FLASH_ERASE_FAILED:
1065 	case IMG_MGMT_ERR_FLASH_CONTEXT_ALREADY_SET:
1066 	case IMG_MGMT_ERR_FLASH_CONTEXT_NOT_SET:
1067 	case IMG_MGMT_ERR_FLASH_AREA_DEVICE_NULL:
1068 	case IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC:
1069 	case IMG_MGMT_ERR_INVALID_IMAGE_VECTOR_TABLE:
1070 	case IMG_MGMT_ERR_INVALID_IMAGE_TOO_LARGE:
1071 	case IMG_MGMT_ERR_INVALID_IMAGE_DATA_OVERRUN:
1072 	case IMG_MGMT_ERR_UNKNOWN:
1073 	default:
1074 		rc = MGMT_ERR_EUNKNOWN;
1075 	}
1076 
1077 	return rc;
1078 }
1079 #endif
1080 
1081 static const struct mgmt_handler img_mgmt_handlers[] = {
1082 	[IMG_MGMT_ID_STATE] = {
1083 		.mh_read = img_mgmt_state_read,
1084 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP) || \
1085 	defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
1086 		.mh_write = NULL
1087 #else
1088 		.mh_write = img_mgmt_state_write,
1089 #endif
1090 	},
1091 	[IMG_MGMT_ID_UPLOAD] = {
1092 		.mh_read = NULL,
1093 		.mh_write = img_mgmt_upload
1094 	},
1095 	[IMG_MGMT_ID_ERASE] = {
1096 		.mh_read = NULL,
1097 		.mh_write = img_mgmt_erase
1098 	},
1099 #if defined(CONFIG_MCUMGR_GRP_IMG_SLOT_INFO)
1100 	[IMG_MGMT_ID_SLOT_INFO] = {
1101 		.mh_read = img_mgmt_slot_info,
1102 		.mh_write = NULL
1103 	},
1104 #endif
1105 };
1106 
1107 static const struct mgmt_handler img_mgmt_handlers[];
1108 
1109 #define IMG_MGMT_HANDLER_CNT ARRAY_SIZE(img_mgmt_handlers)
1110 
1111 static struct mgmt_group img_mgmt_group = {
1112 	.mg_handlers = (struct mgmt_handler *)img_mgmt_handlers,
1113 	.mg_handlers_count = IMG_MGMT_HANDLER_CNT,
1114 	.mg_group_id = MGMT_GROUP_ID_IMAGE,
1115 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
1116 	.mg_translate_error = img_mgmt_translate_error_code,
1117 #endif
1118 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
1119 	.mg_group_name = "img mgmt",
1120 #endif
1121 };
1122 
img_mgmt_register_group(void)1123 static void img_mgmt_register_group(void)
1124 {
1125 	mgmt_register_group(&img_mgmt_group);
1126 }
1127 
1128 MCUMGR_HANDLER_DEFINE(img_mgmt, img_mgmt_register_group);
1129