- 
                Notifications
    You must be signed in to change notification settings 
- Fork 835
bootutil: Add manifest-based loader for Direct XIP #2511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| /* | ||
| * Copyright (c) 2025 Nordic Semiconductor ASA | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|  | ||
| #ifndef __MCUBOOT_MANIFEST_H__ | ||
| #define __MCUBOOT_MANIFEST_H__ | ||
|  | ||
| /** | ||
| * @file mcuboot_manifest.h | ||
| * | ||
| * @note This file is only used when MCUBOOT_MANIFEST_UPDATES is enabled. | ||
| */ | ||
|  | ||
| #include <stdint.h> | ||
| #include "bootutil/bootutil.h" | ||
| #include "bootutil/crypto/sha.h" | ||
|  | ||
| #ifndef __packed | ||
| #define __packed __attribute__((__packed__)) | ||
| #endif | ||
|  | ||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|  | ||
| /** Manifest structure for image updates. */ | ||
| struct mcuboot_manifest { | ||
| uint32_t format; | ||
| uint32_t image_count; | ||
| /* Skip a digest of the MCUBOOT_MANIFEST_IMAGE_NUMBER image. */ | ||
| uint8_t image_hash[BOOT_IMAGE_NUMBER - 1][IMAGE_HASH_SIZE]; | ||
| } __packed; | ||
|  | ||
| /** | ||
| * @brief Check if the specified manifest has the correct format. | ||
| * | ||
| * @param[in] manifest The reference to the manifest structure. | ||
| * | ||
| * @return true on success. | ||
| */ | ||
| static inline bool bootutil_verify_manifest(const struct mcuboot_manifest *manifest) | ||
| { | ||
| if (manifest == NULL) { | ||
| return false; | ||
| } | ||
|  | ||
| /* Currently only the simplest manifest format is supported */ | ||
| if (manifest->format != 0x1) { | ||
| return false; | ||
| } | ||
|  | ||
| if (manifest->image_count != BOOT_IMAGE_NUMBER - 1) { | ||
| return false; | ||
| } | ||
|  | ||
| return true; | ||
| } | ||
|  | ||
| /** | ||
| * @brief Get the image hash from the manifest. | ||
| * | ||
| * @param[in] manifest The reference to the manifest structure. | ||
| * @param[in] image_index The index of the image to get the hash for. | ||
| * Must be in range <0, BOOT_IMAGE_NUMBER - 1>, but | ||
| * must not be equal to MCUBOOT_MANIFEST_IMAGE_NUMBER. | ||
| * | ||
| * @return A pointer to the image hash, or NULL if the image_index is out of range | ||
| * of allowed values. | ||
| */ | ||
| static inline const uint8_t *bootutil_get_image_hash(const struct mcuboot_manifest *manifest, | ||
| uint32_t image_index) | ||
| { | ||
| if (!bootutil_verify_manifest(manifest)) { | ||
| return NULL; | ||
| } | ||
|  | ||
| if (image_index >= BOOT_IMAGE_NUMBER) { | ||
| return NULL; | ||
| } | ||
|  | ||
| if (image_index < MCUBOOT_MANIFEST_IMAGE_NUMBER) { | ||
| return manifest->image_hash[image_index]; | ||
| } else if (image_index > MCUBOOT_MANIFEST_IMAGE_NUMBER) { | ||
| return manifest->image_hash[image_index - 1]; | ||
| } | ||
|  | ||
| return NULL; | ||
| } | ||
| 
      Comment on lines
    
      +72
     to 
      +90
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure this should be able to return NULL in any case. | ||
|  | ||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|  | ||
| #endif /* __MCUBOOT_MANIFEST_H__ */ | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -44,6 +44,10 @@ | |
| #include "bootutil/enc_key.h" | ||
| #endif | ||
|  | ||
| #ifdef MCUBOOT_MANIFEST_UPDATES | ||
| #include "bootutil/mcuboot_manifest.h" | ||
| #endif /* MCUBOOT_MANIFEST_UPDATES */ | ||
|  | ||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|  | @@ -271,6 +275,14 @@ struct boot_loader_state { | |
| #endif | ||
| } slot_usage[BOOT_IMAGE_NUMBER]; | ||
| #endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ | ||
|  | ||
| #if defined(MCUBOOT_MANIFEST_UPDATES) | ||
| struct mcuboot_manifest manifest[BOOT_NUM_SLOTS]; | ||
| bool manifest_valid[BOOT_NUM_SLOTS]; | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move that into the  | ||
| #if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) | ||
| enum boot_slot matching_manifest[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS]; | ||
| #endif | ||
| #endif | ||
| }; | ||
|  | ||
| struct boot_sector_buffer { | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -50,6 +50,10 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); | |
| #include "bootutil/mcuboot_uuid.h" | ||
| #endif /* MCUBOOT_UUID_VID || MCUBOOT_UUID_CID */ | ||
|  | ||
| #ifdef MCUBOOT_MANIFEST_UPDATES | ||
| #include "bootutil/mcuboot_manifest.h" | ||
| #endif /* MCUBOOT_MANIFEST_UPDATES */ | ||
|  | ||
| #ifdef MCUBOOT_ENC_IMAGES | ||
| #include "bootutil/enc_key.h" | ||
| #endif | ||
|  | @@ -206,7 +210,7 @@ bootutil_img_validate(struct boot_loader_state *state, | |
| { | ||
| #if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || \ | ||
| (defined(EXPECTED_SIG_TLV) && defined(MCUBOOT_BUILTIN_KEY)) || \ | ||
| defined(MCUBOOT_HW_ROLLBACK_PROT) || \ | ||
| defined(MCUBOOT_HW_ROLLBACK_PROT) || defined(MCUBOOT_MANIFEST_UPDATES) || \ | ||
| defined(MCUBOOT_UUID_VID) || defined(MCUBOOT_UUID_CID) | ||
| int image_index = (state == NULL ? 0 : BOOT_CURR_IMG(state)); | ||
| #endif | ||
|  | @@ -244,6 +248,12 @@ bootutil_img_validate(struct boot_loader_state *state, | |
| uint32_t img_security_cnt = 0; | ||
| FIH_DECLARE(security_counter_valid, FIH_FAILURE); | ||
| #endif | ||
| #ifdef MCUBOOT_MANIFEST_UPDATES | ||
| bool manifest_found = false; | ||
| bool manifest_valid = false; | ||
| uint8_t slot = (flash_area_get_id(fap) == FLASH_AREA_IMAGE_SECONDARY(image_index) ? 1 : 0); | ||
| const uint8_t *image_hash = NULL; | ||
| #endif | ||
| #ifdef MCUBOOT_UUID_VID | ||
| struct image_uuid img_uuid_vid = {0x00}; | ||
| FIH_DECLARE(uuid_vid_valid, FIH_FAILURE); | ||
|  | @@ -356,6 +366,82 @@ bootutil_img_validate(struct boot_loader_state *state, | |
| goto out; | ||
| } | ||
|  | ||
| #ifdef MCUBOOT_MANIFEST_UPDATES | ||
| if (image_index == MCUBOOT_MANIFEST_IMAGE_NUMBER) { | ||
| if (!state->manifest_valid[slot]) { | ||
| /* Manifest TLV must be processed before any of the image's hash TLV. */ | ||
| BOOT_LOG_INF("bootutil_img_validate: image rejected, manifest not found before image %d hash", | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't that at least warning? It ends up in rc = -1, so basically fails the function. | ||
| image_index); | ||
| rc = -1; | ||
| goto out; | ||
| } | ||
| /* Manifest image does not have hash in the manifest. */ | ||
| image_hash_valid = 1; | ||
| break; | ||
| } | ||
| #if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) | ||
| state->matching_manifest[image_index][slot] = BOOT_SLOT_NONE; | ||
| /* Try to match with the primary manifest first. */ | ||
| if (state->manifest_valid[BOOT_SLOT_PRIMARY]) { | ||
| image_hash = bootutil_get_image_hash(&state->manifest[BOOT_SLOT_PRIMARY], image_index); | ||
| if (image_hash != NULL) { | ||
| FIH_CALL(boot_fih_memequal, fih_rc, hash, image_hash, sizeof(hash)); | ||
| if (FIH_EQ(fih_rc, FIH_SUCCESS)) { | ||
| state->matching_manifest[image_index][slot] = BOOT_SLOT_PRIMARY; | ||
| } | ||
| } | ||
| } | ||
|  | ||
| /* Try to match with the secondary manifest if not matched with the primary. */ | ||
| if(state->matching_manifest[image_index][slot] == BOOT_SLOT_NONE && | ||
| state->manifest_valid[BOOT_SLOT_SECONDARY]) { | ||
| image_hash = bootutil_get_image_hash(&state->manifest[BOOT_SLOT_SECONDARY], image_index); | ||
| if (image_hash != NULL) { | ||
| FIH_CALL(boot_fih_memequal, fih_rc, hash, image_hash, sizeof(hash)); | ||
| if (FIH_EQ(fih_rc, FIH_SUCCESS)) { | ||
| state->matching_manifest[image_index][slot] = BOOT_SLOT_SECONDARY; | ||
| } | ||
| } | ||
| } | ||
|  | ||
| /* No matching manifest found. */ | ||
| if (state->matching_manifest[image_index][slot] == BOOT_SLOT_NONE) { | ||
| BOOT_LOG_INF("bootutil_img_validate: image rejected, no valid manifest for image %d slot %d", | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is either warning or error, we are failing function here. | ||
| image_index, slot); | ||
| rc = -1; | ||
| goto out; | ||
| } else { | ||
| BOOT_LOG_INF("bootutil_img_validate: image %d slot %d matches manifest in slot %d", | ||
| image_index, slot, state->matching_manifest[image_index][slot]); | ||
| } | ||
| #else /* MCUBOOT_SWAP_USING_SCRATCH || MCUBOOT_SWAP_USING_MOVE || MCUBOOT_SWAP_USING_OFFSET */ | ||
| /* Manifest image for a given slot must precede any of other images. */ | ||
| if (!state->manifest_valid[slot]) { | ||
| /* Manifest TLV must be processed before any of the image's hash TLV. */ | ||
| BOOT_LOG_INF("bootutil_img_validate: image rejected, no valid manifest for slot %d", | ||
| slot); | ||
| rc = -1; | ||
| goto out; | ||
| } | ||
|  | ||
| /* Any image, not described by the manifest is considered as invalid. */ | ||
| image_hash = bootutil_get_image_hash(&state->manifest[slot], image_index); | ||
| if (image_hash == NULL) { | ||
| /* Manifest TLV must be processed before any of the image's hash TLV. */ | ||
| BOOT_LOG_INF("bootutil_img_validate: image rejected, no valid manifest for image %d slot %d", | ||
| image_index, slot); | ||
| rc = -1; | ||
| goto out; | ||
| } | ||
|  | ||
| FIH_CALL(boot_fih_memequal, fih_rc, hash, image_hash, sizeof(hash)); | ||
| if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { | ||
| BOOT_LOG_INF("bootutil_img_validate: image rejected, hash does not match manifest contents"); | ||
| FIH_SET(fih_rc, FIH_FAILURE); | ||
| goto out; | ||
| } | ||
| #endif /* MCUBOOT_SWAP_USING_SCRATCH || MCUBOOT_SWAP_USING_MOVE || MCUBOOT_SWAP_USING_OFFSET */ | ||
| #endif /* MCUBOOT_MANIFEST_UPDATES */ | ||
| image_hash_valid = 1; | ||
| break; | ||
| } | ||
|  | @@ -484,6 +570,39 @@ bootutil_img_validate(struct boot_loader_state *state, | |
| break; | ||
| } | ||
| #endif /* MCUBOOT_HW_ROLLBACK_PROT */ | ||
| #ifdef MCUBOOT_MANIFEST_UPDATES | ||
| case IMAGE_TLV_MANIFEST: | ||
| { | ||
| /* There can be only one manifest and must be a part of image with specific index. */ | ||
| if (manifest_found || image_index != MCUBOOT_MANIFEST_IMAGE_NUMBER || | ||
| len != sizeof(struct mcuboot_manifest)) { | ||
| BOOT_LOG_INF("bootutil_img_validate: image %d slot %d rejected, unexpected manifest TLV", | ||
| image_index, slot); | ||
| rc = -1; | ||
| goto out; | ||
| } | ||
|  | ||
| manifest_found = true; | ||
|  | ||
| rc = LOAD_IMAGE_DATA(hdr, fap, off, &state->manifest[slot], sizeof(struct mcuboot_manifest)); | ||
| if (rc) { | ||
| BOOT_LOG_INF("bootutil_img_validate: slot %d rejected, unable to load manifest", slot); | ||
| goto out; | ||
| } | ||
|  | ||
| manifest_valid = bootutil_verify_manifest(&state->manifest[slot]); | ||
| if (!manifest_valid) { | ||
| BOOT_LOG_INF("bootutil_img_validate: slot %d rejected, invalid manifest contents", slot); | ||
| rc = -1; | ||
| goto out; | ||
| } | ||
|  | ||
| /* The image's manifest has been successfully verified. */ | ||
| state->manifest_valid[slot] = true; | ||
| BOOT_LOG_INF("bootutil_img_validate: slot %d manifest verified", slot); | ||
| break; | ||
| } | ||
| #endif | ||
| #ifdef MCUBOOT_UUID_VID | ||
| case IMAGE_TLV_UUID_VID: | ||
| { | ||
|  | @@ -564,6 +683,13 @@ bootutil_img_validate(struct boot_loader_state *state, | |
| } | ||
| #endif | ||
|  | ||
| #ifdef MCUBOOT_MANIFEST_UPDATES | ||
| if (image_index == MCUBOOT_MANIFEST_IMAGE_NUMBER && (!manifest_found || !manifest_valid)) { | ||
| BOOT_LOG_INF("bootutil_img_validate: slot %d rejected, manifest missing or invalid", slot); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this error? | ||
| rc = -1; | ||
| goto out; | ||
| } | ||
| #endif | ||
| #ifdef MCUBOOT_UUID_VID | ||
| if (FIH_NOT_EQ(uuid_vid_valid, FIH_SUCCESS)) { | ||
| rc = -1; | ||
|  | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
() around arithmetic.