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 2016/10/12 02:33:19 UTC

incubator-mynewt-core git commit: imgmgr - "image state" command.

Repository: incubator-mynewt-core
Updated Branches:
  refs/heads/develop d908df939 -> c8713fc81


imgmgr - "image state" command.

This command is intended to replace the following:
    * image list
    * image boot
    * split status

It remains a separate command until it is complete.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/c8713fc8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/c8713fc8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/c8713fc8

Branch: refs/heads/develop
Commit: c8713fc8139deb25395c67d24d9e58aba6c0bfdf
Parents: d908df9
Author: Christopher Collins <cc...@apache.org>
Authored: Tue Oct 11 19:32:03 2016 -0700
Committer: Christopher Collins <cc...@apache.org>
Committed: Tue Oct 11 19:32:03 2016 -0700

----------------------------------------------------------------------
 boot/bootutil/include/bootutil/bootutil_misc.h |  22 ---
 boot/bootutil/src/bootutil_misc.c              |  18 --
 boot/split/include/split/split.h               |  23 +++
 boot/split/src/split.c                         |  38 +++-
 boot/split/src/split_config.c                  |  18 +-
 boot/split/src/split_netmgr.c                  |   2 +-
 boot/split/src/split_priv.h                    |   3 +-
 mgmt/imgmgr/include/imgmgr/imgmgr.h            |   3 +
 mgmt/imgmgr/src/imgmgr.c                       |  43 +++--
 mgmt/imgmgr/src/imgmgr_boot.c                  |   3 +-
 mgmt/imgmgr/src/imgmgr_priv.h                  |   3 +-
 mgmt/imgmgr/src/imgmgr_state.c                 | 204 +++++++++++++++++++-
 12 files changed, 287 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/bootutil/include/bootutil/bootutil_misc.h
