This is an automated email from the ASF dual-hosted git repository. ccollins pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-mcumgr.git
The following commit(s) were added to refs/heads/master by this push: new 36a8312 image management 36a8312 is described below commit 36a83123e844f7d1fd25663d4966261923614a5e Author: Christopher Collins <ccoll...@apache.org> AuthorDate: Tue Jan 2 19:14:27 2018 -0800 image management --- img/include/img/image.h | 83 +++++++++ img/include/img/img.h | 77 ++++++++ img/include/img/img_impl.h | 73 ++++++++ img/port/zephyr/src/zephyr_img.c | 202 +++++++++++++++++++++ img/src/img.c | 379 +++++++++++++++++++++++++++++++++++++++ img/src/img_priv.h | 105 +++++++++++ img/src/img_state.c | 295 ++++++++++++++++++++++++++++++ img/src/img_util.c | 92 ++++++++++ 8 files changed, 1306 insertions(+) diff --git a/img/include/img/image.h b/img/include/img/image.h new file mode 100644 index 0000000..53c6215 --- /dev/null +++ b/img/include/img/image.h @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_IMAGE_ +#define H_IMAGE_ + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMAGE_MAGIC 0x96f3b83d +#define IMAGE_TLV_INFO_MAGIC 0x6907 + +#define IMAGE_HEADER_SIZE 32 + +/* + * Image header flags. + */ +#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */ + +/* + * Image trailer TLV types. + */ +#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ + +struct image_version { + uint8_t iv_major; + uint8_t iv_minor; + uint16_t iv_revision; + uint32_t iv_build_num; +}; + +/** Image header. All fields are in little endian byte order. */ +struct image_header { + uint32_t ih_magic; + uint32_t ih_load_addr; + uint16_t ih_hdr_size; /* Size of image header (bytes). */ + uint16_t _pad2; + uint32_t ih_img_size; /* Does not include header. */ + uint32_t ih_flags; /* IMAGE_F_[...]. */ + struct image_version ih_ver; + uint32_t _pad3; +}; + +/** Image TLV header. All fields in little endian. */ +struct image_tlv_info { + uint16_t it_magic; + uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */ +}; + +/** Image trailer TLV format. All fields in little endian. */ +struct image_tlv { + uint8_t it_type; /* IMAGE_TLV_[...]. */ + uint8_t _pad; + uint16_t it_len; /* Data length (not including TLV header). */ +}; + +_Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE, + "struct image_header not required size"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/img/include/img/img.h b/img/include/img/img.h new file mode 100644 index 0000000..fa5a3cb --- /dev/null +++ b/img/include/img/img.h @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_IMG_ +#define H_IMG_ + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMG_NMGR_ID_STATE 0 +#define IMG_NMGR_ID_UPLOAD 1 +#define IMG_NMGR_ID_FILE 2 +#define IMG_NMGR_ID_CORELIST 3 +#define IMG_NMGR_ID_CORELOAD 4 +#define IMG_NMGR_ID_ERASE 5 + +#define IMG_NMGR_MAX_NAME 64 +#define IMG_NMGR_MAX_VER 25 /* 255.255.65535.4294967295\0 */ + +#define IMG_HASH_LEN 32 + +#define IMG_STATE_F_PENDING 0x01 +#define IMG_STATE_F_CONFIRMED 0x02 +#define IMG_STATE_F_ACTIVE 0x04 +#define IMG_STATE_F_PERMANENT 0x08 + +#define IMG_SWAP_TYPE_NONE 0 +#define IMG_SWAP_TYPE_TEST 1 +#define IMG_SWAP_TYPE_PERM 2 +#define IMG_SWAP_TYPE_REVERT 3 + +struct image_version; + +/* + * Take version and convert it to string in dst. + */ +int img_ver_str(const struct image_version *ver, char *dst); + +/* + * Given flash_map slot id, read in image_version and/or image hash. + */ +int img_read_info(int image_slot, struct image_version *ver, uint8_t *hash, + uint32_t *flags); + +/* + * Returns version number of current image (if available). + */ +int img_my_version(struct image_version *ver); + +uint8_t img_state_flags(int query_slot); +int img_state_slot_in_use(int slot); +int img_group_register(void); + +#ifdef __cplusplus +} +#endif + +#endif /* H_IMG_ */ diff --git a/img/include/img/img_impl.h b/img/include/img/img_impl.h new file mode 100644 index 0000000..f634110 --- /dev/null +++ b/img/include/img/img_impl.h @@ -0,0 +1,73 @@ +#ifndef H_IMG_IMPL_ +#define H_IMG_IMPL_ + +/* This file declares implementation-specific functions required by image + * management. Each function must be defined in a way that is compatible with + * the host OS. + */ + +/** + * @brief Ensures the spare slot (slot 1) is fully erased. + * + * @return 0 on success, MGMT_ERR_[...] code on failure. + */ +int img_impl_erase_slot(void); + +/** + * @brief Marks the image in slot 1 as pending. On the next reboot, the system + * will perform a boot of the slot 1 image. + * + * @param permanent Whether the image should be used permanently or + * only tested once: + * 0=run image once, then confirm or revert. + * 1=run image forever. + * + * @return 0 on success, MGMT_ERR_[...] code on failure. + */ +int img_impl_write_pending(bool permanent); + +/** + * @brief Marks the image in slot 0 as confirmed. The system will continue + * booting into the image in slot 0 until told to boot from a different slot. + * + * @return 0 on success, MGMT_ERR_[...] code on failure. + */ +int img_impl_write_confirmed(void); + +/** + * @brief Reads the specified chunk of data from an image slot. + * + * @param slot The index of the slot to read from. + * @param offset The offset within the slot to read from. + * @param dst On success, the read data gets written here. + * @param num_bytes The number of byets to read. + * + * @return 0 on success, MGMT_ERR_[...] code on failure. + */ +int img_impl_read(int slot, unsigned int offset, void *dst, + unsigned int num_bytes); + +/** + * @brief Writes the specified chunk of image data to slot 1. + * + * @param offset The offset within slot 1 to write to. + * @param data The image data to write. + * @param num_bytes The number of bytes to read. + * @param last Whether this chunk is the end of the image: + * false=additional image chunks are forthcoming + * true=last image chunk; flush unwritten data to disk + * + * @return 0 on success, MGMT_ERR_[...] code on failure. + */ +int img_impl_write_image_data(unsigned int offset, const void *data, + unsigned int num_bytes, bool last); + +/** + * @brief Indicates the type of swap operation that will occur on the next + * reboot, if any. + * + * @return an IMG_SWAP_TYPE_[...] code. + */ +int img_impl_swap_type(void); + +#endif diff --git a/img/port/zephyr/src/zephyr_img.c b/img/port/zephyr/src/zephyr_img.c new file mode 100644 index 0000000..f99d5fe --- /dev/null +++ b/img/port/zephyr/src/zephyr_img.c @@ -0,0 +1,202 @@ +#include <assert.h> +#include <flash.h> +#include <zephyr.h> +#include <soc.h> +#include <dfu/mcuboot.h> +#include <dfu/flash_img.h> +#include "mgmt/mgmt.h" +#include "img/img_impl.h" +#include "img/img.h" + +static struct device *zephyr_img_flash_dev; +static struct flash_img_context zephyr_img_flash_ctxt; + +static void +img_impl_init_flash(void) +{ + if (zephyr_img_flash_dev == NULL) { + zephyr_img_flash_dev = device_get_binding(FLASH_DRIVER_NAME); + } +} + +static int +img_impl_flash_check_empty(off_t offset, size_t size, bool *out_empty) +{ + uint32_t data[16]; + off_t addr; + off_t end; + int bytes_to_read; + int rc; + int i; + + assert(size % 4 == 0); + + end = offset + size; + for (addr = offset; addr < end; addr += sizeof data) { + if (end - addr < sizeof data) { + bytes_to_read = end - addr; + } else { + bytes_to_read = sizeof data; + } + + rc = flash_read(zephyr_img_flash_dev, addr, data, bytes_to_read); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + for (i = 0; i < bytes_to_read / 4; i++) { + if (data[i] != 0xffffffff) { + *out_empty = false; + return 0; + } + } + } + + *out_empty = true; + return 0; +} + +static off_t +img_impl_abs_offset(int slot, off_t sub_offset) +{ + off_t slot_start; + + switch (slot) { + case 0: + slot_start = FLASH_AREA_IMAGE_0_OFFSET; + break; + + case 1: + slot_start = FLASH_AREA_IMAGE_1_OFFSET; + break; + + default: + assert(0); + slot_start = FLASH_AREA_IMAGE_1_OFFSET; + break; + } + + return slot_start + sub_offset; +} + +int +img_impl_erase_slot(void) +{ + bool empty; + int rc; + + img_impl_init_flash(); + + rc = img_impl_flash_check_empty(FLASH_AREA_IMAGE_1_OFFSET, + FLASH_AREA_IMAGE_1_SIZE, + &empty); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + if (!empty) { + rc = boot_erase_img_bank(FLASH_AREA_IMAGE_1_OFFSET); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + } + + return 0; +} + +int +img_impl_write_pending(bool permanent) +{ + int rc; + + img_impl_init_flash(); + + rc = boot_request_upgrade(permanent); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + return 0; +} + +int +img_impl_write_confirmed(void) +{ + int rc; + + img_impl_init_flash(); + + rc = boot_write_img_confirmed(); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + return 0; +} + +int +img_impl_read(int slot, unsigned int offset, void *dst, + unsigned int num_bytes) +{ + off_t abs_offset; + int rc; + + img_impl_init_flash(); + + abs_offset = img_impl_abs_offset(slot, offset); + rc = flash_read(zephyr_img_flash_dev, abs_offset, dst, num_bytes); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + return 0; +} + +int +img_impl_write_image_data(unsigned int offset, const void *data, + unsigned int num_bytes, bool last) +{ + int rc; + + img_impl_init_flash(); + + if (offset == 0) { + flash_img_init(&zephyr_img_flash_ctxt, zephyr_img_flash_dev); + } + + /* Cast away const. */ + rc = flash_img_buffered_write(&zephyr_img_flash_ctxt, (void *)data, + num_bytes, false); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + if (last) { + rc = flash_img_buffered_write(&zephyr_img_flash_ctxt, NULL, 0, true); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + } + + return 0; +} + +int +img_impl_swap_type(void) +{ + img_impl_init_flash(); + + switch (boot_swap_type()) { + case BOOT_SWAP_TYPE_NONE: + return IMG_SWAP_TYPE_NONE; + case BOOT_SWAP_TYPE_TEST: + return IMG_SWAP_TYPE_TEST; + case BOOT_SWAP_TYPE_PERM: + return IMG_SWAP_TYPE_PERM; + case BOOT_SWAP_TYPE_REVERT: + return IMG_SWAP_TYPE_REVERT; + default: + assert(0); + return IMG_SWAP_TYPE_NONE; + } +} diff --git a/img/src/img.c b/img/src/img.c new file mode 100644 index 0000000..9a2019c --- /dev/null +++ b/img/src/img.c @@ -0,0 +1,379 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <limits.h> +#include <assert.h> +#include <string.h> + +#include "cborattr/cborattr.h" +#include "mgmt/mgmt.h" + +#include "img/img.h" +#include "img/image.h" +#include "img/img_impl.h" +#include "img_priv.h" + +#define IMG_MAX_CHUNK_SIZE 512 + +static int img_upload(struct mgmt_cbuf *); +static int img_erase(struct mgmt_cbuf *); + +static const struct mgmt_handler img_nmgr_handlers[] = { + [IMG_NMGR_ID_STATE] = { + .mh_read = img_state_read, + .mh_write = img_state_write, + }, + [IMG_NMGR_ID_UPLOAD] = { + .mh_read = NULL, + .mh_write = img_upload + }, + [IMG_NMGR_ID_ERASE] = { + .mh_read = NULL, + .mh_write = img_erase + }, +#if 0 + [IMG_NMGR_ID_CORELIST] = { +#if MYNEWT_VAL(IMG_COREDUMP) + .mh_read = img_core_list, + .mh_write = NULL +#else + .mh_read = NULL, + .mh_write = NULL +#endif + }, + [IMG_NMGR_ID_CORELOAD] = { +#if MYNEWT_VAL(IMG_COREDUMP) + .mh_read = img_core_load, + .mh_write = img_core_erase, +#else + .mh_read = NULL, + .mh_write = NULL +#endif + }, +#endif +}; + +#define IMG_HANDLER_CNT \ + sizeof(img_nmgr_handlers) / sizeof(img_nmgr_handlers[0]) + +static struct mgmt_group img_nmgr_group = { + .mg_handlers = (struct mgmt_handler *)img_nmgr_handlers, + .mg_handlers_count = IMG_HANDLER_CNT, + .mg_group_id = MGMT_GROUP_ID_IMAGE, +}; + +static struct { + off_t off; + size_t image_len; + bool uploading; +} img_ctxt; + +static int +img_img_tlvs(const struct image_header *hdr, + int slot, off_t *start_off, off_t *end_off) +{ + struct image_tlv_info tlv_info; + int rc; + + rc = img_impl_read(slot, *start_off, &tlv_info, sizeof tlv_info); + if (rc != 0) { + return -1; + } + + if (tlv_info.it_magic != IMAGE_TLV_INFO_MAGIC) { + return 1; + } + + *start_off += sizeof tlv_info; + *end_off = *start_off + tlv_info.it_tlv_tot; + + return 0; +} + +/* + * Read version and build hash from image located slot "image_slot". Note: + * this is a slot index, not a flash area ID. + * + * @param image_slot + * @param ver (optional) + * @param hash (optional) + * @param flags + * + * Returns -1 if area is not readable. + * Returns 0 if image in slot is ok, and version string is valid. + * Returns 1 if there is not a full image. + * Returns 2 if slot is empty. XXXX not there yet + * XXX Define return code macros. + */ +int +img_read_info(int image_slot, struct image_version *ver, uint8_t *hash, + uint32_t *flags) +{ + struct image_header hdr; + struct image_tlv tlv; + uint32_t data_off; + uint32_t data_end; + int rc; + + rc = img_impl_read(image_slot, 0, &hdr, sizeof hdr); + if (rc != 0) { + return -1; + } + + if (ver != NULL) { + memset(ver, 0xff, sizeof(*ver)); + } + if (hdr.ih_magic == IMAGE_MAGIC) { + if (ver != NULL) { + memcpy(ver, &hdr.ih_ver, sizeof(*ver)); + } + } else if (hdr.ih_magic == 0xffffffff) { + return 2; + } else { + return 1; + } + + if (flags != NULL) { + *flags = hdr.ih_flags; + } + + /* The hash is contained in a TLV after the image. */ + data_off = hdr.ih_hdr_size + hdr.ih_img_size; + rc = img_img_tlvs(&hdr, image_slot, &data_off, &data_end); + if (rc != 0) { + return rc; + } + + while (data_off + sizeof tlv <= data_end) { + rc = img_impl_read(image_slot, data_off, &tlv, sizeof tlv); + if (rc != 0) { + return 0; + } + if (tlv.it_type == 0xff && tlv.it_len == 0xffff) { + return 1; + } + if (tlv.it_type != IMAGE_TLV_SHA256 || tlv.it_len != IMG_HASH_LEN) { + data_off += sizeof tlv + tlv.it_len; + continue; + } + data_off += sizeof tlv; + if (hash != NULL) { + if (data_off + IMG_HASH_LEN > data_end) { + return 0; + } + rc = img_impl_read(image_slot, data_off, hash, IMG_HASH_LEN); + if (rc != 0) { + return 0; + } + } + return 0; + } + + return 1; +} + +/* + * Finds image given version number. Returns the slot number image is in, + * or -1 if not found. + */ +int +img_find_by_ver(struct image_version *find, uint8_t *hash) +{ + int i; + struct image_version ver; + + for (i = 0; i < 2; i++) { + if (img_read_info(i, &ver, hash, NULL) != 0) { + continue; + } + if (!memcmp(find, &ver, sizeof(ver))) { + return i; + } + } + return -1; +} + +/* + * Finds image given hash of the image. Returns the slot number image is in, + * or -1 if not found. + */ +int +img_find_by_hash(uint8_t *find, struct image_version *ver) +{ + int i; + uint8_t hash[IMG_HASH_LEN]; + + for (i = 0; i < 2; i++) { + if (img_read_info(i, ver, hash, NULL) != 0) { + continue; + } + if (!memcmp(hash, find, IMG_HASH_LEN)) { + return i; + } + } + return -1; +} + +static int +img_erase(struct mgmt_cbuf *cb) +{ + CborError err; + int rc; + + rc = img_impl_erase_slot(); + + err = 0; + err |= cbor_encode_text_stringz(&cb->encoder, "rc"); + err |= cbor_encode_int(&cb->encoder, rc); + + if (err != 0) { + return MGMT_ERR_ENOMEM; + } + + return 0; +} + +static int +img_write_upload_rsp(struct mgmt_cbuf *cb, int status) +{ + CborError err; + + err = 0; + err |= cbor_encode_text_stringz(&cb->encoder, "rc"); + err |= cbor_encode_int(&cb->encoder, status); + err |= cbor_encode_text_stringz(&cb->encoder, "off"); + err |= cbor_encode_int(&cb->encoder, img_ctxt.off); + + if (err != 0) { + return MGMT_ERR_ENOMEM; + } + return 0; +} + +/* XXX: Rename */ +static int +img_upload_first(struct mgmt_cbuf *cb, const uint8_t *req_data, size_t len) +{ + struct image_header hdr; + int rc; + + if (len < sizeof hdr) { + return MGMT_ERR_EINVAL; + } + + memcpy(&hdr, req_data, sizeof hdr); + if (hdr.ih_magic != IMAGE_MAGIC) { + return MGMT_ERR_EINVAL; + } + + if (img_state_slot_in_use(1)) { + /* No free slot. */ + return MGMT_ERR_ENOMEM; + } + + rc = img_impl_erase_slot(); + if (rc != 0) { + return rc; + } + + img_ctxt.uploading = true; + img_ctxt.off = 0; + img_ctxt.image_len = 0; + + return 0; +} + +static int +img_upload(struct mgmt_cbuf *cb) +{ + long long unsigned int off = UINT_MAX; + long long unsigned int size = UINT_MAX; + uint8_t img_data[IMG_MAX_CHUNK_SIZE]; + size_t data_len = 0; + bool last; + int rc; + + const struct cbor_attr_t off_attr[4] = { + [0] = { + .attribute = "data", + .type = CborAttrByteStringType, + .addr.bytestring.data = img_data, + .addr.bytestring.len = &data_len, + .len = sizeof(img_data) + }, + [1] = { + .attribute = "len", + .type = CborAttrUnsignedIntegerType, + .addr.uinteger = &size, + .nodefault = true + }, + [2] = { + .attribute = "off", + .type = CborAttrUnsignedIntegerType, + .addr.uinteger = &off, + .nodefault = true + }, + [3] = { 0 }, + }; + + rc = cbor_read_object(&cb->it, off_attr); + if (rc || off == UINT_MAX) { + return MGMT_ERR_EINVAL; + } + + if (off == 0) { + rc = img_upload_first(cb, img_data, data_len); + if (rc != 0) { + return rc; + } + img_ctxt.image_len = size; + } else { + if (!img_ctxt.uploading) { + return MGMT_ERR_EINVAL; + } + + if (off != img_ctxt.off) { + /* Invalid offset. Drop the data, and respond with the offset we're + * expecting data for. + */ + return img_write_upload_rsp(cb, 0); + } + } + + if (data_len > 0) { + last = img_ctxt.off + data_len == img_ctxt.image_len; + rc = img_impl_write_image_data(off, img_data, data_len, last); + if (rc != 0) { + return rc; + } + + img_ctxt.off += data_len; + if (last) { + img_ctxt.uploading = false; + } + } + + return img_write_upload_rsp(cb, 0); +} + +int +img_group_register(void) +{ + return mgmt_group_register(&img_nmgr_group); +} diff --git a/img/src/img_priv.h b/img/src/img_priv.h new file mode 100644 index 0000000..8140856 --- /dev/null +++ b/img/src/img_priv.h @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_IMG_PRIV_ +#define H_IMG_PRIV_ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMG_MAX_IMGS 2 + +#define IMG_HASH_STR 48 + +/* + * When accompanied by image, it's this structure followed by data. + * Response contains just the offset. + */ +struct img_upload_cmd { + uint32_t iuc_off; +}; + +/* + * Response to list: + * { + * "images":[ <version1>, <version2>] + * } + * + * + * Request to boot to version: + * { + * "test":<version> + * } + * + * + * Response to boot read: + * { + * "test":<version>, + * "main":<version>, + * "active":<version> + * } + * + * + * Request to image upload: + * { + * "off":<offset>, + * "len":<img_size> inspected when off = 0 + * "data":<base64encoded binary> + * } + * + * + * Response to upload: + * { + * "off":<offset> + * } + * + * + * Request to image upload: + * { + * "off":<offset> + * "name":<filename> inspected when off = 0 + * "len":<file_size> inspected when off = 0 + * "data":<base64encoded binary> + * } + */ + +struct nmgr_hdr; +struct os_mbuf; +struct fs_file; +struct mgmt_cbuf; + +struct nmgr_jbuf; + +int img_core_list(struct mgmt_cbuf *); +int img_core_load(struct mgmt_cbuf *); +int img_core_erase(struct mgmt_cbuf *); +int img_state_read(struct mgmt_cbuf *cb); +int img_state_write(struct mgmt_cbuf *njb); +int img_find_by_ver(struct image_version *find, uint8_t *hash); +int img_find_by_hash(uint8_t *find, struct image_version *ver); +int img_cli_register(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __IMG_PRIV_H */ diff --git a/img/src/img_state.c b/img/src/img_state.c new file mode 100644 index 0000000..b8ae6fa --- /dev/null +++ b/img/src/img_state.c @@ -0,0 +1,295 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <assert.h> + +#include <zephyr.h> +#include "dfu/mcuboot.h" +#include "cborattr/cborattr.h" +#include "cbor.h" +#include "mgmt/mgmt.h" +#include "img/img.h" +#include "img/image.h" +#include "img_priv.h" +#include "img/img_impl.h" + +uint8_t +img_state_flags(int query_slot) +{ + uint8_t flags; + int swap_type; + + assert(query_slot == 0 || query_slot == 1); + + flags = 0; + + /* Determine if this is is pending or confirmed (only applicable for + * unified images and loaders. + */ + swap_type = img_impl_swap_type(); + switch (swap_type) { + case IMG_SWAP_TYPE_NONE: + if (query_slot == 0) { + flags |= IMG_STATE_F_CONFIRMED; + flags |= IMG_STATE_F_ACTIVE; + } + break; + + case IMG_SWAP_TYPE_TEST: + if (query_slot == 0) { + flags |= IMG_STATE_F_CONFIRMED; + } else if (query_slot == 1) { + flags |= IMG_STATE_F_PENDING; + } + break; + + case IMG_SWAP_TYPE_PERM: + if (query_slot == 0) { + flags |= IMG_STATE_F_CONFIRMED; + } else if (query_slot == 1) { + flags |= IMG_STATE_F_PENDING | IMG_STATE_F_PERMANENT; + } + break; + + case IMG_SWAP_TYPE_REVERT: + if (query_slot == 0) { + flags |= IMG_STATE_F_ACTIVE; + } else if (query_slot == 1) { + flags |= IMG_STATE_F_CONFIRMED; + } + break; + } + + /* Slot 0 is always active. */ + /* XXX: The slot 0 assumption only holds when running from flash. */ + if (query_slot == 0) { + flags |= IMG_STATE_F_ACTIVE; + } + + return flags; +} + +static int +img_state_any_pending(void) +{ + return img_state_flags(0) & IMG_STATE_F_PENDING || + img_state_flags(1) & IMG_STATE_F_PENDING; +} + +int +img_state_slot_in_use(int slot) +{ + uint8_t state_flags; + + state_flags = img_state_flags(slot); + return state_flags & IMG_STATE_F_ACTIVE || + state_flags & IMG_STATE_F_CONFIRMED || + state_flags & IMG_STATE_F_PENDING; +} + +int +img_state_set_pending(int slot, int permanent) +{ + uint32_t image_flags; + uint8_t state_flags; + int rc; + + state_flags = img_state_flags(slot); + + /* Unconfirmed slots are always runable. A confirmed slot can only be + * run if it is a loader in a split image setup. + */ + if (state_flags & IMG_STATE_F_CONFIRMED && slot != 0) { + return MGMT_ERR_EBADSTATE; + } + + rc = img_read_info(slot, NULL, NULL, &image_flags); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + rc = img_impl_write_pending(permanent); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + return 0; +} + +int +img_state_confirm(void) +{ + int rc; + + /* Confirm disallowed if a test is pending. */ + if (img_state_any_pending()) { + return MGMT_ERR_EBADSTATE; + } + + /* Confirm the unified image or loader in slot 0. */ + rc = img_impl_write_confirmed(); + if (rc != 0) { + return MGMT_ERR_EUNKNOWN; + } + + return 0; +} + +int +img_state_read(struct mgmt_cbuf *cb) +{ + int i; + int rc; + uint32_t flags; + struct image_version ver; + uint8_t hash[IMG_HASH_LEN]; /* SHA256 hash */ + char vers_str[IMG_NMGR_MAX_VER]; + int any_non_bootable; + uint8_t state_flags; + CborError g_err = CborNoError; + CborEncoder images; + CborEncoder image; + + any_non_bootable = 0; + + g_err |= cbor_encode_text_stringz(&cb->encoder, "images"); + + g_err |= cbor_encoder_create_array(&cb->encoder, &images, + CborIndefiniteLength); + for (i = 0; i < 2; i++) { + rc = img_read_info(i, &ver, hash, &flags); + if (rc != 0) { + continue; + } + + if (flags & IMAGE_F_NON_BOOTABLE) { + any_non_bootable = 1; + } + + state_flags = img_state_flags(i); + + g_err |= cbor_encoder_create_map(&images, &image, + CborIndefiniteLength); + g_err |= cbor_encode_text_stringz(&image, "slot"); + g_err |= cbor_encode_int(&image, i); + + g_err |= cbor_encode_text_stringz(&image, "version"); + img_ver_str(&ver, vers_str); + g_err |= cbor_encode_text_stringz(&image, vers_str); + + g_err |= cbor_encode_text_stringz(&image, "hash"); + g_err |= cbor_encode_byte_string(&image, hash, IMG_HASH_LEN); + + g_err |= cbor_encode_text_stringz(&image, "bootable"); + g_err |= cbor_encode_boolean(&image, !(flags & IMAGE_F_NON_BOOTABLE)); + + g_err |= cbor_encode_text_stringz(&image, "pending"); + g_err |= cbor_encode_boolean(&image, + state_flags & IMG_STATE_F_PENDING); + + g_err |= cbor_encode_text_stringz(&image, "confirmed"); + g_err |= cbor_encode_boolean(&image, + state_flags & IMG_STATE_F_CONFIRMED); + + g_err |= cbor_encode_text_stringz(&image, "active"); + g_err |= cbor_encode_boolean(&image, + state_flags & IMG_STATE_F_ACTIVE); + + g_err |= cbor_encode_text_stringz(&image, "permanent"); + g_err |= cbor_encode_boolean(&image, + state_flags & IMG_STATE_F_PERMANENT); + + g_err |= cbor_encoder_close_container(&images, &image); + } + + g_err |= cbor_encoder_close_container(&cb->encoder, &images); + + g_err |= cbor_encode_text_stringz(&cb->encoder, "splitStatus"); + g_err |= cbor_encode_int(&cb->encoder, 0); + + if (g_err) { + return MGMT_ERR_ENOMEM; + } + return 0; +} + +int +img_state_write(struct mgmt_cbuf *cb) +{ + uint8_t hash[IMG_HASH_LEN]; + size_t hash_len = 0; + bool confirm; + int slot; + int rc; + + const struct cbor_attr_t write_attr[] = { + [0] = { + .attribute = "hash", + .type = CborAttrByteStringType, + .addr.bytestring.data = hash, + .addr.bytestring.len = &hash_len, + .len = sizeof(hash), + }, + [1] = { + .attribute = "confirm", + .type = CborAttrBooleanType, + .addr.boolean = &confirm, + .dflt.boolean = false, + }, + [2] = { 0 }, + }; + + rc = cbor_read_object(&cb->it, write_attr); + if (rc != 0) { + return MGMT_ERR_EINVAL; + } + + /* Determine which slot is being operated on. */ + if (hash_len == 0) { + if (confirm) { + slot = 0; + } else { + /* A 'test' without a hash is invalid. */ + return MGMT_ERR_EINVAL; + } + } else { + slot = img_find_by_hash(hash, NULL); + if (slot < 0) { + return MGMT_ERR_EINVAL; + } + } + + if (slot == 0 && confirm) { + /* Confirm current setup. */ + rc = img_state_confirm(); + } else { + rc = img_state_set_pending(slot, confirm); + } + if (rc != 0) { + return rc; + } + + /* Send the current image state in the response. */ + rc = img_state_read(cb); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/img/src/img_util.c b/img/src/img_util.c new file mode 100644 index 0000000..ae9ca1e --- /dev/null +++ b/img/src/img_util.c @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "img/image.h" +#include "img/img.h" + +// Removed due to missing strsep(). +#if 0 +int +img_ver_parse(char *src, struct image_version *ver) +{ + unsigned long ul; + char *tok; + char *nxt; + char *ep; + + memset(ver, 0, sizeof(*ver)); + + nxt = src; + tok = strsep(&nxt, "."); + ul = strtoul(tok, &ep, 10); + if (tok[0] == '\0' || ep[0] != '\0' || ul > UINT8_MAX) { + return -1; + } + ver->iv_major = ul; + if (nxt == NULL) { + return 0; + } + tok = strsep(&nxt, "."); + ul = strtoul(tok, &ep, 10); + if (tok[0] == '\0' || ep[0] != '\0' || ul > UINT8_MAX) { + return -1; + } + ver->iv_minor = ul; + if (nxt == NULL) { + return 0; + } + + tok = strsep(&nxt, "."); + ul = strtoul(tok, &ep, 10); + if (tok[0] == '\0' || ep[0] != '\0' || ul > UINT16_MAX) { + return -1; + } + ver->iv_revision = ul; + if (nxt == NULL) { + return 0; + } + + tok = nxt; + ul = strtoul(tok, &ep, 10); + if (tok[0] == '\0' || ep[0] != '\0' || ul > UINT32_MAX) { + return -1; + } + ver->iv_build_num = ul; + + return 0; +} +#endif + +int +img_ver_str(const struct image_version *ver, char *dst) +{ + if (ver->iv_build_num) { + return sprintf(dst, "%u.%u.%u.%lu", + ver->iv_major, ver->iv_minor, ver->iv_revision, + (unsigned long)ver->iv_build_num); + } else { + return sprintf(dst, "%u.%u.%u", + ver->iv_major, ver->iv_minor, ver->iv_revision); + } +} -- To stop receiving notification emails like this one, please contact ['"commits@mynewt.apache.org" <commits@mynewt.apache.org>'].