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.h>
9 #include <limits.h>
10 #include <assert.h>
11 #include <string.h>
12 #include <zephyr/toolchain.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/storage/flash_map.h>
15 
16 #include <zcbor_common.h>
17 #include <zcbor_decode.h>
18 #include <zcbor_encode.h>
19 
20 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
21 #include <zephyr/mgmt/mcumgr/smp/smp.h>
22 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
23 #include <zephyr/mgmt/mcumgr/grp/img_mgmt/img_mgmt.h>
24 
25 #include <mgmt/mcumgr/util/zcbor_bulk.h>
26 #include <mgmt/mcumgr/grp/img_mgmt/img_mgmt_priv.h>
27 
28 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
29 #include <zephyr/dfu/flash_img.h>
30 #endif
31 
32 #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
33 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
34 #endif
35 
36 #ifndef CONFIG_FLASH_LOAD_OFFSET
37 #error MCUmgr requires application to be built with CONFIG_FLASH_LOAD_OFFSET set \
38 	to be able to figure out application running slot.
39 #endif
40 
41 #define FIXED_PARTITION_IS_RUNNING_APP_PARTITION(label)	\
42 	 (FIXED_PARTITION_OFFSET(label) == CONFIG_FLASH_LOAD_OFFSET)
43 
44 BUILD_ASSERT(sizeof(struct image_header) == IMAGE_HEADER_SIZE,
45 	     "struct image_header not required size");
46 
47 #if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER >= 2
48 #if FIXED_PARTITION_EXISTS(slot0_ns_partition) &&			\
49 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_ns_partition)
50 #define ACTIVE_IMAGE_IS 0
51 #elif FIXED_PARTITION_EXISTS(slot0_partition) &&			\
52 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot0_partition)
53 #define ACTIVE_IMAGE_IS 0
54 #elif FIXED_PARTITION_EXISTS(slot1_partition) &&			\
55 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot1_partition)
56 #define ACTIVE_IMAGE_IS 0
57 #elif FIXED_PARTITION_EXISTS(slot2_partition) &&			\
58 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot2_partition)
59 #define ACTIVE_IMAGE_IS 1
60 #elif FIXED_PARTITION_EXISTS(slot3_partition) &&			\
61 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot3_partition)
62 #define ACTIVE_IMAGE_IS 1
63 #elif FIXED_PARTITION_EXISTS(slot4_partition) &&			\
64 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot4_partition)
65 #define ACTIVE_IMAGE_IS 2
66 #elif FIXED_PARTITION_EXISTS(slot5_partition) &&			\
67 	FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot5_partition)
68 #define ACTIVE_IMAGE_IS 2
69 #else
70 #define ACTIVE_IMAGE_IS 0
71 #endif
72 #else
73 #define ACTIVE_IMAGE_IS 0
74 #endif
75 
76 LOG_MODULE_REGISTER(mcumgr_img_grp, CONFIG_MCUMGR_GRP_IMG_LOG_LEVEL);
77 
78 struct img_mgmt_state g_img_mgmt_state;
79 
80 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
81 static K_MUTEX_DEFINE(img_mgmt_mutex);
82 #endif
83 
84 #ifdef CONFIG_MCUMGR_GRP_IMG_VERBOSE_ERR
85 const char *img_mgmt_err_str_app_reject = "app reject";
86 const char *img_mgmt_err_str_hdr_malformed = "header malformed";
87 const char *img_mgmt_err_str_magic_mismatch = "magic mismatch";
88 const char *img_mgmt_err_str_no_slot = "no slot";
89 const char *img_mgmt_err_str_flash_open_failed = "fa open fail";
90 const char *img_mgmt_err_str_flash_erase_failed = "fa erase fail";
91 const char *img_mgmt_err_str_flash_write_failed = "fa write fail";
92 const char *img_mgmt_err_str_downgrade = "downgrade";
93 const char *img_mgmt_err_str_image_bad_flash_addr = "img addr mismatch";
94 const char *img_mgmt_err_str_image_too_large = "img too large";
95 const char *img_mgmt_err_str_data_overrun = "data overrun";
96 #endif
97 
img_mgmt_take_lock(void)98 void img_mgmt_take_lock(void)
99 {
100 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
101 	k_mutex_lock(&img_mgmt_mutex, K_FOREVER);
102 #endif
103 }
104 
img_mgmt_release_lock(void)105 void img_mgmt_release_lock(void)
106 {
107 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
108 	k_mutex_unlock(&img_mgmt_mutex);
109 #endif
110 }
111 
112 /**
113  * Finds the TLVs in the specified image slot, if any.
114  */
img_mgmt_find_tlvs(int slot,size_t * start_off,size_t * end_off,uint16_t magic)115 static int img_mgmt_find_tlvs(int slot, size_t *start_off, size_t *end_off, uint16_t magic)
116 {
117 	struct image_tlv_info tlv_info;
118 	int rc;
119 
120 	rc = img_mgmt_read(slot, *start_off, &tlv_info, sizeof(tlv_info));
121 	if (rc != 0) {
122 		/* Read error. */
123 		return rc;
124 	}
125 
126 	if (tlv_info.it_magic != magic) {
127 		/* No TLVs. */
128 		return IMG_MGMT_ERR_NO_TLVS;
129 	}
130 
131 	*start_off += sizeof(tlv_info);
132 	*end_off = *start_off + tlv_info.it_tlv_tot;
133 
134 	return IMG_MGMT_ERR_OK;
135 }
136 
img_mgmt_active_slot(int image)137 int img_mgmt_active_slot(int image)
138 {
139 	int slot = 0;
140 
141 	/* Multi image does not support DirectXIP currently */
142 #if CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER > 1
143 	slot = (image << 1);
144 #else
145 	/* This covers single image, including DirectXiP */
146 	if (FIXED_PARTITION_IS_RUNNING_APP_PARTITION(slot1_partition)) {
147 		slot = 1;
148 	}
149 #endif
150 	LOG_DBG("(%d) => %d", image, slot);
151 
152 	return slot;
153 }
154 
img_mgmt_active_image(void)155 int img_mgmt_active_image(void)
156 {
157 	return ACTIVE_IMAGE_IS;
158 }
159 
160 /*
161  * Reads the version and build hash from the specified image slot.
162  */
img_mgmt_read_info(int image_slot,struct image_version * ver,uint8_t * hash,uint32_t * flags)163 int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
164 				   uint32_t *flags)
165 {
166 	struct image_header hdr;
167 	struct image_tlv tlv;
168 	size_t data_off;
169 	size_t data_end;
170 	bool hash_found;
171 	uint8_t erased_val;
172 	uint32_t erased_val_32;
173 	int rc;
174 
175 	rc = img_mgmt_erased_val(image_slot, &erased_val);
176 	if (rc != 0) {
177 		return IMG_MGMT_ERR_FLASH_CONFIG_QUERY_FAIL;
178 	}
179 
180 	rc = img_mgmt_read(image_slot, 0, &hdr, sizeof(hdr));
181 	if (rc != 0) {
182 		return rc;
183 	}
184 
185 	if (ver != NULL) {
186 		memset(ver, erased_val, sizeof(*ver));
187 	}
188 	erased_val_32 = ERASED_VAL_32(erased_val);
189 	if (hdr.ih_magic == IMAGE_MAGIC) {
190 		if (ver != NULL) {
191 			memcpy(ver, &hdr.ih_ver, sizeof(*ver));
192 		}
193 	} else if (hdr.ih_magic == erased_val_32) {
194 		return IMG_MGMT_ERR_NO_IMAGE;
195 	} else {
196 		return IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC;
197 	}
198 
199 	if (flags != NULL) {
200 		*flags = hdr.ih_flags;
201 	}
202 
203 	/* Read the image's TLVs. We first try to find the protected TLVs, if the protected
204 	 * TLV does not exist, we try to find non-protected TLV which also contains the hash
205 	 * TLV. All images are required to have a hash TLV.  If the hash is missing, the image
206 	 * is considered invalid.
207 	 */
208 	data_off = hdr.ih_hdr_size + hdr.ih_img_size;
209 
210 	rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_PROT_INFO_MAGIC);
211 	if (!rc) {
212 		/* The data offset should start after the header bytes after the end of
213 		 * the protected TLV, if one exists.
214 		 */
215 		data_off = data_end - sizeof(struct image_tlv_info);
216 	}
217 
218 	rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_INFO_MAGIC);
219 	if (rc != 0) {
220 		return IMG_MGMT_ERR_NO_TLVS;
221 	}
222 
223 	hash_found = false;
224 	while (data_off + sizeof(tlv) <= data_end) {
225 		rc = img_mgmt_read(image_slot, data_off, &tlv, sizeof(tlv));
226 		if (rc != 0) {
227 			return rc;
228 		}
229 		if (tlv.it_type == 0xff && tlv.it_len == 0xffff) {
230 			return IMG_MGMT_ERR_INVALID_TLV;
231 		}
232 		if (tlv.it_type != IMAGE_TLV_SHA256 || tlv.it_len != IMAGE_HASH_LEN) {
233 			/* Non-hash TLV.  Skip it. */
234 			data_off += sizeof(tlv) + tlv.it_len;
235 			continue;
236 		}
237 
238 		if (hash_found) {
239 			/* More than one hash. */
240 			return IMG_MGMT_ERR_TLV_MULTIPLE_HASHES_FOUND;
241 		}
242 		hash_found = true;
243 
244 		data_off += sizeof(tlv);
245 		if (hash != NULL) {
246 			if (data_off + IMAGE_HASH_LEN > data_end) {
247 				return IMG_MGMT_ERR_TLV_INVALID_SIZE;
248 			}
249 			rc = img_mgmt_read(image_slot, data_off, hash, IMAGE_HASH_LEN);
250 			if (rc != 0) {
251 				return rc;
252 			}
253 		}
254 	}
255 
256 	if (!hash_found) {
257 		return IMG_MGMT_ERR_HASH_NOT_FOUND;
258 	}
259 
260 	return 0;
261 }
262 
263 /*
264  * Finds image given version number. Returns the slot number image is in,
265  * or -1 if not found.
266  */
267 int
img_mgmt_find_by_ver(struct image_version * find,uint8_t * hash)268 img_mgmt_find_by_ver(struct image_version *find, uint8_t *hash)
269 {
270 	int i;
271 	struct image_version ver;
272 
273 	for (i = 0; i < 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER; i++) {
274 		if (img_mgmt_read_info(i, &ver, hash, NULL) != 0) {
275 			continue;
276 		}
277 		if (!memcmp(find, &ver, sizeof(ver))) {
278 			return i;
279 		}
280 	}
281 	return -1;
282 }
283 
284 /*
285  * Finds image given hash of the image. Returns the slot number image is in,
286  * or -1 if not found.
287  */
288 int
img_mgmt_find_by_hash(uint8_t * find,struct image_version * ver)289 img_mgmt_find_by_hash(uint8_t *find, struct image_version *ver)
290 {
291 	int i;
292 	uint8_t hash[IMAGE_HASH_LEN];
293 
294 	for (i = 0; i < 2 * CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER; i++) {
295 		if (img_mgmt_read_info(i, ver, hash, NULL) != 0) {
296 			continue;
297 		}
298 		if (!memcmp(hash, find, IMAGE_HASH_LEN)) {
299 			return i;
300 		}
301 	}
302 	return -1;
303 }
304 
305 /*
306  * Resets upload status to defaults (no upload in progress)
307  */
308 #ifdef CONFIG_MCUMGR_GRP_IMG_MUTEX
img_mgmt_reset_upload(void)309 void img_mgmt_reset_upload(void)
310 #else
311 static void img_mgmt_reset_upload(void)
312 #endif
313 {
314 	img_mgmt_take_lock();
315 	memset(&g_img_mgmt_state, 0, sizeof(g_img_mgmt_state));
316 	g_img_mgmt_state.area_id = -1;
317 	img_mgmt_release_lock();
318 }
319 
320 /**
321  * Command handler: image erase
322  */
323 static int
img_mgmt_erase(struct smp_streamer * ctxt)324 img_mgmt_erase(struct smp_streamer *ctxt)
325 {
326 	struct image_version ver;
327 	int rc;
328 	zcbor_state_t *zse = ctxt->writer->zs;
329 	zcbor_state_t *zsd = ctxt->reader->zs;
330 	bool ok;
331 	uint32_t slot = img_mgmt_get_opposite_slot(img_mgmt_active_slot(img_mgmt_active_image()));
332 	size_t decoded = 0;
333 
334 	struct zcbor_map_decode_key_val image_erase_decode[] = {
335 		ZCBOR_MAP_DECODE_KEY_DECODER("slot", zcbor_uint32_decode, &slot),
336 	};
337 
338 	ok = zcbor_map_decode_bulk(zsd, image_erase_decode,
339 		ARRAY_SIZE(image_erase_decode), &decoded) == 0;
340 
341 	if (!ok) {
342 		return MGMT_ERR_EINVAL;
343 	}
344 
345 	img_mgmt_take_lock();
346 
347 	/*
348 	 * First check if image info is valid.
349 	 * This check is done incase the flash area has a corrupted image.
350 	 */
351 	rc = img_mgmt_read_info(slot, &ver, NULL, NULL);
352 
353 	if (rc == 0) {
354 		/* Image info is valid. */
355 		if (img_mgmt_slot_in_use(slot)) {
356 			/* No free slot. */
357 			ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE,
358 					     IMG_MGMT_ERR_NO_FREE_SLOT);
359 			goto end;
360 		}
361 	}
362 
363 	rc = img_mgmt_erase_slot(slot);
364 	img_mgmt_reset_upload();
365 
366 	if (rc != 0) {
367 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
368 		int32_t err_rc;
369 		uint16_t err_group;
370 
371 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED, NULL, 0, &err_rc,
372 					   &err_group);
373 #endif
374 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
375 		goto end;
376 	}
377 
378 	if (IS_ENABLED(CONFIG_MCUMGR_SMP_LEGACY_RC_BEHAVIOUR)) {
379 		if (!zcbor_tstr_put_lit(zse, "rc") || !zcbor_int32_put(zse, 0)) {
380 			img_mgmt_release_lock();
381 			return MGMT_ERR_EMSGSIZE;
382 		}
383 	}
384 
385 end:
386 	img_mgmt_release_lock();
387 
388 	return MGMT_ERR_EOK;
389 }
390 
391 static int
img_mgmt_upload_good_rsp(struct smp_streamer * ctxt)392 img_mgmt_upload_good_rsp(struct smp_streamer *ctxt)
393 {
394 	zcbor_state_t *zse = ctxt->writer->zs;
395 	bool ok = true;
396 
397 	if (IS_ENABLED(CONFIG_MCUMGR_SMP_LEGACY_RC_BEHAVIOUR)) {
398 		ok = zcbor_tstr_put_lit(zse, "rc")		&&
399 		     zcbor_int32_put(zse, MGMT_ERR_EOK);
400 	}
401 
402 	ok = ok && zcbor_tstr_put_lit(zse, "off")		&&
403 		   zcbor_size_put(zse, g_img_mgmt_state.off);
404 
405 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
406 }
407 
408 /**
409  * Logs an upload request if necessary.
410  *
411  * @param is_first	Whether the request includes the first chunk of the image.
412  * @param is_last	Whether the request includes the last chunk of the image.
413  * @param status	The result of processing the upload request (MGMT_ERR code).
414  *
415  * @return 0 on success; nonzero on failure.
416  */
417 static int
img_mgmt_upload_log(bool is_first,bool is_last,int status)418 img_mgmt_upload_log(bool is_first, bool is_last, int status)
419 {
420 	uint8_t hash[IMAGE_HASH_LEN];
421 	const uint8_t *hashp;
422 	int rc;
423 
424 	if (is_last || status != 0) {
425 		/* Log the image hash if we know it. */
426 		rc = img_mgmt_read_info(1, NULL, hash, NULL);
427 		if (rc != 0) {
428 			hashp = NULL;
429 		} else {
430 			hashp = hash;
431 		}
432 	}
433 
434 	return 0;
435 }
436 
437 /**
438  * Command handler: image upload
439  */
440 static int
img_mgmt_upload(struct smp_streamer * ctxt)441 img_mgmt_upload(struct smp_streamer *ctxt)
442 {
443 	zcbor_state_t *zse = ctxt->writer->zs;
444 	zcbor_state_t *zsd = ctxt->reader->zs;
445 	bool ok;
446 	size_t decoded = 0;
447 	struct img_mgmt_upload_req req = {
448 		.off = SIZE_MAX,
449 		.size = SIZE_MAX,
450 		.img_data = { 0 },
451 		.data_sha = { 0 },
452 		.upgrade = false,
453 		.image = 0,
454 	};
455 	int rc;
456 	struct img_mgmt_upload_action action;
457 	bool last = false;
458 	bool reset = false;
459 
460 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
461 	bool data_match = false;
462 #endif
463 
464 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK)
465 	enum mgmt_cb_return status;
466 #endif
467 
468 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK) ||	\
469 defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS) ||		\
470 defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
471 	int32_t err_rc;
472 	uint16_t err_group;
473 #endif
474 
475 	struct zcbor_map_decode_key_val image_upload_decode[] = {
476 		ZCBOR_MAP_DECODE_KEY_DECODER("image", zcbor_uint32_decode, &req.image),
477 		ZCBOR_MAP_DECODE_KEY_DECODER("data", zcbor_bstr_decode, &req.img_data),
478 		ZCBOR_MAP_DECODE_KEY_DECODER("len", zcbor_size_decode, &req.size),
479 		ZCBOR_MAP_DECODE_KEY_DECODER("off", zcbor_size_decode, &req.off),
480 		ZCBOR_MAP_DECODE_KEY_DECODER("sha", zcbor_bstr_decode, &req.data_sha),
481 		ZCBOR_MAP_DECODE_KEY_DECODER("upgrade", zcbor_bool_decode, &req.upgrade)
482 	};
483 
484 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
485 	struct mgmt_evt_op_cmd_arg cmd_status_arg = {
486 		.group = MGMT_GROUP_ID_IMAGE,
487 		.id = IMG_MGMT_ID_UPLOAD,
488 	};
489 #endif
490 
491 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK)
492 	struct img_mgmt_upload_check upload_check_data = {
493 		.action = &action,
494 		.req = &req,
495 	};
496 #endif
497 
498 	ok = zcbor_map_decode_bulk(zsd, image_upload_decode,
499 		ARRAY_SIZE(image_upload_decode), &decoded) == 0;
500 
501 	IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action, NULL);
502 
503 	if (!ok) {
504 		return MGMT_ERR_EINVAL;
505 	}
506 
507 	img_mgmt_take_lock();
508 
509 	/* Determine what actions to take as a result of this request. */
510 	rc = img_mgmt_upload_inspect(&req, &action);
511 	if (rc != 0) {
512 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
513 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED, NULL, 0, &err_rc,
514 					   &err_group);
515 #endif
516 
517 		MGMT_CTXT_SET_RC_RSN(ctxt, IMG_MGMT_UPLOAD_ACTION_RC_RSN(&action));
518 		LOG_ERR("Image upload inspect failed: %d", rc);
519 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
520 		goto end;
521 	}
522 
523 	if (!action.proceed) {
524 		/* Request specifies incorrect offset.  Respond with a success code and
525 		 * the correct offset.
526 		 */
527 		rc = img_mgmt_upload_good_rsp(ctxt);
528 		img_mgmt_release_lock();
529 		return rc;
530 	}
531 
532 #if defined(CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK)
533 	/* Request is valid.  Give the application a chance to reject this upload
534 	 * request.
535 	 */
536 	status = mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK, &upload_check_data,
537 				      sizeof(upload_check_data), &err_rc, &err_group);
538 
539 	if (status != MGMT_CB_OK) {
540 		IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action, img_mgmt_err_str_app_reject);
541 
542 		if (status == MGMT_CB_ERROR_RC) {
543 			rc = err_rc;
544 			ok = zcbor_tstr_put_lit(zse, "rc")	&&
545 			     zcbor_int32_put(zse, rc);
546 		} else {
547 			ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
548 		}
549 
550 		goto end;
551 	}
552 #endif
553 
554 	/* Remember flash area ID and image size for subsequent upload requests. */
555 	g_img_mgmt_state.area_id = action.area_id;
556 	g_img_mgmt_state.size = action.size;
557 
558 	if (req.off == 0) {
559 		/*
560 		 * New upload.
561 		 */
562 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
563 		struct flash_img_context ctx;
564 		struct flash_img_check fic;
565 #endif
566 
567 		g_img_mgmt_state.off = 0;
568 
569 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
570 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STARTED, NULL, 0, &err_rc,
571 					   &err_group);
572 #endif
573 
574 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
575 		cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_START;
576 #endif
577 
578 		/*
579 		 * We accept SHA trimmed to any length by client since it's up to client
580 		 * to make sure provided data are good enough to avoid collisions when
581 		 * resuming upload.
582 		 */
583 		g_img_mgmt_state.data_sha_len = req.data_sha.len;
584 		memcpy(g_img_mgmt_state.data_sha, req.data_sha.value, req.data_sha.len);
585 		memset(&g_img_mgmt_state.data_sha[req.data_sha.len], 0,
586 			   IMG_MGMT_DATA_SHA_LEN - req.data_sha.len);
587 
588 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
589 		/* Check if the existing image hash matches the hash of the underlying data,
590 		 * this check can only be performed if the provided hash is a full SHA256 hash
591 		 * of the file that is being uploaded, do not attempt the check if the length
592 		 * of the provided hash is less.
593 		 */
594 		if (g_img_mgmt_state.data_sha_len == IMG_MGMT_DATA_SHA_LEN) {
595 			fic.match = g_img_mgmt_state.data_sha;
596 			fic.clen = g_img_mgmt_state.size;
597 
598 			if (flash_img_check(&ctx, &fic, g_img_mgmt_state.area_id) == 0) {
599 				/* Underlying data already matches, no need to upload any more,
600 				 * set offset to image size so client knows upload has finished.
601 				 */
602 				g_img_mgmt_state.off = g_img_mgmt_state.size;
603 				reset = true;
604 				last = true;
605 				data_match = true;
606 
607 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
608 				cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_COMPLETE;
609 #endif
610 
611 				goto end;
612 			}
613 		}
614 #endif
615 
616 #ifndef CONFIG_IMG_ERASE_PROGRESSIVELY
617 		/* erase the entire req.size all at once */
618 		if (action.erase) {
619 			rc = img_mgmt_erase_image_data(0, req.size);
620 			if (rc != 0) {
621 				IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action,
622 					img_mgmt_err_str_flash_erase_failed);
623 				ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
624 				goto end;
625 			}
626 		}
627 #endif
628 	} else {
629 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
630 		cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_ONGOING;
631 #endif
632 	}
633 
634 	/* Write the image data to flash. */
635 	if (req.img_data.len != 0) {
636 		/* If this is the last chunk */
637 		if (g_img_mgmt_state.off + req.img_data.len == g_img_mgmt_state.size) {
638 			last = true;
639 		}
640 
641 		rc = img_mgmt_write_image_data(req.off, req.img_data.value, action.write_bytes,
642 						    last);
643 		if (rc == 0) {
644 			g_img_mgmt_state.off += action.write_bytes;
645 		} else {
646 			/* Write failed, currently not able to recover from this */
647 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
648 			cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_COMPLETE;
649 #endif
650 
651 			IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action,
652 				img_mgmt_err_str_flash_write_failed);
653 			reset = true;
654 			IMG_MGMT_UPLOAD_ACTION_SET_RC_RSN(&action,
655 				img_mgmt_err_str_flash_write_failed);
656 
657 			LOG_ERR("Irrecoverable error: flash write failed: %d", rc);
658 
659 			ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_IMAGE, rc);
660 			goto end;
661 		}
662 
663 		if (g_img_mgmt_state.off == g_img_mgmt_state.size) {
664 			/* Done */
665 			reset = true;
666 
667 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
668 			static struct flash_img_context ctx;
669 
670 			if (flash_img_init_id(&ctx, g_img_mgmt_state.area_id) == 0) {
671 				struct flash_img_check fic = {
672 					.match = g_img_mgmt_state.data_sha,
673 					.clen = g_img_mgmt_state.size,
674 				};
675 
676 				if (flash_img_check(&ctx, &fic, g_img_mgmt_state.area_id) == 0) {
677 					data_match = true;
678 				} else {
679 					LOG_ERR("Uploaded image sha256 hash verification failed");
680 				}
681 			} else {
682 				LOG_ERR("Uploaded image sha256 could not be checked");
683 			}
684 #endif
685 
686 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
687 			(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_PENDING, NULL, 0,
688 						   &err_rc, &err_group);
689 		} else {
690 			/* Notify that the write has completed */
691 			(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE,
692 						   NULL, 0, &err_rc, &err_group);
693 #endif
694 		}
695 	}
696 end:
697 
698 	img_mgmt_upload_log(req.off == 0, g_img_mgmt_state.off == g_img_mgmt_state.size, rc);
699 
700 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
701 	(void)mgmt_callback_notify(MGMT_EVT_OP_CMD_STATUS, &cmd_status_arg,
702 				   sizeof(cmd_status_arg), &err_rc, &err_group);
703 #endif
704 
705 	if (rc != 0) {
706 #if defined(CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS)
707 		(void)mgmt_callback_notify(MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED, NULL, 0, &err_rc,
708 					   &err_group);
709 #endif
710 
711 		img_mgmt_reset_upload();
712 	} else {
713 		rc = img_mgmt_upload_good_rsp(ctxt);
714 
715 #ifdef CONFIG_IMG_ENABLE_IMAGE_CHECK
716 		if (last && rc == MGMT_ERR_EOK) {
717 			/* Append status to last packet */
718 			ok = zcbor_tstr_put_lit(zse, "match")	&&
719 			     zcbor_bool_put(zse, data_match);
720 		}
721 #endif
722 
723 		if (reset) {
724 			/* Reset the upload state struct back to default */
725 			img_mgmt_reset_upload();
726 		}
727 	}
728 
729 	img_mgmt_release_lock();
730 
731 	if (!ok) {
732 		return MGMT_ERR_EMSGSIZE;
733 	}
734 
735 	return MGMT_ERR_EOK;
736 }
737 
img_mgmt_my_version(struct image_version * ver)738 int img_mgmt_my_version(struct image_version *ver)
739 {
740 	return img_mgmt_read_info(img_mgmt_active_slot(img_mgmt_active_image()),
741 				  ver, NULL, NULL);
742 }
743 
744 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
745 /*
746  * @brief	Translate IMG mgmt group error code into MCUmgr error code
747  *
748  * @param ret	#img_mgmt_err_code_t error code
749  *
750  * @return	#mcumgr_err_t error code
751  */
img_mgmt_translate_error_code(uint16_t err)752 static int img_mgmt_translate_error_code(uint16_t err)
753 {
754 	int rc;
755 
756 	switch (err) {
757 	case IMG_MGMT_ERR_NO_IMAGE:
758 	case IMG_MGMT_ERR_NO_TLVS:
759 		rc = MGMT_ERR_ENOENT;
760 		break;
761 
762 	case IMG_MGMT_ERR_NO_FREE_SLOT:
763 	case IMG_MGMT_ERR_CURRENT_VERSION_IS_NEWER:
764 	case IMG_MGMT_ERR_IMAGE_ALREADY_PENDING:
765 		rc = MGMT_ERR_EBADSTATE;
766 		break;
767 
768 	case IMG_MGMT_ERR_NO_FREE_MEMORY:
769 		rc = MGMT_ERR_ENOMEM;
770 		break;
771 
772 	case IMG_MGMT_ERR_INVALID_SLOT:
773 	case IMG_MGMT_ERR_INVALID_PAGE_OFFSET:
774 	case IMG_MGMT_ERR_INVALID_OFFSET:
775 	case IMG_MGMT_ERR_INVALID_LENGTH:
776 	case IMG_MGMT_ERR_INVALID_IMAGE_HEADER:
777 	case IMG_MGMT_ERR_INVALID_HASH:
778 	case IMG_MGMT_ERR_INVALID_FLASH_ADDRESS:
779 		rc = MGMT_ERR_EINVAL;
780 		break;
781 
782 	case IMG_MGMT_ERR_FLASH_CONFIG_QUERY_FAIL:
783 	case IMG_MGMT_ERR_VERSION_GET_FAILED:
784 	case IMG_MGMT_ERR_TLV_MULTIPLE_HASHES_FOUND:
785 	case IMG_MGMT_ERR_TLV_INVALID_SIZE:
786 	case IMG_MGMT_ERR_HASH_NOT_FOUND:
787 	case IMG_MGMT_ERR_INVALID_TLV:
788 	case IMG_MGMT_ERR_FLASH_OPEN_FAILED:
789 	case IMG_MGMT_ERR_FLASH_READ_FAILED:
790 	case IMG_MGMT_ERR_FLASH_WRITE_FAILED:
791 	case IMG_MGMT_ERR_FLASH_ERASE_FAILED:
792 	case IMG_MGMT_ERR_FLASH_CONTEXT_ALREADY_SET:
793 	case IMG_MGMT_ERR_FLASH_CONTEXT_NOT_SET:
794 	case IMG_MGMT_ERR_FLASH_AREA_DEVICE_NULL:
795 	case IMG_MGMT_ERR_INVALID_IMAGE_HEADER_MAGIC:
796 	case IMG_MGMT_ERR_INVALID_IMAGE_VECTOR_TABLE:
797 	case IMG_MGMT_ERR_INVALID_IMAGE_TOO_LARGE:
798 	case IMG_MGMT_ERR_INVALID_IMAGE_DATA_OVERRUN:
799 	case IMG_MGMT_ERR_UNKNOWN:
800 	default:
801 		rc = MGMT_ERR_EUNKNOWN;
802 	}
803 
804 	return rc;
805 }
806 #endif
807 
808 static const struct mgmt_handler img_mgmt_handlers[] = {
809 	[IMG_MGMT_ID_STATE] = {
810 		.mh_read = img_mgmt_state_read,
811 #ifdef CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP
812 		.mh_write = NULL
813 #else
814 		.mh_write = img_mgmt_state_write,
815 #endif
816 	},
817 	[IMG_MGMT_ID_UPLOAD] = {
818 		.mh_read = NULL,
819 		.mh_write = img_mgmt_upload
820 	},
821 	[IMG_MGMT_ID_ERASE] = {
822 		.mh_read = NULL,
823 		.mh_write = img_mgmt_erase
824 	},
825 };
826 
827 static const struct mgmt_handler img_mgmt_handlers[];
828 
829 #define IMG_MGMT_HANDLER_CNT ARRAY_SIZE(img_mgmt_handlers)
830 
831 static struct mgmt_group img_mgmt_group = {
832 	.mg_handlers = (struct mgmt_handler *)img_mgmt_handlers,
833 	.mg_handlers_count = IMG_MGMT_HANDLER_CNT,
834 	.mg_group_id = MGMT_GROUP_ID_IMAGE,
835 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
836 	.mg_translate_error = img_mgmt_translate_error_code,
837 #endif
838 };
839 
img_mgmt_register_group(void)840 static void img_mgmt_register_group(void)
841 {
842 	mgmt_register_group(&img_mgmt_group);
843 }
844 
845 MCUMGR_HANDLER_DEFINE(img_mgmt, img_mgmt_register_group);
846