----------------------------------------------------------------------
diff --git a/boot/bootutil/include/bootutil/bootutil_misc.h b/boot/bootutil/include/bootutil/bootutil_misc.h
index 32b5456..1efe864 100644
--- a/boot/bootutil/include/bootutil/bootutil_misc.h
+++ b/boot/bootutil/include/bootutil/bootutil_misc.h
@@ -26,33 +26,11 @@
 extern "C" {
 #endif
 
-typedef enum {
-    /** Loader only. */
-    BOOT_SPLIT_MODE_LOADER =            0,
-
-    /** Loader + app; revert to loader on reboot. */
-    BOOT_SPLIT_MODE_TEST_APP =          1,
-
-    /** Loader + app; no change on reboot. */
-    BOOT_SPLIT_MODE_APP =               2,
-
-    /** Loader only, revert to loader + app on reboot. */
-    BOOT_SPLIT_MODE_TEST_LOADER =       3,
-} boot_split_mode_t;
-
-/** Count of valid enum values. */
-#define BOOT_SPLIT_MODE_CNT         4
-
-extern int8_t boot_split_mode;
-
 int boot_vect_read_test(int *slot);
 int boot_vect_read_main(int *slot);
 int boot_vect_write_test(int slot);
 int boot_vect_write_main(void);
 
-boot_split_mode_t boot_split_mode_get(void);
-int boot_split_mode_set(boot_split_mode_t split_mode);
-
 int boot_split_app_active_get(void);
 void boot_split_app_active_set(int active);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/bootutil/src/bootutil_misc.c
----------------------------------------------------------------------
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index 4e096ec..0ee7ea9 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -34,7 +34,6 @@
 #include "bootutil_priv.h"
 
 int boot_current_slot;
-int8_t boot_split_mode;
 int8_t boot_split_app_active;
 
 /*
@@ -332,23 +331,6 @@ boot_clear_status(void)
     hal_flash_write(flash_id, off, &val, sizeof(val));
 }
 
-boot_split_mode_t
-boot_split_mode_get(void)
-{
-    return boot_split_mode;
-}
-
-int
-boot_split_mode_set(boot_split_mode_t split_mode)
-{
-    if (split_mode < 0 || split_mode >= BOOT_SPLIT_MODE_CNT) {
-        return EINVAL;
-    }
-
-    boot_split_mode = split_mode;
-    return 0;
-}
-
 int
 boot_split_app_active_get(void)
 {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/split/include/split/split.h
----------------------------------------------------------------------
diff --git a/boot/split/include/split/split.h b/boot/split/include/split/split.h
index 6e81727..3e12000 100644
--- a/boot/split/include/split/split.h
+++ b/boot/split/include/split/split.h
@@ -20,6 +20,8 @@
 #ifndef _SPLIT_H__
 #define _SPLIT_H__
 
+#include "bootutil/bootutil_misc.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -27,6 +29,23 @@ extern "C" {
 #define SPLIT_NMGR_OP_SPLIT 0
 
 typedef enum {
+    /** Loader only. */
+    SPLIT_MODE_LOADER =            0,
+
+    /** Loader + app; revert to loader on reboot. */
+    SPLIT_MODE_TEST_APP =          1,
+
+    /** Loader + app; no change on reboot. */
+    SPLIT_MODE_APP =               2,
+
+    /** Loader only, revert to loader + app on reboot. */
+    SPLIT_MODE_TEST_LOADER =       3,
+} split_mode_t;
+
+/** Count of valid enum values. */
+#define SPLIT_MODE_CNT         4
+
+typedef enum {
     SPLIT_STATUS_INVALID =      0,
     SPLIT_STATUS_NOT_MATCHING = 1,
     SPLIT_STATUS_MATCHING =     2,
@@ -52,6 +71,10 @@ void split_app_init(void);
 int split_app_go(void **entry, int toboot);
 
 split_status_t split_check_status(void);
+split_mode_t split_mode_get(void);
+int split_mode_set(split_mode_t split_mode);
+
+int split_write_split(split_mode_t mode);
 
 #ifdef __cplusplus
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/split/src/split.c
----------------------------------------------------------------------
diff --git a/boot/split/src/split.c b/boot/split/src/split.c
index ce7a409..7194874 100644
--- a/boot/split/src/split.c
+++ b/boot/split/src/split.c
@@ -18,6 +18,7 @@
  */
 
 #include <assert.h>
+#include "defs/error.h"
 #include "bootutil/bootutil_misc.h"
 #include "bootutil/image.h"
 #include "bootutil/loader.h"
@@ -29,6 +30,8 @@
 #define SPLIT_IMAGE_SLOT    1
 #define SPLIT_TOTAL_IMAGES  2
 
+static int8_t split_mode_cur;
+
 void
 split_app_init(void)
 {
@@ -64,6 +67,23 @@ split_check_status(void)
     }
 }
 
+split_mode_t
+split_mode_get(void)
+{
+    return split_mode_cur;
+}
+
+int
+split_mode_set(split_mode_t split_mode)
+{
+    if (split_mode < 0 || split_mode >= SPLIT_MODE_CNT) {
+        return EINVAL;
+    }
+
+    split_mode_cur = split_mode;
+    return 0;
+}
+
 /**
  * This validates and provides the loader image data
  *
@@ -72,35 +92,35 @@ split_check_status(void)
 int
 split_app_go(void **entry, int toboot)
 {
-    boot_split_mode_t split_mode;
+    split_mode_t split_mode;
     int run_app;
     int rc;
 
     if (toboot) {
-        split_mode = boot_split_mode_get();
+        split_mode = split_mode_get();
 
         /* if we are told not to, then we don't boot an app */
