Boot: Add dependency check to multi-image boot
This patch adds the capability to check image dependencies in case
of multi-image boot. The dependencies are described with a new type
of TLV in the manifest.
Change-Id: If45f81a00d4324c881634f50156f9939e1bf8707
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h
index 22f6e62..88b1a43 100644
--- a/boot/bootutil/include/bootutil/image.h
+++ b/boot/bootutil/include/bootutil/image.h
@@ -17,6 +17,10 @@
* under the License.
*/
+/*
+ * Modifications are Copyright (c) 2019 Arm Limited.
+ */
+
#ifndef H_IMAGE_
#define H_IMAGE_
@@ -76,6 +80,7 @@
#define IMAGE_TLV_ED25519 0x24 /* ed25519 of hash output */
#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */
#define IMAGE_TLV_ENC_KW128 0x31 /* Key encrypted with AES-KW-128 */
+#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */
struct image_version {
uint8_t iv_major;
@@ -84,16 +89,24 @@
uint32_t iv_build_num;
};
+struct image_dependency {
+ uint8_t image_id; /* Image index (from 0) */
+ struct image_version image_min_version; /* Indicates at minimum which
+ * version of firmware must be
+ * available to satisfy compliance
+ */
+};
+
/** 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 _pad1;
- uint32_t ih_img_size; /* Does not include header. */
- uint32_t ih_flags; /* IMAGE_F_[...]. */
+ uint16_t ih_hdr_size; /* Size of image header (bytes). */
+ uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */
+ uint32_t ih_img_size; /* Does not include header. */
+ uint32_t ih_flags; /* IMAGE_F_[...]. */
struct image_version ih_ver;
- uint32_t _pad2;
+ uint32_t _pad1;
};
/** Image TLV header. All fields in little endian. */
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index b2a07d0..a2fd7e3 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -741,3 +741,38 @@
flash_area_close(fap);
return rc;
}
+
+#if (BOOT_IMAGE_NUMBER > 1)
+/**
+ * Check if the version of the image is not older than required.
+ *
+ * @param req Required minimal image version.
+ * @param ver Version of the image to be checked.
+ *
+ * @return 0 if the version is sufficient, nonzero otherwise.
+ */
+int
+boot_is_version_sufficient(struct image_version *req,
+ struct image_version *ver)
+{
+ if (ver->iv_major > req->iv_major) {
+ return 0;
+ }
+ if (ver->iv_major < req->iv_major) {
+ return BOOT_EBADVERSION;
+ }
+ /* The major version numbers are equal. */
+ if (ver->iv_minor > req->iv_minor) {
+ return 0;
+ }
+ if (ver->iv_minor < req->iv_minor) {
+ return BOOT_EBADVERSION;
+ }
+ /* The minor version numbers are equal. */
+ if (ver->iv_revision < req->iv_revision) {
+ return BOOT_EBADVERSION;
+ }
+
+ return 0;
+}
+#endif /* BOOT_IMAGE_NUMBER > 1 */
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 09d5fac..5f86c7b 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -48,13 +48,14 @@
struct flash_area;
-#define BOOT_EFLASH 1
-#define BOOT_EFILE 2
-#define BOOT_EBADIMAGE 3
-#define BOOT_EBADVECT 4
-#define BOOT_EBADSTATUS 5
-#define BOOT_ENOMEM 6
-#define BOOT_EBADARGS 7
+#define BOOT_EFLASH 1
+#define BOOT_EFILE 2
+#define BOOT_EBADIMAGE 3
+#define BOOT_EBADVECT 4
+#define BOOT_EBADSTATUS 5
+#define BOOT_ENOMEM 6
+#define BOOT_EBADARGS 7
+#define BOOT_EBADVERSION 8
#define BOOT_TMPBUF_SZ 256
@@ -241,6 +242,10 @@
const uint8_t *enckey);
int boot_read_enc_key(uint8_t slot, uint8_t *enckey);
#endif
+#if (BOOT_IMAGE_NUMBER > 1)
+int boot_is_version_sufficient(struct image_version *req,
+ struct image_version *ver);
+#endif
/*
* Accessors for the contents of struct boot_loader_state.
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index 9cff0ac..b4defb2 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -17,6 +17,10 @@
* under the License.
*/
+/*
+ * Modifications are Copyright (c) 2019 Arm Limited.
+ */
+
#include <assert.h>
#include <stddef.h>
#include <inttypes.h>
@@ -78,12 +82,20 @@
}
#endif
- /*
- * Hash is computed over image header and image itself. No TLV is
- * included ATM.
- */
+ /* Hash is computed over image header and image itself. */
hdr_size = hdr->ih_hdr_size;
size = hdr->ih_img_size + hdr_size;
+
+#if (MCUBOOT_IMAGE_NUMBER > 1)
+ /* If dependency TLVs are present then the TLV info header and the
+ * dependency TLVs are also protected and have to be included in the hash
+ * calculation.
+ */
+ if (hdr->ih_protect_tlv_size != 0) {
+ size += hdr->ih_protect_tlv_size;
+ }
+#endif
+
for (off = 0; off < size; off += blk_sz) {
blk_sz = size - off;
if (blk_sz > tmp_buf_sz) {
@@ -212,7 +224,6 @@
}
/* The TLVs come after the image. */
- /* After image there are TLVs. */
off = hdr->ih_img_size + hdr->ih_hdr_size;
rc = flash_area_read(fap, off, &info, sizeof(info));
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 4c94fa7..191171f 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -1608,6 +1608,198 @@
}
#endif /* !MCUBOOT_OVERWRITE_ONLY */
+#if (BOOT_IMAGE_NUMBER > 1)
+/**
+ * Check the image dependency whether it is satisfied and modify
+ * the swap type if necessary.
+ *
+ * @param dep Image dependency which has to be verified.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_verify_single_dependency(struct image_dependency *dep)
+{
+ struct image_version *dep_version;
+ size_t dep_slot;
+ int rc;
+
+ /* Determine the source of the image which is the subject of
+ * the dependency and get it's version. */
+ dep_slot = (boot_data.swap_type[dep->image_id] != BOOT_SWAP_TYPE_NONE) ?
+ BOOT_SECONDARY_SLOT : BOOT_PRIMARY_SLOT;
+ dep_version = &boot_data.imgs[dep->image_id][dep_slot].hdr.ih_ver;
+
+ rc = boot_is_version_sufficient(&dep->image_min_version, dep_version);
+ if (rc != 0) {
+ /* Dependency not satisfied.
+ * Modify the swap type to decrease the version number of the image
+ * (which will be located in the primary slot after the boot process),
+ * consequently the number of unsatisfied dependencies will be
+ * decreased or remain the same.
+ */
+ switch (BOOT_SWAP_TYPE(&boot_data)) {
+ case BOOT_SWAP_TYPE_TEST:
+ case BOOT_SWAP_TYPE_PERM:
+ BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_NONE;
+ break;
+ case BOOT_SWAP_TYPE_NONE:
+ BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_REVERT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Read all dependency TLVs of an image from the flash and verify
+ * one after another to see if they are all satisfied.
+ *
+ * @param slot Image slot number.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_verify_all_dependency(uint32_t slot)
+{
+ const struct flash_area *fap;
+ struct image_header *hdr;
+ struct image_tlv_info info;
+ struct image_tlv tlv;
+ struct image_dependency dep;
+ uint32_t off;
+ uint32_t end;
+ bool dep_tlvs_found = false;
+ int rc;
+
+ rc = flash_area_open(flash_area_id_from_image_slot(slot), &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ hdr = boot_img_hdr(&boot_data, slot);
+ /* The TLVs come after the image. */
+ off = hdr->ih_hdr_size + hdr->ih_img_size;
+
+ /* The TLV area always starts with an image_tlv_info structure. */
+ rc = flash_area_read(fap, off, &info, sizeof(info));
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+ end = off + info.it_tlv_tot;
+ off += sizeof(info);
+
+ /* Traverse through all of the TLVs to find the dependency TLVs. */
+ for (; off < end; off += sizeof(tlv) + tlv.it_len) {
+ rc = flash_area_read(fap, off, &tlv, sizeof(tlv));
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ if (tlv.it_type == IMAGE_TLV_DEPENDENCY) {
+ if (!dep_tlvs_found) {
+ dep_tlvs_found = true;
+ }
+
+ if (tlv.it_len != sizeof(dep)) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ rc = flash_area_read(fap, off + sizeof(tlv), &dep, tlv.it_len);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ /* Verify dependency and modify the swap type if not satisfied. */
+ rc = boot_verify_single_dependency(&dep);
+ if (rc != 0) {
+ /* Dependency not satisfied. */
+ goto done;
+ }
+
+ /* Dependency satisfied, no action needed.
+ * Continue with the next TLV entry.
+ */
+ } else if (dep_tlvs_found) {
+ /* The dependency TLVs are contiguous in the TLV area. If a
+ * dependency had already been found and the last read TLV
+ * has a different type then there are no more dependency TLVs.
+ * The search can be finished.
+ */
+ break;
+ }
+ }
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+/**
+ * Verify whether the image dependencies in the TLV area are
+ * all satisfied and modify the swap type if necessary.
+ *
+ * @return 0 if all dependencies are satisfied,
+ * nonzero otherwise.
+ */
+static int
+boot_verify_single_image_dependency(void)
+{
+ size_t slot;
+
+ /* Determine the source of the dependency TLVs. Those dependencies have to
+ * be checked which belong to the image that will be located in the primary
+ * slot after the firmware update process.
+ */
+ if (BOOT_SWAP_TYPE(&boot_data) != BOOT_SWAP_TYPE_NONE &&
+ BOOT_SWAP_TYPE(&boot_data) != BOOT_SWAP_TYPE_FAIL) {
+ slot = BOOT_SECONDARY_SLOT;
+ } else {
+ slot = BOOT_PRIMARY_SLOT;
+ }
+
+ return boot_verify_all_dependency(slot);
+}
+
+/**
+ * Iterate over all the images and verify whether the image dependencies in the
+ * TLV area are all satisfied and update the related swap type if necessary.
+ */
+static void
+boot_verify_all_image_dependency(void)
+{
+ current_image = 0;
+ int rc;
+
+ while (current_image < BOOT_IMAGE_NUMBER) {
+ rc = boot_verify_single_image_dependency();
+ if ( rc == 0) {
+ /* All dependencies've been satisfied, continue with next image. */
+ current_image++;
+ } else if (rc == BOOT_EBADVERSION) {
+ /* Dependency check needs to be restarted. */
+ current_image = 0;
+ } else {
+ /* Other error happened, images are inconsistent */
+ return;
+ }
+ }
+}
+#endif /* (BOOT_IMAGE_NUMBER > 1) */
+
/**
* Performs a clean (not aborted) image update.
*
@@ -1966,6 +2158,13 @@
boot_prepare_image_for_update(&bs);
}
+#if (BOOT_IMAGE_NUMBER > 1)
+ /* Iterate over all the images and verify whether the image dependencies
+ * are all satisfied and update swap type if necessary.
+ */
+ boot_verify_all_image_dependency();
+#endif
+
/* Iterate over all the images. At this point there are no aborted swaps
* and the swap types are determined for each image. By the end of the loop
* all required update operations will have been finished.