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