You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2018/01/03 03:14:44 UTC

[mynewt-mcumgr] branch master updated: image management

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 <cc...@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" <co...@mynewt.apache.org>'].