1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <limits.h>
21 #include <assert.h>
22 #include <string.h>
23 
24 #include "cborattr/cborattr.h"
25 #include "mgmt/mgmt.h"
26 
27 #include "img_mgmt/image.h"
28 #include "img_mgmt/img_mgmt.h"
29 #include "img_mgmt/img_mgmt_impl.h"
30 #include "img_mgmt_priv.h"
31 #include "img_mgmt/img_mgmt_config.h"
32 
33 static mgmt_handler_fn img_mgmt_upload;
34 static mgmt_handler_fn img_mgmt_erase;
35 static img_mgmt_upload_fn *img_mgmt_upload_cb;
36 static void *img_mgmt_upload_arg;
37 
38 const img_mgmt_dfu_callbacks_t *img_mgmt_dfu_callbacks_fn;
39 
40 struct img_mgmt_state g_img_mgmt_state;
41 
42 static const struct mgmt_handler img_mgmt_handlers[] = {
43     [IMG_MGMT_ID_STATE] = {
44         .mh_read = img_mgmt_state_read,
45         .mh_write = img_mgmt_state_write,
46     },
47     [IMG_MGMT_ID_UPLOAD] = {
48         .mh_read = NULL,
49         .mh_write = img_mgmt_upload
50     },
51     [IMG_MGMT_ID_ERASE] = {
52         .mh_read = NULL,
53         .mh_write = img_mgmt_erase
54     },
55 };
56 
57 #define IMG_MGMT_HANDLER_CNT \
58     sizeof(img_mgmt_handlers) / sizeof(img_mgmt_handlers[0])
59 
60 static struct mgmt_group img_mgmt_group = {
61     .mg_handlers = (struct mgmt_handler *)img_mgmt_handlers,
62     .mg_handlers_count = IMG_MGMT_HANDLER_CNT,
63     .mg_group_id = MGMT_GROUP_ID_IMAGE,
64 };
65 
66 #if IMG_MGMT_VERBOSE_ERR
67 const char *img_mgmt_err_str_app_reject = "app reject";
68 const char *img_mgmt_err_str_hdr_malformed = "header malformed";
69 const char *img_mgmt_err_str_magic_mismatch = "magic mismatch";
70 const char *img_mgmt_err_str_no_slot = "no slot";
71 const char *img_mgmt_err_str_flash_open_failed = "fa open fail";
72 const char *img_mgmt_err_str_flash_erase_failed = "fa erase fail";
73 const char *img_mgmt_err_str_flash_write_failed = "fa write fail";
74 const char *img_mgmt_err_str_downgrade = "downgrade";
75 const char *img_mgmt_err_str_image_bad_flash_addr = "img addr mismatch";
76 #endif
77 
78 /**
79  * Finds the TLVs in the specified image slot, if any.
80  */
81 static int
img_mgmt_find_tlvs(int slot,size_t * start_off,size_t * end_off,uint16_t magic)82 img_mgmt_find_tlvs(int slot, size_t *start_off, size_t *end_off,
83                    uint16_t magic)
84 {
85     struct image_tlv_info tlv_info;
86     int rc;
87 
88     rc = img_mgmt_impl_read(slot, *start_off, &tlv_info, sizeof tlv_info);
89     if (rc != 0) {
90         /* Read error. */
91         return MGMT_ERR_EUNKNOWN;
92     }
93 
94     if (tlv_info.it_magic != magic) {
95         /* No TLVs. */
96         return MGMT_ERR_ENOENT;
97     }
98 
99     *start_off += sizeof tlv_info;
100     *end_off = *start_off + tlv_info.it_tlv_tot;
101 
102     return 0;
103 }
104 
105 /*
106  * Reads the version and build hash from the specified image slot.
107  */
108 int
img_mgmt_read_info(int image_slot,struct image_version * ver,uint8_t * hash,uint32_t * flags)109 img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
110                    uint32_t *flags)
111 {
112 
113 #if IMG_MGMT_DUMMY_HDR
114     uint8_t dummy_hash[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
115                             0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
116 
117     if (!hash && !ver && !flags) {
118         return 0;
119     }
120 
121     if (hash) {
122         memcpy(hash, dummy_hash, IMG_MGMT_HASH_LEN);
123     }
124 
125     if (ver) {
126         memset(ver, 0xff, sizeof *ver);
127     }
128 
129     if (flags) {
130         *flags = 0;
131     }
132 
133     return 0;
134 #endif
135 
136     struct image_header hdr;
137     struct image_tlv tlv;
138     size_t data_off;
139     size_t data_end;
140     bool hash_found;
141     uint8_t erased_val;
142     uint32_t erased_val_32;
143     int rc;
144 
145     rc = img_mgmt_impl_erased_val(image_slot, &erased_val);
146     if (rc != 0) {
147         return MGMT_ERR_EUNKNOWN;
148     }
149 
150     rc = img_mgmt_impl_read(image_slot, 0, &hdr, sizeof hdr);
151     if (rc != 0) {
152         return MGMT_ERR_EUNKNOWN;
153     }
154 
155     if (ver != NULL) {
156         memset(ver, erased_val, sizeof(*ver));
157     }
158     erased_val_32 = ERASED_VAL_32(erased_val);
159     if (hdr.ih_magic == IMAGE_MAGIC) {
160         if (ver != NULL) {
161             memcpy(ver, &hdr.ih_ver, sizeof(*ver));
162         }
163     } else if (hdr.ih_magic == erased_val_32) {
164         return MGMT_ERR_ENOENT;
165     } else {
166         return MGMT_ERR_EUNKNOWN;
167     }
168 
169     if (flags != NULL) {
170         *flags = hdr.ih_flags;
171     }
172 
173     /* Read the image's TLVs. We first try to find the protected TLVs, if the protected
174      * TLV does not exist, we try to find non-protected TLV which also contains the hash
175      * TLV. All images are required to have a hash TLV.  If the hash is missing, the image
176      * is considered invalid.
177      */
178     data_off = hdr.ih_hdr_size + hdr.ih_img_size;
179 
180     rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_PROT_INFO_MAGIC);
181     if (!rc) {
182         /* The data offset should start after the header bytes after the end of the protected TLV,
183          * if one exists.
184          */
185         data_off = data_end - sizeof(struct image_tlv_info);
186     }
187 
188     rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_INFO_MAGIC);
189     if (rc != 0) {
190         return MGMT_ERR_EUNKNOWN;
191     }
192 
193     hash_found = false;
194     while (data_off + sizeof tlv <= data_end) {
195         rc = img_mgmt_impl_read(image_slot, data_off, &tlv, sizeof tlv);
196         if (rc != 0) {
197             return MGMT_ERR_EUNKNOWN;
198         }
199         if (tlv.it_type == 0xff && tlv.it_len == 0xffff) {
200             return MGMT_ERR_EUNKNOWN;
201         }
202         if (tlv.it_type != IMAGE_TLV_SHA256 || tlv.it_len != IMAGE_HASH_LEN) {
203             /* Non-hash TLV.  Skip it. */
204             data_off += sizeof tlv + tlv.it_len;
205             continue;
206         }
207 
208         if (hash_found) {
209             /* More than one hash. */
210             return MGMT_ERR_EUNKNOWN;
211         }
212         hash_found = true;
213 
214         data_off += sizeof tlv;
215         if (hash != NULL) {
216             if (data_off + IMAGE_HASH_LEN > data_end) {
217                 return MGMT_ERR_EUNKNOWN;
218             }
219             rc = img_mgmt_impl_read(image_slot, data_off, hash,
220                                     IMAGE_HASH_LEN);
221             if (rc != 0) {
222                 return MGMT_ERR_EUNKNOWN;
223             }
224         }
225     }
226 
227     if (!hash_found) {
228         return MGMT_ERR_EUNKNOWN;
229     }
230 
231     return 0;
232 }
233 
234 /*
235  * Finds image given version number. Returns the slot number image is in,
236  * or -1 if not found.
237  */
238 int
img_mgmt_find_by_ver(struct image_version * find,uint8_t * hash)239 img_mgmt_find_by_ver(struct image_version *find, uint8_t *hash)
240 {
241     int i;
242     struct image_version ver;
243 
244     for (i = 0; i < 2; i++) {
245         if (img_mgmt_read_info(i, &ver, hash, NULL) != 0) {
246             continue;
247         }
248         if (!memcmp(find, &ver, sizeof(ver))) {
249             return i;
250         }
251     }
252     return -1;
253 }
254 
255 /*
256  * Finds image given hash of the image. Returns the slot number image is in,
257  * or -1 if not found.
258  */
259 int
img_mgmt_find_by_hash(uint8_t * find,struct image_version * ver)260 img_mgmt_find_by_hash(uint8_t *find, struct image_version *ver)
261 {
262     int i;
263     uint8_t hash[IMAGE_HASH_LEN];
264 
265     for (i = 0; i < 2; i++) {
266         if (img_mgmt_read_info(i, ver, hash, NULL) != 0) {
267             continue;
268         }
269         if (!memcmp(hash, find, IMAGE_HASH_LEN)) {
270             return i;
271         }
272     }
273     return -1;
274 }
275 
276 #if IMG_MGMT_VERBOSE_ERR
277 int
img_mgmt_error_rsp(struct mgmt_ctxt * ctxt,int rc,const char * rsn)278 img_mgmt_error_rsp(struct mgmt_ctxt *ctxt, int rc, const char *rsn)
279 {
280     /*
281      * This is an error response so returning a different error when failed to
282      * encode other error probably does not make much sense - just ignore errors
283      * here.
284      */
285     cbor_encode_text_stringz(&ctxt->encoder, "rsn");
286     cbor_encode_text_stringz(&ctxt->encoder, rsn);
287     return rc;
288 }
289 #endif
290 
291 /*
292  * Resets upload status to defaults (no upload in progress)
293  */
img_mgmt_reset_upload(void)294 void img_mgmt_reset_upload(void)
295 {
296     memset(&g_img_mgmt_state, 0, sizeof(g_img_mgmt_state));
297     g_img_mgmt_state.area_id = -1;
298 }
299 
300 /**
301  * Command handler: image erase
302  */
303 static int
img_mgmt_erase(struct mgmt_ctxt * ctxt)304 img_mgmt_erase(struct mgmt_ctxt *ctxt)
305 {
306     struct image_version ver;
307     CborError err;
308     int rc;
309 
310     /*
311      * First check if image info is valid.
312      * This check is done incase the flash area has a corrupted image.
313      */
314     rc = img_mgmt_read_info(1, &ver, NULL, NULL);
315 
316     if (rc == 0) {
317         /* Image info is valid. */
318         if (img_mgmt_slot_in_use(1)) {
319             /* No free slot. */
320             return MGMT_ERR_EBADSTATE;
321         }
322     }
323 
324     rc = img_mgmt_impl_erase_slot();
325     img_mgmt_reset_upload();
326 
327     if (!rc) {
328         img_mgmt_dfu_stopped();
329     }
330 
331     err = 0;
332     err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
333     err |= cbor_encode_int(&ctxt->encoder, rc);
334 
335     if (err != 0) {
336         return MGMT_ERR_ENOMEM;
337     }
338 
339     return 0;
340 }
341 
342 static int
img_mgmt_upload_good_rsp(struct mgmt_ctxt * ctxt)343 img_mgmt_upload_good_rsp(struct mgmt_ctxt *ctxt)
344 {
345     CborError err = CborNoError;
346 
347     err |= cbor_encode_text_stringz(&ctxt->encoder, "rc");
348     err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK);
349     err |= cbor_encode_text_stringz(&ctxt->encoder, "off");
350     err |= cbor_encode_int(&ctxt->encoder, g_img_mgmt_state.off);
351 
352     if (err != 0) {
353         return MGMT_ERR_ENOMEM;
354     }
355 
356     return 0;
357 }
358 
359 /**
360  * Logs an upload request if necessary.
361  *
362  * @param is_first              Whether the request includes the first chunk of
363  *                                  the image.
364  * @param is_last               Whether the request includes the last chunk of
365  *                                  the image.
366  * @param status                The result of processing the upload request
367  *                                  (MGMT_ERR code).
368  *
369  * @return                      0 on success; nonzero on failure.
370  */
371 static int
img_mgmt_upload_log(bool is_first,bool is_last,int status)372 img_mgmt_upload_log(bool is_first, bool is_last, int status)
373 {
374     uint8_t hash[IMAGE_HASH_LEN];
375     const uint8_t *hashp;
376     int rc;
377 
378     if (is_first) {
379         return img_mgmt_impl_log_upload_start(status);
380     }
381 
382     if (is_last || status != 0) {
383         /* Log the image hash if we know it. */
384         rc = img_mgmt_read_info(1, NULL, hash, NULL);
385         if (rc != 0) {
386             hashp = NULL;
387         } else {
388             hashp = hash;
389         }
390 
391         return img_mgmt_impl_log_upload_done(status, hashp);
392     }
393 
394     /* Nothing to log. */
395     return 0;
396 }
397 
398 /**
399  * Command handler: image upload
400  */
401 static int
img_mgmt_upload(struct mgmt_ctxt * ctxt)402 img_mgmt_upload(struct mgmt_ctxt *ctxt)
403 {
404     struct mgmt_evt_op_cmd_status_arg cmd_status_arg;
405     struct img_mgmt_upload_req req = {
406         .off = -1,
407         .size = -1,
408         .data_len = 0,
409         .data_sha_len = 0,
410         .upgrade = false,
411         .image = 0,
412     };
413     bool reset = false;
414 
415     const struct cbor_attr_t off_attr[] = {
416         [0] = {
417             .attribute = "image",
418             .type = CborAttrUnsignedIntegerType,
419             .addr.uinteger = &req.image,
420             .nodefault = true
421         },
422         [1] = {
423             .attribute = "data",
424             .type = CborAttrByteStringType,
425             .addr.bytestring.data = req.img_data,
426             .addr.bytestring.len = &req.data_len,
427             .len = sizeof(req.img_data)
428         },
429         [2] = {
430             .attribute = "len",
431             .type = CborAttrUnsignedIntegerType,
432             .addr.uinteger = &req.size,
433             .nodefault = true
434         },
435         [3] = {
436             .attribute = "off",
437             .type = CborAttrUnsignedIntegerType,
438             .addr.uinteger = &req.off,
439             .nodefault = true
440         },
441         [4] = {
442             .attribute = "sha",
443             .type = CborAttrByteStringType,
444             .addr.bytestring.data = req.data_sha,
445             .addr.bytestring.len = &req.data_sha_len,
446             .len = sizeof(req.data_sha)
447         },
448         [5] = {
449             .attribute = "upgrade",
450             .type = CborAttrBooleanType,
451             .addr.boolean = &req.upgrade,
452             .dflt.boolean = false,
453         },
454         [6] = { 0 },
455     };
456     int rc;
457     const char *errstr = NULL;
458     struct img_mgmt_upload_action action;
459     bool last = false;
460 
461     rc = cbor_read_object(&ctxt->it, off_attr);
462     if (rc != 0) {
463         return MGMT_ERR_EINVAL;
464     }
465 
466     /* Determine what actions to take as a result of this request. */
467     rc = img_mgmt_impl_upload_inspect(&req, &action, &errstr);
468     if (rc != 0) {
469         img_mgmt_dfu_stopped();
470         return rc;
471     }
472 
473     if (!action.proceed) {
474         /* Request specifies incorrect offset.  Respond with a success code and
475          * the correct offset.
476          */
477         return img_mgmt_upload_good_rsp(ctxt);
478     }
479 
480     /* Request is valid.  Give the application a chance to reject this upload
481      * request.
482      */
483     if (img_mgmt_upload_cb != NULL) {
484         rc = img_mgmt_upload_cb(req.off, action.size, img_mgmt_upload_arg);
485         if (rc != 0) {
486             errstr = img_mgmt_err_str_app_reject;
487             goto end;
488         }
489     }
490 
491     /* Remember flash area ID and image size for subsequent upload requests. */
492     g_img_mgmt_state.area_id = action.area_id;
493     g_img_mgmt_state.size = action.size;
494 
495     if (req.off == 0) {
496         /*
497          * New upload.
498          */
499         g_img_mgmt_state.off = 0;
500 
501         img_mgmt_dfu_started();
502         cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_START;
503 
504         /*
505          * We accept SHA trimmed to any length by client since it's up to client
506          * to make sure provided data are good enough to avoid collisions when
507          * resuming upload.
508          */
509         g_img_mgmt_state.data_sha_len = req.data_sha_len;
510         memcpy(g_img_mgmt_state.data_sha, req.data_sha, req.data_sha_len);
511         memset(&g_img_mgmt_state.data_sha[req.data_sha_len], 0,
512                IMG_MGMT_DATA_SHA_LEN - req.data_sha_len);
513 
514 #if IMG_MGMT_LAZY_ERASE
515         /* setup for lazy sector by sector erase */
516         g_img_mgmt_state.sector_id = -1;
517         g_img_mgmt_state.sector_end = 0;
518 #else
519         /* erase the entire req.size all at once */
520         if (action.erase) {
521             rc = img_mgmt_impl_erase_image_data(0, req.size);
522             if (rc != 0) {
523                 rc = MGMT_ERR_EUNKNOWN;
524                 errstr = img_mgmt_err_str_flash_erase_failed;
525                 goto end;
526             }
527         }
528 #endif
529     } else {
530         cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_ONGOING;
531     }
532 
533     /* Write the image data to flash. */
534     if (req.data_len != 0) {
535 #if IMG_MGMT_LAZY_ERASE
536         /* erase as we cross sector boundaries */
537         if (img_mgmt_impl_erase_if_needed(req.off, action.write_bytes) != 0) {
538             rc = MGMT_ERR_EUNKNOWN;
539             errstr = img_mgmt_err_str_flash_erase_failed;
540             reset = true;
541             goto end;
542         }
543 #endif
544         /* If this is the last chunk */
545         if (g_img_mgmt_state.off + req.data_len == g_img_mgmt_state.size) {
546             last = true;
547         }
548 
549         rc = img_mgmt_impl_write_image_data(req.off, req.img_data, action.write_bytes, last);
550         if (rc != 0) {
551             rc = MGMT_ERR_EUNKNOWN;
552             errstr = img_mgmt_err_str_flash_write_failed;
553             reset = true;
554             goto end;
555         } else {
556             g_img_mgmt_state.off += action.write_bytes;
557             if (g_img_mgmt_state.off == g_img_mgmt_state.size) {
558                 /* Done */
559                 img_mgmt_dfu_pending();
560                 cmd_status_arg.status = IMG_MGMT_ID_UPLOAD_STATUS_COMPLETE;
561                 g_img_mgmt_state.area_id = -1;
562             }
563         }
564     }
565 
566 end:
567 
568     img_mgmt_upload_log(req.off == 0, g_img_mgmt_state.off == g_img_mgmt_state.size, rc);
569     mgmt_evt(MGMT_EVT_OP_CMD_STATUS, MGMT_GROUP_ID_IMAGE, IMG_MGMT_ID_UPLOAD,
570              &cmd_status_arg);
571 
572     if (reset == true || rc != 0) {
573         /* Reset the upload state struct back to default */
574         img_mgmt_reset_upload();
575     }
576 
577     if (rc != 0) {
578         img_mgmt_dfu_stopped();
579         return img_mgmt_error_rsp(ctxt, rc, errstr);
580     }
581 
582     return img_mgmt_upload_good_rsp(ctxt);
583 }
584 
585 void
img_mgmt_dfu_stopped(void)586 img_mgmt_dfu_stopped(void)
587 {
588     if (img_mgmt_dfu_callbacks_fn && img_mgmt_dfu_callbacks_fn->dfu_stopped_cb) {
589         img_mgmt_dfu_callbacks_fn->dfu_stopped_cb();
590     }
591 }
592 
593 void
img_mgmt_dfu_started(void)594 img_mgmt_dfu_started(void)
595 {
596     if (img_mgmt_dfu_callbacks_fn && img_mgmt_dfu_callbacks_fn->dfu_started_cb) {
597         img_mgmt_dfu_callbacks_fn->dfu_started_cb();
598     }
599 }
600 
601 void
img_mgmt_dfu_pending(void)602 img_mgmt_dfu_pending(void)
603 {
604     if (img_mgmt_dfu_callbacks_fn && img_mgmt_dfu_callbacks_fn->dfu_pending_cb) {
605         img_mgmt_dfu_callbacks_fn->dfu_pending_cb();
606     }
607 }
608 
609 void
img_mgmt_dfu_confirmed(void)610 img_mgmt_dfu_confirmed(void)
611 {
612     if (img_mgmt_dfu_callbacks_fn && img_mgmt_dfu_callbacks_fn->dfu_confirmed_cb) {
613         img_mgmt_dfu_callbacks_fn->dfu_confirmed_cb();
614     }
615 }
616 
617 void
img_mgmt_set_upload_cb(img_mgmt_upload_fn * cb,void * arg)618 img_mgmt_set_upload_cb(img_mgmt_upload_fn *cb, void *arg)
619 {
620     img_mgmt_upload_cb = cb;
621     img_mgmt_upload_arg = arg;
622 }
623 
624 void
img_mgmt_register_callbacks(const img_mgmt_dfu_callbacks_t * cb_struct)625 img_mgmt_register_callbacks(const img_mgmt_dfu_callbacks_t *cb_struct)
626 {
627     img_mgmt_dfu_callbacks_fn = cb_struct;
628 }
629 
630 
631 int
img_mgmt_my_version(struct image_version * ver)632 img_mgmt_my_version(struct image_version *ver)
633 {
634     return img_mgmt_read_info(IMG_MGMT_BOOT_CURR_SLOT, ver, NULL, NULL);
635 }
636 
637 void
img_mgmt_register_group(void)638 img_mgmt_register_group(void)
639 {
640     mgmt_register_group(&img_mgmt_group);
641 }
642 
643 void
img_mgmt_unregister_group(void)644 img_mgmt_unregister_group(void)
645 {
646     mgmt_unregister_group(&img_mgmt_group);
647 }
648