-        if (split_mode == BOOT_SPLIT_MODE_LOADER) {
+        if (split_mode == SPLIT_MODE_LOADER) {
             return -1;
         }
 
         /* if this is a one-time test, reset the split mode */
         switch (split_mode) {
-        case BOOT_SPLIT_MODE_LOADER:
+        case SPLIT_MODE_LOADER:
             run_app = 0;
             break;
 
-        case BOOT_SPLIT_MODE_TEST_APP:
-            split_write_split(BOOT_SPLIT_MODE_LOADER);
+        case SPLIT_MODE_TEST_APP:
+            split_write_split(SPLIT_MODE_LOADER);
             run_app = 1;
             break;
 
-        case BOOT_SPLIT_MODE_TEST_LOADER:
-            split_write_split(BOOT_SPLIT_MODE_APP);
+        case SPLIT_MODE_TEST_LOADER:
+            split_write_split(SPLIT_MODE_APP);
             run_app = 0;
             break;
 
-        case BOOT_SPLIT_MODE_APP:
+        case SPLIT_MODE_APP:
             run_app = 1;
             break;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/split/src/split_config.c
----------------------------------------------------------------------
diff --git a/boot/split/src/split_config.c b/boot/split/src/split_config.c
index fab8594..9332e90 100644
--- a/boot/split/src/split_config.c
+++ b/boot/split/src/split_config.c
@@ -36,11 +36,11 @@ split_conf_init(void)
 static char *
 split_conf_get(int argc, char **argv, char *buf, int max_len)
 {
-    boot_split_mode_t split_mode;
+    split_mode_t split_mode;
 
     if (argc == 1) {
         if (!strcmp(argv[0], "status")) {
-            split_mode = boot_split_mode_get();
+            split_mode = split_mode_get();
             return conf_str_from_value(CONF_INT8, &split_mode, buf, max_len);
         }
     }
@@ -50,7 +50,7 @@ split_conf_get(int argc, char **argv, char *buf, int max_len)
 static int
 split_conf_set(int argc, char **argv, char *val)
 {
-    boot_split_mode_t split_mode;
+    split_mode_t split_mode;
     int rc;
 
     if (argc == 1) {
@@ -60,7 +60,7 @@ split_conf_set(int argc, char **argv, char *val)
                 return rc;
             }
 
-            rc = boot_split_mode_set(split_mode);
+            rc = split_mode_set(split_mode);
             if (rc != 0) {
                 return rc;
             }
@@ -80,22 +80,22 @@ split_conf_commit(void)
 static int
 split_conf_export(void (*func)(char *name, char *val), enum conf_export_tgt tgt)
 {
-    boot_split_mode_t split_mode;
+    split_mode_t split_mode;
     char buf[4];
 
-    split_mode = boot_split_mode_get();
+    split_mode = split_mode_get();
     conf_str_from_value(CONF_INT8, &split_mode, buf, sizeof(buf));
     func("split/status", buf);
     return 0;
 }
 
 int
-split_write_split(boot_split_mode_t split_mode)
+split_write_split(split_mode_t split_mode)
 {
-    char str[CONF_STR_FROM_BYTES_LEN(sizeof(boot_split_mode_t))];
+    char str[CONF_STR_FROM_BYTES_LEN(sizeof(split_mode_t))];
     int rc;
 
-    rc = boot_split_mode_set(split_mode);
+    rc = split_mode_set(split_mode);
     if (rc != 0) {
         return rc;
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/split/src/split_netmgr.c
----------------------------------------------------------------------
diff --git a/boot/split/src/split_netmgr.c b/boot/split/src/split_netmgr.c
index 79a4e9e..50c21f5 100644
--- a/boot/split/src/split_netmgr.c
+++ b/boot/split/src/split_netmgr.c
@@ -61,7 +61,7 @@ imgr_splitapp_read(struct mgmt_jbuf *njb)
 
     json_encode_object_start(enc);
 
-    x = boot_split_mode_get();
+    x = split_mode_get();
     JSON_VALUE_INT(&jv, x)
     json_encode_object_entry(enc, "splitMode", &jv);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/boot/split/src/split_priv.h
----------------------------------------------------------------------
diff --git a/boot/split/src/split_priv.h b/boot/split/src/split_priv.h
index 4858c1a..d809142 100644
--- a/boot/split/src/split_priv.h
+++ b/boot/split/src/split_priv.h
@@ -28,8 +28,7 @@ extern "C" {
 
 int split_conf_init(void);
 int split_nmgr_register(void);
-int split_read_split(boot_split_mode_t *split);
-int split_write_split(boot_split_mode_t mode);
+int split_read_split(split_mode_t *split);
 
 #ifdef __cplusplus
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/mgmt/imgmgr/include/imgmgr/imgmgr.h
----------------------------------------------------------------------
diff --git a/mgmt/imgmgr/include/imgmgr/imgmgr.h b/mgmt/imgmgr/include/imgmgr/imgmgr.h
index 45c77c7..d040a7b 100644
--- a/mgmt/imgmgr/include/imgmgr/imgmgr.h
+++ b/mgmt/imgmgr/include/imgmgr/imgmgr.h
@@ -68,6 +68,9 @@ int imgr_read_info(int area_id, struct image_version *ver, uint8_t *hash, uint32
  */
 int imgr_my_version(struct image_version *ver);
 
+uint8_t imgmgr_state_flags(int query_slot);
+int imgmgr_state_slot_in_use(int slot);
+
 #ifdef __cplusplus
 }
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/mgmt/imgmgr/src/imgmgr.c
----------------------------------------------------------------------
diff --git a/mgmt/imgmgr/src/imgmgr.c b/mgmt/imgmgr/src/imgmgr.c
index 2bc4eec..ec8d09c 100644
--- a/mgmt/imgmgr/src/imgmgr.c
+++ b/mgmt/imgmgr/src/imgmgr.c
@@ -87,8 +87,8 @@ static const struct mgmt_handler imgr_nmgr_handlers[] = {
 #endif
     },
     [IMGMGR_NMGR_OP_STATE] = {
-        .mh_read = imgr_state_read,
-        .mh_write = NULL,//imgr_state_write,
+        .mh_read = imgmgr_state_read,
+        .mh_write = imgmgr_state_write,
     },
 };
 
@@ -107,6 +107,11 @@ struct imgr_state imgr_state;
  * 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.
@@ -136,15 +141,18 @@ imgr_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
     if (rc2) {
         goto end;
     }
-    memset(ver, 0xff, sizeof(*ver));
-    if (hdr->ih_magic == IMAGE_MAGIC) {
-        memcpy(ver, &hdr->ih_ver, sizeof(*ver));
-    } else if (hdr->ih_magic == 0xffffffff) {
-        rc = 2;
-        goto end;
-    } else {
-        rc = 1;
-        goto end;
+
+    if (ver != NULL) {
+        memset(ver, 0xff, sizeof(*ver));
+        if (hdr->ih_magic == IMAGE_MAGIC) {
+            memcpy(ver, &hdr->ih_ver, sizeof(*ver));
+        } else if (hdr->ih_magic == 0xffffffff) {
+            rc = 2;
+            goto end;
+        } else {
+            rc = 1;
+            goto end;
+        }
     }
 
     if(flags) {
@@ -320,7 +328,6 @@ imgr_upload(struct mgmt_jbuf *njb)
     struct json_encoder *enc;
     struct json_value jv;
     int area_id;
-    int active;
     int best;
     int rc;
     int len;
@@ -359,7 +366,6 @@ imgr_upload(struct mgmt_jbuf *njb)
          */
         imgr_state.upload.off = 0;
         imgr_state.upload.size = size;
-        active = boot_current_slot;
         best = -1;
 
         for (i = 0; i < 2; i++) {
@@ -368,13 +374,9 @@ imgr_upload(struct mgmt_jbuf *njb)
                 continue;
             }
             if (rc == 0) {
-                /*
-                 * Image in slot is ok.
-                 */
-                if (active == i) {
-                    /*
-                     * Slot is currently active one. Can't upload to this.
-                     */
+                /* Image in slot is ok. */
+                if (imgmgr_state_slot_in_use(i)) {
+                    /* Slot is in use; can't upload to this. */
                     continue;
                 } else {
                     /*
@@ -476,5 +478,6 @@ imgmgr_module_init(void)
     SYSINIT_PANIC_ASSERT(rc == 0);
 #endif
 
+    /* XXX: Confirm image under test; this needs to be removed. */
     boot_vect_write_main();
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/mgmt/imgmgr/src/imgmgr_boot.c
----------------------------------------------------------------------
diff --git a/mgmt/imgmgr/src/imgmgr_boot.c b/mgmt/imgmgr/src/imgmgr_boot.c
index fdbbda6..d21b009 100644
--- a/mgmt/imgmgr/src/imgmgr_boot.c
+++ b/mgmt/imgmgr/src/imgmgr_boot.c
@@ -31,6 +31,7 @@
 #include <base64/base64.h>
 #include <hal/hal_bsp.h>
 
+#include "split/split.h"
 #include "imgmgr/imgmgr.h"
 #include "imgmgr_priv.h"
 
@@ -67,7 +68,7 @@ imgr_boot2_read(struct mgmt_jbuf *njb)
 
     /* Temporary hack to preserve old behavior. */
     if (boot_split_app_active_get()) {
-        if (boot_split_mode_get() == BOOT_SPLIT_MODE_TEST_APP) {
+        if (split_mode_get() == SPLIT_MODE_TEST_APP) {
             test_slot = 0;
         }
         main_slot = 0;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/mgmt/imgmgr/src/imgmgr_priv.h
----------------------------------------------------------------------
diff --git a/mgmt/imgmgr/src/imgmgr_priv.h b/mgmt/imgmgr/src/imgmgr_priv.h
index 7f8a0bf..6642bc6 100644
--- a/mgmt/imgmgr/src/imgmgr_priv.h
+++ b/mgmt/imgmgr/src/imgmgr_priv.h
@@ -111,7 +111,8 @@ int imgr_core_load(struct mgmt_jbuf *);
 int imgr_core_erase(struct mgmt_jbuf *);
 int imgr_splitapp_read(struct mgmt_jbuf *);
 int imgr_splitapp_write(struct mgmt_jbuf *);
-int imgr_state_read(struct mgmt_jbuf *njb);
+int imgmgr_state_read(struct mgmt_jbuf *njb);
+int imgmgr_state_write(struct mgmt_jbuf *njb);
 int imgr_find_by_ver(struct image_version *find, uint8_t *hash);
 int imgr_find_by_hash(uint8_t *find, struct image_version *ver);
 int imgr_cli_register(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/c8713fc8/mgmt/imgmgr/src/imgmgr_state.c
----------------------------------------------------------------------
diff --git a/mgmt/imgmgr/src/imgmgr_state.c b/mgmt/imgmgr/src/imgmgr_state.c
index 4fdd8cb..9bba7aa 100644
--- a/mgmt/imgmgr/src/imgmgr_state.c
+++ b/mgmt/imgmgr/src/imgmgr_state.c
@@ -31,10 +31,10 @@
 #define IMGMGR_STATE_F_CONFIRMED        0x02
 #define IMGMGR_STATE_F_ACTIVE           0x04
 
-static uint8_t
+uint8_t
 imgmgr_state_flags(int query_slot)
 {
-    boot_split_mode_t split_mode;
+    split_mode_t split_mode;
     uint8_t flags;
     int slot;
     int rc;
@@ -66,24 +66,24 @@ imgmgr_state_flags(int query_slot)
     /* Read the split/status config state to determine any pending split-image
      * state changes.
      */
-    split_mode = boot_split_mode_get();
+    split_mode = split_mode_get();
     switch (split_mode) {
-    case BOOT_SPLIT_MODE_LOADER:
+    case SPLIT_MODE_LOADER:
         break;
 
-    case BOOT_SPLIT_MODE_APP:
+    case SPLIT_MODE_APP:
         if (query_slot == 1) {
             flags |= IMGMGR_STATE_F_CONFIRMED;
         }
         break;
 
-    case BOOT_SPLIT_MODE_TEST_LOADER:
+    case SPLIT_MODE_TEST_LOADER:
         if (query_slot == 0) {
             flags |= IMGMGR_STATE_F_PENDING;
         }
         break;
 
-    case BOOT_SPLIT_MODE_TEST_APP:
+    case SPLIT_MODE_TEST_APP:
         if (query_slot == 1) {
             flags |= IMGMGR_STATE_F_PENDING;
         }
@@ -101,8 +101,101 @@ imgmgr_state_flags(int query_slot)
     return flags;
 }
 
+static int
+imgmgr_state_any_pending(void)
+{
+    return imgmgr_state_flags(0) & IMGMGR_STATE_F_PENDING ||
+           imgmgr_state_flags(1) & IMGMGR_STATE_F_PENDING;
+}
+
+int
+imgmgr_state_slot_in_use(int slot)
+{
+    uint8_t state_flags;
+
+    state_flags = imgmgr_state_flags(slot);
+    return state_flags & IMGMGR_STATE_F_ACTIVE       ||
+           state_flags & IMGMGR_STATE_F_CONFIRMED    ||
+           state_flags & IMGMGR_STATE_F_PENDING;
+}
+
+static int
+imgmgr_state_test_slot(int slot)
+{
+    uint32_t image_flags;
+    uint8_t state_flags;
+    int split_app_active;
+    int rc;
+
+    state_flags = imgmgr_state_flags(slot);
+    split_app_active = boot_split_app_active_get();
+
+    /* Unconfirmed slots are always testable.  A confirmed slot can only be
+     * tested if it is a loader in a split image setup.
+     */
+    if (state_flags & IMGMGR_STATE_F_CONFIRMED &&
+        (slot != 0 || !split_app_active)) {
+
+        return MGMT_ERR_EINVAL;
+    }
+
+    rc = imgr_read_info(slot, NULL, NULL, &image_flags);
+    if (rc != 0) {
+        return MGMT_ERR_EUNKNOWN;
+    }
+
+    if (!(image_flags & IMAGE_F_NON_BOOTABLE)) {
+        /* Unified image or loader. */
+        if (!split_app_active) {
+            /* No change in split status. */
+            rc = boot_vect_write_test(slot);
+            if (rc != 0) {
+                return MGMT_ERR_EUNKNOWN;
+            }
+        } else {
+            /* Currently loader + app; testing loader-only. */
+            rc = split_write_split(SPLIT_MODE_TEST_LOADER);
+        }
+    } else {
+        /* Testing split app. */
+        rc = split_write_split(SPLIT_MODE_TEST_APP);
+        if (rc != 0) {
+            return MGMT_ERR_EUNKNOWN;
+        }
+    }
+
+    return 0;
+}
+
+static int
+imgmgr_state_confirm(void)
+{
+    int rc;
+
+    /* Confirm disallowed if a test is pending. */
+    if (imgmgr_state_any_pending()) {
+        return MGMT_ERR_EINVAL;
+    }
+
+    /* Confirm the unified image or loader in slot 0. */
+    rc = boot_vect_write_main();
+    if (rc != 0) {
+        return MGMT_ERR_EUNKNOWN;
+    }
+
+    /* If a split app in slot 1 is active, confirm it as well. */
+    if (boot_split_app_active_get()) {
+        rc = split_write_split(SPLIT_MODE_APP);
+        if (rc != 0) {
+            return MGMT_ERR_EUNKNOWN;
+        }
+    }
+
+    return 0;
+}
+
 int
-imgr_state_read(struct mgmt_jbuf *njb)
+imgmgr_state_read(struct mgmt_jbuf *njb)
 {
     struct json_encoder *enc;
     int i;
@@ -114,11 +207,15 @@ imgr_state_read(struct mgmt_jbuf *njb)
     char vers_str[IMGMGR_NMGR_MAX_VER];
     char hash_str[IMGMGR_HASH_STR + 1];
     int ver_len;
+    int any_non_bootable;
+    int split_status;
     uint8_t state_flags;
 
+    any_non_bootable = 0;
     enc = &njb->mjb_enc;
 
     json_encode_object_start(enc);
+
     json_encode_array_name(enc, "images");
     json_encode_array_start(enc);
     for (i = 0; i < 2; i++) {
@@ -127,6 +224,10 @@ imgr_state_read(struct mgmt_jbuf *njb)
             continue;
         }
 
+        if (flags & IMAGE_F_NON_BOOTABLE) {
+            any_non_bootable = 1;
+        }
+
         state_flags = imgmgr_state_flags(i);
 
         json_encode_object_start(enc);
@@ -146,7 +247,7 @@ imgr_state_read(struct mgmt_jbuf *njb)
         json_encode_object_entry(enc, "bootable", &jv);
 
         JSON_VALUE_BOOL(&jv, state_flags & IMGMGR_STATE_F_PENDING);
-        json_encode_object_entry(enc, "test-pending", &jv);
+        json_encode_object_entry(enc, "pending", &jv);
 
         JSON_VALUE_BOOL(&jv, state_flags & IMGMGR_STATE_F_CONFIRMED);
         json_encode_object_entry(enc, "confirmed", &jv);
@@ -158,10 +259,93 @@ imgr_state_read(struct mgmt_jbuf *njb)
     }
     json_encode_array_finish(enc);
 
-    JSON_VALUE_INT(&jv, split_check_status());
+    if (any_non_bootable) {
+        split_status = split_check_status();
+    } else {
+        split_status = SPLIT_STATUS_INVALID;
+    }
+    JSON_VALUE_INT(&jv, split_status);
     json_encode_object_entry(enc, "splitStatus", &jv);
 
     json_encode_object_finish(enc);
 
     return 0;
 }
+
+int
+imgmgr_state_write(struct mgmt_jbuf *njb)
+{
+    char hash_str[IMGMGR_HASH_STR + 1];
+    uint8_t hash[IMGMGR_HASH_LEN];
+    bool confirm;
+    int slot;
+    int rc;
+
+    const struct json_attr_t write_attr[] = {
+        [0] = {
+            .attribute = "hash",
+            .type = t_string,
+            .addr.string = hash_str,
+            .len = sizeof(hash_str),
+        },
+        [1] = {
+            .attribute = "confirm",
+            .type = t_boolean,
+            .addr.boolean = &confirm,
+            .dflt.boolean = false,
+        },
+        [2] = {
+            .attribute = NULL
+        },
+    };
+
+    rc = json_read_object(&njb->mjb_buf, write_attr);
+    if (rc != 0) {
+        rc = MGMT_ERR_EINVAL;
+        goto err;
+    }
+
+    /* Validate arguments. */
+    if (hash_str[0] == '\0' && !confirm) {
+        rc = MGMT_ERR_EINVAL;
+        goto err;
+    }
+    if (hash_str[0] != '\0' && confirm) {
+        rc = MGMT_ERR_EINVAL;
+        goto err;
+    }
+
+    if (!confirm) {
+        /* Test image with specified hash. */
+        base64_decode(hash_str, hash);
+        slot = imgr_find_by_hash(hash, NULL);
+        if (slot < 0) {
+            rc = MGMT_ERR_EINVAL;
+            goto err;
+        }
+
+        rc = imgmgr_state_test_slot(slot);
+        if (rc != 0) {
+            goto err;
+        }
+    } else {
+        /* Confirm current setup. */
+        rc = imgmgr_state_confirm();
+        if (rc != 0) {
+            goto err;
+        }
+    }
+
+    /* Send the current image state in the response. */
+    rc = imgmgr_state_read(njb);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    mgmt_jbuf_setoerr(njb, rc);
+
+    return 0;
+}