diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 000000000..e986738ff --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,31 @@ +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + branches: + - main + +jobs: + backport: + name: Backport + runs-on: ubuntu-22.04 + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged && + ( + github.event.action == 'closed' || + ( + github.event.action == 'labeled' && + contains(github.event.label.name, 'backport') + ) + ) + steps: + - name: Backport + uses: zephyrproject-rtos/action-backport@v2.0.3-3 + with: + github_token: ${{ secrets.NCS_GITHUB_TOKEN }} + issue_labels: Backport + labels_template: '["Backport"]' diff --git a/.github/workflows/commit-tags.yml b/.github/workflows/commit-tags.yml new file mode 100644 index 000000000..534ed5b58 --- /dev/null +++ b/.github/workflows/commit-tags.yml @@ -0,0 +1,28 @@ +name: Commit tags + +on: + pull_request: + types: [synchronize, opened, reopened, edited, labeled, unlabeled, + milestoned, demilestoned, assigned, unassigned, ready_for_review, + review_requested] + +jobs: + commit_tags: + runs-on: ubuntu-22.04 + name: Run commit tags checks on patch series (PR) + steps: + - name: Update PATH for west + run: | + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Checkout the code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Run the commit tags + uses: nrfconnect/action-commit-tags@main + with: + target: . + upstream: mcu-tools/mcuboot/main diff --git a/.github/workflows/manifest-PR.yml b/.github/workflows/manifest-PR.yml new file mode 100644 index 000000000..473301146 --- /dev/null +++ b/.github/workflows/manifest-PR.yml @@ -0,0 +1,17 @@ +name: handle manifest PR +on: + pull_request_target: + types: [opened, synchronize, closed, reopened] + branches: + - main + + +jobs: + call-manifest-pr-action: + runs-on: ubuntu-latest + steps: + - name: handle manifest PR + uses: nrfconnect/action-manifest-pr@main + with: + token: ${{ secrets.NCS_GITHUB_TOKEN }} + manifest-pr-title-details: ${{ github.event.pull_request.title }} diff --git a/boot/bootutil/include/bootutil/boot_hooks.h b/boot/bootutil/include/bootutil/boot_hooks.h index f5b10e8c7..96414e3df 100644 --- a/boot/bootutil/include/bootutil/boot_hooks.h +++ b/boot/bootutil/include/bootutil/boot_hooks.h @@ -282,6 +282,6 @@ int flash_area_get_device_id_hook(const struct flash_area *fa, * @return 0 if a slot was requested; * BOOT_HOOK_REGULAR follow the normal execution path. */ -int boot_find_next_slot_hook(struct boot_loader_state *state, uint8_t image, uint32_t *active_slot); +int boot_find_next_slot_hook(struct boot_loader_state *state, uint8_t image, enum boot_slot *active_slot); #endif /*H_BOOTUTIL_HOOKS*/ diff --git a/boot/bootutil/include/bootutil/boot_request.h b/boot/bootutil/include/bootutil/boot_request.h new file mode 100644 index 000000000..c92a91d4e --- /dev/null +++ b/boot/bootutil/include/bootutil/boot_request.h @@ -0,0 +1,106 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Nordic Semiconductor ASA + */ + +#ifndef __BOOT_REQUEST_H__ +#define __BOOT_REQUEST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** Special value, indicating that there is no preferred slot. */ +#define BOOT_REQUEST_NO_PREFERRED_SLOT UINT32_MAX + +/** + * @brief Request a bootloader to confirm the specified slot of an image. + * + * @param[in] image Image number. + * @param[in] slot Slot number. + * + * @return 0 if requested, negative error code otherwise. + */ +int boot_request_confirm_slot(uint8_t image, enum boot_slot slot); + +/** + * @brief Request a bootloader to boot the specified slot of an image. + * + * @param[in] image Image number. + * @param[in] slot Slot number. + * + * @return 0 if requested, negative error code otherwise. + */ +int boot_request_set_preferred_slot(uint8_t image, enum boot_slot slot); + +/** + * @brief Request a bootloader to boot recovery image. + * + * @return 0 if requested, negative error code otherwise. + */ +int boot_request_enter_recovery(void); + +/** + * @brief Request a bootloader to boot firmware loader image. + * + * @return 0 if requested, negative error code otherwise. + */ +int boot_request_enter_firmware_loader(void); + +/** + * @brief Check if there is a request to confirm the specified slot of an image. + * + * @param[in] image Image number. + * @param[in] slot Slot number. + * + * @return true if requested, false otherwise. + */ +bool boot_request_check_confirmed_slot(uint8_t image, enum boot_slot slot); + +/** + * @brief Find if there is a request to boot certain slot of the specified image. + * + * @param[in] image Image number. + * + * @return slot number if requested, BOOT_SLOT_NONE otherwise. + */ +enum boot_slot boot_request_get_preferred_slot(uint8_t image); + +/** + * @brief Check if there is a request to boot recovery image. + * + * @return true if requested, false otherwise. + */ +bool boot_request_detect_recovery(void); + +/** + * @brief Check if there is a request to boot firmware loader image. + * + * @return true if requested, false otherwise. + */ +bool boot_request_detect_firmware_loader(void); + +/** + * @brief Initialize boot requests module. + * + * @return 0 if successful, negative error code otherwise. + */ +int boot_request_init(void); + +/** + * @brief Clear/drop all requests. + * + * @return 0 if successful, negative error code otherwise. + */ +int boot_request_clear(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOT_REQUEST_H__ */ diff --git a/boot/bootutil/include/bootutil/crypto/ecdsa.h b/boot/bootutil/include/bootutil/crypto/ecdsa.h index 8a1463e57..7fc5474e2 100644 --- a/boot/bootutil/include/bootutil/crypto/ecdsa.h +++ b/boot/bootutil/include/bootutil/crypto/ecdsa.h @@ -34,6 +34,7 @@ #if (defined(MCUBOOT_USE_TINYCRYPT) + \ defined(MCUBOOT_USE_CC310) + \ + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ defined(MCUBOOT_USE_PSA_OR_MBED_TLS)) != 1 #error "One crypto backend must be defined: either CC310/TINYCRYPT/MBED_TLS/PSA_CRYPTO" #endif @@ -72,12 +73,18 @@ #include "bootutil/crypto/common.h" #endif +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + #include + #define NUM_ECC_BYTES (256 / 8) +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + #ifdef __cplusplus extern "C" { #endif #if (defined(MCUBOOT_USE_TINYCRYPT) || defined(MCUBOOT_USE_MBED_TLS) || \ - defined(MCUBOOT_USE_CC310)) && !defined(MCUBOOT_USE_PSA_CRYPTO) + defined(MCUBOOT_USE_CC310) || defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO)) \ + && !defined(MCUBOOT_USE_PSA_CRYPTO) /* * Declaring these like this adds NULL termination. */ @@ -129,8 +136,7 @@ static int bootutil_import_key(uint8_t **cp, uint8_t *end) } #endif /* (MCUBOOT_USE_TINYCRYPT || MCUBOOT_USE_MBED_TLS || MCUBOOT_USE_CC310) && !MCUBOOT_USE_PSA_CRYPTO */ -#if defined(MCUBOOT_USE_TINYCRYPT) -#ifndef MCUBOOT_ECDSA_NEED_ASN1_SIG +#ifndef MCUBOOT_USE_PSA_CRYPTO /* * cp points to ASN1 string containing an integer. * Verify the tag, and that the length is 32 bytes. Helper function. @@ -180,8 +186,9 @@ static int bootutil_decode_sig(uint8_t signature[NUM_ECC_BYTES * 2], uint8_t *cp } return 0; } -#endif /* not MCUBOOT_ECDSA_NEED_ASN1_SIG */ +#endif /* !MCUBOOT_USE_PSA_CRYPTO */ +#if defined(MCUBOOT_USE_TINYCRYPT) typedef uintptr_t bootutil_ecdsa_context; static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) { @@ -250,8 +257,12 @@ static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, { (void)ctx; (void)pk_len; - (void)sig_len; (void)hash_len; + uint8_t dsig[2 * NUM_ECC_BYTES]; + + if (bootutil_decode_sig(dsig, sig, sig + sig_len)) { + return -1; + } /* Only support uncompressed keys. */ if (pk[0] != 0x04) { @@ -259,7 +270,7 @@ static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, } pk++; - return cc310_ecdsa_verify_secp256r1(hash, pk, sig, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE); + return cc310_ecdsa_verify_secp256r1(hash, pk, dsig, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE); } static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, @@ -464,6 +475,7 @@ static int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, } #endif /* !MCUBOOT_BUILTIN_KEY */ +#if !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) /* Verify the signature against the provided hash. The signature gets parsed from * the encoding first, then PSA Crypto has a dedicated API for ECDSA verification */ @@ -482,6 +494,55 @@ static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, return (int) psa_verify_hash(ctx->key_id, PSA_ALG_ECDSA(ctx->required_algorithm), hash, hlen, reformatted_signature, 2*ctx->curve_byte_count); } +#else /* !CONFIG_NCS_BOOT_SIGNATURE_USING_ITS */ + +static const psa_key_id_t builtin_key_ids[] = { + 0x40022100, + 0x40022101, + 0x40022102, + 0x40022103 +}; + +#define BOOT_SIGNATURE_BUILTIN_KEY_SLOTS ARRAY_SIZE(builtin_key_ids) + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hlen, + uint8_t *sig, size_t slen) +{ + (void)pk; + (void)pk_len; + (void)slen; + psa_status_t status = PSA_ERROR_BAD_STATE; + + /* Initialize PSA Crypto */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", status); + return 1; + } + + uint8_t reformatted_signature[96] = {0}; /* Enough for P-384 signature sizes */ + parse_signature_from_rfc5480_encoding(sig, ctx->curve_byte_count, reformatted_signature); + + status = PSA_ERROR_BAD_STATE; + + for (int i = 0; i < BOOT_SIGNATURE_BUILTIN_KEY_SLOTS; ++i) { + psa_key_id_t kid = builtin_key_ids[i]; + + status = psa_verify_hash(kid, PSA_ALG_ECDSA(ctx->required_algorithm), + hash, hlen, reformatted_signature, 2*ctx->curve_byte_count); + if (status == PSA_SUCCESS) { + break; + } + BOOT_LOG_ERR("ECDSA signature verification failed %d", status); + } + + return status == PSA_SUCCESS ? 0 : 2; +} + +#endif /* !CONFIG_NCS_BOOT_SIGNATURE_USING_ITS */ + #elif defined(MCUBOOT_USE_MBED_TLS) typedef mbedtls_ecdsa_context bootutil_ecdsa_context; @@ -615,6 +676,49 @@ static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, #endif /* MCUBOOT_USE_MBED_TLS */ +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) +typedef uintptr_t bootutil_ecdsa_context; +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + (void)ctx; + (void)pk_len; + (void)hash_len; + uint8_t dsig[2 * NUM_ECC_BYTES]; + + if (bootutil_decode_sig(dsig, sig, sig + sig_len)) { + return -1; + } + + /* Only support uncompressed keys. */ + if (pk[0] != 0x04) { + return -1; + } + pk++; + + return bl_secp256r1_validate(hash, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE, pk, dsig); +} + +static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp,uint8_t *end) +{ + (void)ctx; + return bootutil_import_key(cp, end); +} +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/crypto/sha.h b/boot/bootutil/include/bootutil/crypto/sha.h index 6a009ff95..b83a3ec40 100644 --- a/boot/bootutil/include/bootutil/crypto/sha.h +++ b/boot/bootutil/include/bootutil/crypto/sha.h @@ -30,6 +30,7 @@ #if (defined(MCUBOOT_USE_PSA_OR_MBED_TLS) + \ defined(MCUBOOT_USE_TINYCRYPT) + \ + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ defined(MCUBOOT_USE_CC310)) != 1 #error "One crypto backend must be defined: either CC310/MBED_TLS/TINYCRYPT/PSA_CRYPTO" #endif @@ -270,6 +271,37 @@ static inline int bootutil_sha_finish(bootutil_sha_context *ctx, } #endif /* MCUBOOT_USE_CC310 */ +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + +#include + +typedef bl_sha256_ctx_t bootutil_sha_context; + +static inline void bootutil_sha_init(bootutil_sha_context *ctx) +{ + bl_sha256_init(ctx); +} + +static inline void bootutil_sha_drop(bootutil_sha_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + return bl_sha256_update(ctx, data, data_len); +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + bl_sha256_finalize(ctx, output); + return 0; +} +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/key_revocation.h b/boot/bootutil/include/bootutil/key_revocation.h new file mode 100644 index 000000000..d184c9579 --- /dev/null +++ b/boot/bootutil/include/bootutil/key_revocation.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_KEY_REVOCATION_ +#define H_KEY_REVOCATION_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOOT_KEY_REVOKE_OK 0 +#define BOOT_KEY_REVOKE_NOT_READY 1 +#define BOOT_KEY_REVOKE_INVALID 2 +#define BOOT_KEY_REVOKE_FAILED 2 + + +void allow_revoke(void); + +int revoke(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/boot/bootutil/include/bootutil/security_cnt.h b/boot/bootutil/include/bootutil/security_cnt.h index 228ada8dc..ff3a7371c 100644 --- a/boot/bootutil/include/bootutil/security_cnt.h +++ b/boot/bootutil/include/bootutil/security_cnt.h @@ -39,6 +39,15 @@ extern "C" { */ fih_ret boot_nv_security_counter_init(void); +/** + * Checks if the specified image should have a security counter present on it or not + * + * @param image_index Index of the image to check (from 0). + * + * @return FIH_SUCCESS if security counter should be present; FIH_FAILURE if otherwise + */ +fih_ret boot_nv_image_should_have_security_counter(uint32_t image_index); + /** * Reads the stored value of a given image's security counter. * diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 4fc9e8279..ec61f6f9e 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -47,8 +47,9 @@ #include "swap_priv.h" #endif -#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_SWAP_USING_SCRATCH) -#include "swap_priv.h" +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include #endif BOOT_LOG_MODULE_DECLARE(mcuboot); @@ -432,6 +433,18 @@ boot_write_enc_key(const struct flash_area *fap, uint8_t slot, uint32_t bootutil_max_image_size(struct boot_loader_state *state, const struct flash_area *fap) { +#if defined(CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* NSIB is a direct upgrade without any status or trailer, get the full size of the + * primary slot. + */ + const struct flash_area *fap_nsib = BOOT_IMG_AREA(state, 0); + assert(fap_nsib != NULL); + + return flash_area_get_size(fap_nsib); + } +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ + #if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ defined(MCUBOOT_FIRMWARE_LOADER) || \ defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) @@ -475,35 +488,76 @@ boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) fap = BOOT_IMG_AREA(state, slot); assert(fap != NULL); - off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), boot_img_hdr(state, slot))) { + uint32_t tmp_size = 0; - if (flash_area_read(fap, off, &info, sizeof(info))) { - rc = BOOT_EFLASH; - goto done; - } + rc = bootutil_get_img_decomp_size(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off = boot_img_hdr(state, slot)->ih_hdr_size + tmp_size; - protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; - if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { - if (protect_tlv_size != info.it_tlv_tot) { + rc = boot_size_protected_tlvs(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { rc = BOOT_EBADIMAGE; goto done; } - if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + off += tmp_size; + + if (flash_area_read(fap, (BOOT_TLV_OFF(boot_img_hdr(state, slot)) + + boot_img_hdr(state, slot)->ih_protect_tlv_size), &info, + sizeof(info))) { rc = BOOT_EFLASH; goto done; } - } else if (protect_tlv_size != 0) { - rc = BOOT_EBADIMAGE; - goto done; - } - if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { - rc = BOOT_EBADIMAGE; - goto done; + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + info.it_tlv_tot; + } else { +#else + if (1) { +#endif + off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); + + if (flash_area_read(fap, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; } - *size = off + protect_tlv_size + info.it_tlv_tot; rc = 0; done: diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index b97825ed4..784baf7e7 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -253,7 +253,7 @@ struct boot_loader_state { #endif uint8_t swap_type[BOOT_IMAGE_NUMBER]; - uint32_t write_sz; + uint32_t write_sz[BOOT_IMAGE_NUMBER]; #if defined(MCUBOOT_SWAP_USING_OFFSET) uint32_t secondary_offset[BOOT_IMAGE_NUMBER]; @@ -480,7 +480,7 @@ static inline bool boot_u16_safe_add(uint16_t *dest, uint16_t a, uint16_t b) #endif #define BOOT_IMG(state, slot) ((state)->imgs[BOOT_CURR_IMG(state)][(slot)]) #define BOOT_IMG_AREA(state, slot) (BOOT_IMG(state, slot).area) -#define BOOT_WRITE_SZ(state) ((state)->write_sz) +#define BOOT_WRITE_SZ(state) ((state)->write_sz[BOOT_CURR_IMG(state)]) #define BOOT_SWAP_TYPE(state) ((state)->swap_type[BOOT_CURR_IMG(state)]) #define BOOT_TLV_OFF(hdr) ((hdr)->ih_hdr_size + (hdr)->ih_img_size) diff --git a/boot/bootutil/src/bootutil_public.c b/boot/bootutil/src/bootutil_public.c index cb689be3b..f54dd6151 100644 --- a/boot/bootutil/src/bootutil_public.c +++ b/boot/bootutil/src/bootutil_public.c @@ -51,6 +51,11 @@ #include "bootutil_priv.h" #include "bootutil_misc.h" +#if defined(CONFIG_NRF_MCUBOOT_BOOT_REQUEST) && !defined(CONFIG_MCUBOOT) +#include +#define SEND_BOOT_REQUEST +#endif /* CONFIG_NRF_MCUBOOT_BOOT_REQUEST && !CONFIG_MCUBOOT */ + #ifdef CONFIG_MCUBOOT BOOT_LOG_MODULE_DECLARE(mcuboot); #else @@ -503,32 +508,91 @@ boot_write_copy_done(const struct flash_area *fap) return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET); } -#ifndef MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP +#ifdef SEND_BOOT_REQUEST +static int +send_boot_request(uint8_t magic, uint8_t image_ok, bool confirm, int image_id, + enum boot_slot slot_id) +{ + int rc = BOOT_EBADIMAGE; -static int flash_area_to_image(const struct flash_area *fa) + /* Handle write-protected active image. */ + if ((magic == BOOT_MAGIC_GOOD) || (magic == BOOT_MAGIC_UNSET)) { + if (confirm) { + BOOT_LOG_DBG("Confirm image: %d, %d", image_id, slot_id); + if ((image_ok != BOOT_FLAG_SET) || (magic != BOOT_MAGIC_GOOD)) { + rc = boot_request_confirm_slot(image_id, slot_id); + } else { + rc = 0; + } + } else { +#ifdef CONFIG_NCS_MCUBOOT_BOOT_REQUEST_TEST_SETS_BOOT_PREFERENCE + BOOT_LOG_DBG("Set image preference: %d, %d", image_id, slot_id); + rc = boot_request_set_preferred_slot(image_id, slot_id); +#else + rc = 0; +#endif /* CONFIG_NCS_MCUBOOT_BOOT_REQUEST_TEST_SETS_BOOT_PREFERENCE */ + } + if (rc != 0) { + rc = BOOT_EBADIMAGE; + } + } + + return rc; +} +#endif /* SEND_BOOT_REQUEST */ + +#if defined(SEND_BOOT_REQUEST) || (!defined(MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP)) +static int flash_area_to_image_slot(const struct flash_area *fa, uint32_t *slot) { + int id = flash_area_get_id(fa); #if BOOT_IMAGE_NUMBER > 1 uint8_t i = 0; - int id = flash_area_get_id(fa); while (i < BOOT_IMAGE_NUMBER) { - if (FLASH_AREA_IMAGE_PRIMARY(i) == id || (FLASH_AREA_IMAGE_SECONDARY(i) == id)) { + if (FLASH_AREA_IMAGE_PRIMARY(i) == id) { + if (slot != NULL) { + *slot = 0; + } + return i; + } else if (FLASH_AREA_IMAGE_SECONDARY(i) == id) { + if (slot != NULL) { + *slot = 1; + } return i; } ++i; } + + /* Image not found */ + *slot = UINT32_MAX; #else (void)fa; + if (slot != NULL) { + if (FLASH_AREA_IMAGE_PRIMARY(0) == id) { + *slot = 0; + } else if (FLASH_AREA_IMAGE_SECONDARY(0) == id) { + *slot = 1; + } else { + *slot = UINT32_MAX; + } + } #endif return 0; } +#endif /* defined(SEND_BOOT_REQUEST) || (!defined(MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP)) */ +#ifndef MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP int boot_set_next(const struct flash_area *fa, bool active, bool confirm) { struct boot_swap_state slot_state; int rc; + int image_id; + uint32_t slot_id; + + BOOT_LOG_DBG("boot_set_next: fa %p active == %d, confirm == %d", + fa, (int)active, (int)confirm); if (active) { confirm = true; @@ -539,6 +603,16 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm) return rc; } + image_id = flash_area_to_image_slot(fa, &slot_id); + +#ifdef SEND_BOOT_REQUEST + rc = send_boot_request(slot_state.magic, slot_state.image_ok, confirm, + image_id, slot_id); + if ((rc != 0) || active) { + return rc; + } +#endif + switch (slot_state.magic) { case BOOT_MAGIC_GOOD: /* If non-active then swap already scheduled, else confirm needed.*/ @@ -569,7 +643,7 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm) } else { swap_type = BOOT_SWAP_TYPE_TEST; } - rc = boot_write_swap_info(fa, swap_type, flash_area_to_image(fa)); + rc = boot_write_swap_info(fa, swap_type, image_id); } } break; @@ -597,6 +671,10 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm) { struct boot_swap_state slot_state; int rc; +#ifdef SEND_BOOT_REQUEST + int image_id; + uint32_t slot_id; +#endif BOOT_LOG_DBG("boot_set_next: fa %p active == %d, confirm == %d", fa, (int)active, (int)confirm); @@ -615,6 +693,16 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm) return rc; } +#ifdef SEND_BOOT_REQUEST + image_id = flash_area_to_image_slot(fa, &slot_id); + + rc = send_boot_request(slot_state.magic, slot_state.image_ok, confirm, + image_id, slot_id); + if ((rc != 0) || active) { + return rc; + } +#endif + switch (slot_state.magic) { case BOOT_MAGIC_UNSET: /* Magic is needed for MCUboot to even consider booting an image */ diff --git a/boot/bootutil/src/ed25519_psa.c b/boot/bootutil/src/ed25519_psa.c index 5b8a4ed7c..c7e3910b1 100644 --- a/boot/bootutil/src/ed25519_psa.c +++ b/boot/bootutil/src/ed25519_psa.c @@ -12,6 +12,10 @@ #include #include +#include +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +#include +#endif BOOT_LOG_MODULE_REGISTER(ed25519_psa); @@ -19,6 +23,47 @@ BOOT_LOG_MODULE_REGISTER(ed25519_psa); #define EDDSA_KEY_LENGTH 32 #define EDDSA_SIGNAGURE_LENGTH 64 +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +/* List of KMU stored key ids available for MCUboot */ +#define PSA_KEY_INDEX_SIZE 2 + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 || \ + defined(CONFIG_NCS_BOOT_SIGNATURE_KMU_UROT_MAPPING) +#define PSA_KEY_STARTING_ID 226 +#else +#define PSA_KEY_STARTING_ID 242 +#endif + +#define MAKE_PSA_KMU_KEY_ID(id) PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, id) +static psa_key_id_t key_ids[] = { + MAKE_PSA_KMU_KEY_ID(PSA_KEY_STARTING_ID), + MAKE_PSA_KMU_KEY_ID(PSA_KEY_STARTING_ID + PSA_KEY_INDEX_SIZE), + MAKE_PSA_KMU_KEY_ID(PSA_KEY_STARTING_ID + (2 * PSA_KEY_INDEX_SIZE)) +}; + +#define KEY_SLOTS_COUNT CONFIG_BOOT_SIGNATURE_KMU_SLOTS + +#if defined(CONFIG_BOOT_KMU_KEYS_REVOCATION) +#include +static psa_key_id_t *validated_with = NULL; +#endif + +BUILD_ASSERT(CONFIG_BOOT_SIGNATURE_KMU_SLOTS <= ARRAY_SIZE(key_ids), + "Invalid number of KMU slots, up to 3 are supported on nRF54L15"); +#endif + +#if defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) +static const psa_key_id_t key_ids[] = { + 0x40022100, + 0x40022101, + 0x40022102, + 0x40022103 +}; + +#define KEY_SLOTS_COUNT ARRAY_SIZE(key_ids) +#endif + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) int ED25519_verify(const uint8_t *message, size_t message_len, const uint8_t signature[EDDSA_SIGNAGURE_LENGTH], const uint8_t public_key[EDDSA_KEY_LENGTH]) @@ -71,3 +116,101 @@ int ED25519_verify(const uint8_t *message, size_t message_len, return ret; } +#else +int ED25519_verify(const uint8_t *message, size_t message_len, + const uint8_t signature[EDDSA_SIGNAGURE_LENGTH], + const uint8_t public_key[EDDSA_KEY_LENGTH]) +{ + ARG_UNUSED(public_key); + /* Set to any error */ + psa_status_t status = PSA_ERROR_BAD_STATE; + + /* Initialize PSA Crypto */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", status); + return 0; + } + + status = PSA_ERROR_BAD_STATE; + + for (int i = 0; i < KEY_SLOTS_COUNT; ++i) { + psa_key_id_t kid = key_ids[i]; + + status = psa_verify_message(kid, PSA_ALG_PURE_EDDSA, message, + message_len, signature, + EDDSA_SIGNAGURE_LENGTH); + if (status == PSA_SUCCESS) { +#if defined(CONFIG_BOOT_KMU_KEYS_REVOCATION) + validated_with = key_ids + i; +#endif + return 1; + } + + } + + BOOT_LOG_ERR("ED25519 signature verification failed %d", status); + + return 0; +} +#if defined(CONFIG_BOOT_KMU_KEYS_REVOCATION) +int exec_revoke(void) +{ + int ret = BOOT_KEY_REVOKE_OK; + psa_status_t status = psa_crypto_init(); + + if (!validated_with) { + ret = BOOT_KEY_REVOKE_INVALID; + goto out; + } + + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed with error %d", status); + ret = BOOT_KEY_REVOKE_FAILED; + goto out; + } + for (int i = 0; i < CONFIG_BOOT_SIGNATURE_KMU_SLOTS; i++) { + if ((key_ids + i) == validated_with) { + break; + } + BOOT_LOG_DBG("Invalidating key ID %d", i); + + status = psa_destroy_key(key_ids[i]); + if (status == PSA_SUCCESS) { + BOOT_LOG_DBG("Success on key ID %d", i); + } else { + BOOT_LOG_ERR("Key invalidation failed with: %d", status); + } + } +out: + return ret; +} +#endif /* CONFIG_BOOT_KMU_KEYS_REVOCATION */ + +void nrf_crypto_keys_housekeeping(void) +{ + psa_status_t status; + + /* We will continue through all keys, even if we have error while + * processing any of it. Only doing BOOT_LOG_DBG, as we do not + * really want to inform on failures to lock. + */ + for (int i = 0; i < CONFIG_BOOT_SIGNATURE_KMU_SLOTS; ++i) { + psa_key_attributes_t attr; + + status = psa_get_key_attributes(key_ids[i], &attr); + BOOT_LOG_DBG("KMU key 0x%x(%d) attr query status == %d", + key_ids[i], i, status); + + if (status == PSA_SUCCESS) { + status = cracen_kmu_block(&attr); + BOOT_LOG_DBG("KMU key lock status == %d", status); + } + + status = psa_purge_key(key_ids[i]); + BOOT_LOG_DBG("KMU key 0x%x(%d) purge status == %d", + key_ids[i], i, status); + } +} + +#endif diff --git a/boot/bootutil/src/encrypted_psa.c b/boot/bootutil/src/encrypted_psa.c index 55f7f6f2d..1ef57184d 100644 --- a/boot/bootutil/src/encrypted_psa.c +++ b/boot/bootutil/src/encrypted_psa.c @@ -25,7 +25,7 @@ #include "bootutil_priv.h" #include "bootutil/bootutil_log.h" -BOOT_LOG_MODULE_DECLARE(mcuboot_psa_enc); +BOOT_LOG_MODULE_REGISTER(mcuboot_psa_enc); #if defined(MCUBOOT_HMAC_SHA512) #define PSA_HMAC_HKDF_SHA PSA_ALG_SHA_512 diff --git a/boot/bootutil/src/image_ed25519.c b/boot/bootutil/src/image_ed25519.c index 4d83bb3d7..177550749 100644 --- a/boot/bootutil/src/image_ed25519.c +++ b/boot/bootutil/src/image_ed25519.c @@ -34,7 +34,9 @@ extern int ED25519_verify(const uint8_t *message, size_t message_len, const uint8_t signature[EDDSA_SIGNATURE_LENGTH], const uint8_t public_key[NUM_ED25519_BYTES]); +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) #if !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) +#if !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) /* * Parse the public key used for signing. */ @@ -76,6 +78,8 @@ bootutil_import_key(uint8_t **cp, uint8_t *end) return 0; } #endif /* !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) */ +#endif +#endif /* Signature verification base function. * The function takes buffer of specified length and tries to verify @@ -90,8 +94,10 @@ bootutil_verify(uint8_t *buf, uint32_t blen, { int rc; FIH_DECLARE(fih_rc, FIH_FAILURE); - uint8_t *pubkey; + uint8_t *pubkey = NULL; +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) uint8_t *end; +#endif BOOT_LOG_DBG("bootutil_verify: ED25519 key_id %d", (int)key_id); @@ -102,6 +108,7 @@ bootutil_verify(uint8_t *buf, uint32_t blen, goto out; } +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) pubkey = (uint8_t *)bootutil_keys[key_id].key; end = pubkey + *bootutil_keys[key_id].len; @@ -125,6 +132,8 @@ bootutil_verify(uint8_t *buf, uint32_t blen, } pubkey = end - NUM_ED25519_BYTES; +#endif + #endif rc = ED25519_verify(buf, blen, sig, pubkey); diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c index f40667866..16ff9c09c 100644 --- a/boot/bootutil/src/image_validate.c +++ b/boot/bootutil/src/image_validate.c @@ -50,6 +50,11 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); #include "bootutil/mcuboot_uuid.h" #endif /* MCUBOOT_UUID_VID || MCUBOOT_UUID_CID */ +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif @@ -112,6 +117,7 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); # define EXPECTED_KEY_TLV IMAGE_TLV_PUBKEY # define KEY_BUF_SIZE (SIG_BUF_SIZE + 24) #endif /* !MCUBOOT_HW_KEY */ + #endif /* !MCUBOOT_BUILTIN_KEY */ #endif /* EXPECTED_SIG_TLV */ @@ -205,7 +211,8 @@ bootutil_img_validate(struct boot_loader_state *state, ) { #if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || defined(MCUBOOT_HW_ROLLBACK_PROT) \ - || defined(MCUBOOT_UUID_VID) || defined(MCUBOOT_UUID_CID) + || defined(MCUBOOT_UUID_VID) || defined(MCUBOOT_UUID_CID) || defined(MCUBOOT_DECOMPRESS_IMAGES) \ + || defined(MCUBOOT_BUILTIN_KEY) int image_index = (state == NULL ? 0 : BOOT_CURR_IMG(state)); #endif uint32_t off; @@ -241,6 +248,15 @@ bootutil_img_validate(struct boot_loader_state *state, fih_int security_cnt = fih_int_encode(INT_MAX); uint32_t img_security_cnt = 0; FIH_DECLARE(security_counter_valid, FIH_FAILURE); + FIH_DECLARE(security_counter_should_be_present, FIH_FAILURE); + + FIH_CALL(boot_nv_image_should_have_security_counter, security_counter_should_be_present, + image_index); + if (FIH_NOT_EQ(security_counter_should_be_present, FIH_SUCCESS) && + FIH_NOT_EQ(security_counter_should_be_present, FIH_FAILURE)) { + rc = -1; + goto out; + } #endif #ifdef MCUBOOT_UUID_VID struct image_uuid img_uuid_vid = {0x00}; @@ -252,6 +268,67 @@ bootutil_img_validate(struct boot_loader_state *state, #endif BOOT_LOG_DBG("bootutil_img_validate: flash area %p", fap); +#ifdef MCUBOOT_DECOMPRESS_IMAGES + /* If the image is compressed, the integrity of the image must also be validated */ + if (MUST_DECOMPRESS(fap, image_index, hdr)) { + bool found_decompressed_size = false; + bool found_decompressed_sha = false; + bool found_decompressed_signature = false; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + uint16_t expected_size = 0; + bool *found_flag = NULL; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + switch (type) { + case IMAGE_TLV_DECOMP_SIZE: + expected_size = sizeof(size_t); + found_flag = &found_decompressed_size; + break; + case IMAGE_TLV_DECOMP_SHA: + expected_size = IMAGE_HASH_SIZE; + found_flag = &found_decompressed_sha; + break; + case IMAGE_TLV_DECOMP_SIGNATURE: + found_flag = &found_decompressed_signature; + break; + default: + continue; + }; + + if (type == IMAGE_TLV_DECOMP_SIGNATURE && !EXPECTED_SIG_LEN(len)) { + rc = -1; + goto out; + } else if (type != IMAGE_TLV_DECOMP_SIGNATURE && len != expected_size) { + rc = -1; + goto out; + } + + *found_flag = true; + } + + rc = (!found_decompressed_size || !found_decompressed_sha || !found_decompressed_signature); + if (rc) { + goto out; + } + } +#endif #if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) rc = bootutil_img_hash(state, hdr, fap, tmp_buf, tmp_buf_sz, hash, seed, seed_len); @@ -358,6 +435,7 @@ bootutil_img_validate(struct boot_loader_state *state, break; } #endif /* defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) */ +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) #ifdef EXPECTED_KEY_TLV case EXPECTED_KEY_TLV: { @@ -389,15 +467,18 @@ bootutil_img_validate(struct boot_loader_state *state, break; } #endif /* EXPECTED_KEY_TLV */ +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ #ifdef EXPECTED_SIG_TLV case EXPECTED_SIG_TLV: { BOOT_LOG_DBG("bootutil_img_validate: EXPECTED_SIG_TLV == %d", EXPECTED_SIG_TLV); +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) /* Ignore this signature if it is out of bounds. */ if (key_id < 0 || key_id >= bootutil_key_cnt) { key_id = -1; continue; } +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) */ if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { rc = -1; goto out; @@ -445,6 +526,10 @@ bootutil_img_validate(struct boot_loader_state *state, goto out; } + if (FIH_EQ(security_counter_should_be_present, FIH_FAILURE)) { + goto skip_security_counter_read; + } + FIH_CALL(boot_nv_security_counter_get, fih_rc, image_index, &security_cnt); if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { @@ -479,6 +564,7 @@ bootutil_img_validate(struct boot_loader_state *state, /* The image's security counter has been successfully verified. */ security_counter_valid = fih_rc; +skip_security_counter_read: break; } #endif /* MCUBOOT_HW_ROLLBACK_PROT */ @@ -556,10 +642,16 @@ bootutil_img_validate(struct boot_loader_state *state, FIH_SET(fih_rc, valid_signature); #endif #ifdef MCUBOOT_HW_ROLLBACK_PROT + if (FIH_EQ(security_counter_should_be_present, FIH_FAILURE)) { + goto skip_security_counter_check; + } + if (FIH_NOT_EQ(security_counter_valid, FIH_SUCCESS)) { rc = -1; goto out; } + +skip_security_counter_check: #endif #ifdef MCUBOOT_UUID_VID @@ -575,6 +667,163 @@ bootutil_img_validate(struct boot_loader_state *state, } #endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + /* Only after all previous verifications have passed, perform a dry-run of the decompression + * and ensure the image is valid + */ + if (!rc && MUST_DECOMPRESS(fap, image_index, hdr)) { + image_hash_valid = 0; + FIH_SET(valid_signature, FIH_FAILURE); + + rc = bootutil_img_hash_decompress(state, hdr, fap, tmp_buf, tmp_buf_sz, + hash, seed, seed_len); + if (rc) { + goto out; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SHA, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + if (type == IMAGE_TLV_DECOMP_SHA) { + /* Verify the image hash. This must always be present. */ + if (len != sizeof(hash)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, sizeof(hash)); + if (rc) { + goto out; + } + + FIH_CALL(boot_fih_memequal, fih_rc, hash, buf, sizeof(hash)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + image_hash_valid = 1; + } + } + + rc = !image_hash_valid; + if (rc) { + goto out; + } + +#ifdef EXPECTED_SIG_TLV +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && defined(EXPECTED_KEY_TLV) + rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_KEY_TLV, false); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + if (type == EXPECTED_KEY_TLV) { + /* + * Determine which key we should be checking. + */ + if (len > KEY_BUF_SIZE) { + rc = -1; + goto out; + } +#ifndef MCUBOOT_HW_KEY + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(buf, len); +#else + rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(image_index, key_buf, len); +#endif /* !MCUBOOT_HW_KEY */ + /* + * The key may not be found, which is acceptable. There + * can be multiple signatures, each preceded by a key. + */ + } + } +#endif /* !CONFIG_BOOT_SIGNATURE_USING_KMU && EXPECTED_KEY_TLV */ + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SIGNATURE, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIGNATURE) { + /* Ignore this signature if it is out of bounds. */ +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && !defined(CONFIG_NCS_BOOT_SIGNATURE_USING_ITS) + if (key_id < 0 || key_id >= bootutil_key_cnt) { + key_id = -1; + continue; + } +#endif + + if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + + FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash), + buf, len, key_id); + key_id = -1; + } + } +#endif /* EXPECTED_SIG_TLV */ + } +#endif + +#ifdef EXPECTED_SIG_TLV + FIH_SET(fih_rc, valid_signature); +#endif + out: if (rc) { FIH_SET(fih_rc, FIH_FAILURE); diff --git a/boot/bootutil/src/key_revocation.c b/boot/bootutil/src/key_revocation.c new file mode 100644 index 000000000..0768a3188 --- /dev/null +++ b/boot/bootutil/src/key_revocation.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +extern int exec_revoke(void); + +static uint8_t ready_to_revoke; + +void allow_revoke(void) +{ + ready_to_revoke = 1; +} + +int revoke(void) +{ + if (ready_to_revoke) { + return exec_revoke(); + } + return BOOT_KEY_REVOKE_NOT_READY; +} diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 9769428b4..12be5cff3 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -50,6 +50,26 @@ #include "bootutil/boot_hooks.h" #include "bootutil/mcuboot_status.h" +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#ifdef __ZEPHYR__ +#include +#if defined(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) +#include +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) && defined(CONFIG_PCD_APP) +#include +#ifdef CONFIG_PCD_READ_NETCORE_APP_VERSION +#include +int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr); +#endif +#endif + #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif @@ -60,14 +80,59 @@ #include "mcuboot_config/mcuboot_config.h" +#if defined(CONFIG_BOOT_KEYS_REVOCATION) +#include "bootutil/key_revocation.h" +#endif + +#ifdef CONFIG_SOC_EARLY_RESET_HOOK +void s2ram_designate_slot(uint8_t slot); +#endif + BOOT_LOG_MODULE_DECLARE(mcuboot); static struct boot_loader_state boot_data; +#ifdef PM_S1_ADDRESS +static bool owner_nsib[BOOT_IMAGE_NUMBER] = {false}; +#endif #if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; #endif +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +#if !defined(__BOOTSIM__) +/* Used for holding static buffers in multiple functions to work around issues + * in older versions of gcc (e.g. 4.8.4) + */ +struct sector_buffer_t { + boot_sector_t primary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + boot_sector_t secondary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_sector_t scratch[BOOT_MAX_IMG_SECTORS]; +#endif +}; + +#endif +#endif + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 && defined(MCUBOOT_OVERWRITE_ONLY) && \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/* s0/s1 package version of the current MCUboot image */ +static const struct image_version mcuboot_s0_s1_image_version = { + .iv_major = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MAJOR, + .iv_minor = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MINOR, + .iv_revision = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_REVISION, + .iv_build_num = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_BUILD_NUMBER, +}; +#endif + +#if (BOOT_IMAGE_NUMBER > 1) +#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) +#else +#define IMAGES_ITER(x) +#endif + /* * This macro allows some control on the allocation of local variables. * When running natively on a target, we don't want to allocated huge @@ -129,6 +194,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all, * * Failure to read any headers is a fatal error. */ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + /* Patch needed for NCS. The primary slot of the second image + * (image 1) will not contain a valid image header until an upgrade + * of mcuboot has happened (filling S1 with the new version). + */ + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) { + continue; + } +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ if (i > 0 && !require_all) { return 0; } else { @@ -423,7 +497,7 @@ boot_verify_dependencies(struct boot_loader_state *state) if (rc == 0) { /* All dependencies've been satisfied, continue with next image. */ BOOT_CURR_IMG(state)++; - } else { + } else if (rc == BOOT_EBADIMAGE) { /* Cannot upgrade due to non-met dependencies, so disable all * image upgrades. */ @@ -432,7 +506,10 @@ boot_verify_dependencies(struct boot_loader_state *state) BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; } break; - } + } else { + /* Other error happened, images are inconsistent */ + return rc; + } } return rc; } @@ -539,7 +616,7 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) #ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER /* Validate against possible dependency slot values. */ - switch(dep->slot) { + switch(dep.slot) { case VERSION_DEP_SLOT_ACTIVE: case VERSION_DEP_SLOT_PRIMARY: case VERSION_DEP_SLOT_SECONDARY: @@ -695,9 +772,37 @@ boot_image_check(struct boot_loader_state *state, struct image_header *hdr, } #endif - FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, - NULL, 0, NULL); + for (int i = 1; i <= CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT; i++ ) { +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Image validation attempt %d/%d", i, CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + + FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL); + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Image validation attempt %d/%d success", i, CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + break; + } else { +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_WRN("Image validation attempt %d/%d failure: %d", + i, + CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT, fih_rc); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + + if (i < CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT) { +#if defined(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Waiting %d ms before next attempt", + CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + k_busy_wait(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS * 1000); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ + } + } + } FIH_RET(fih_rc); } @@ -789,10 +894,10 @@ boot_is_header_valid(const struct image_header *hdr, const struct flash_area *fa return false; } #else - if ((hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1) && - (hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) - { - return false; + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { + if (!boot_is_compressed_header_valid(hdr, fap, state)) { + return false; + } } #endif @@ -961,9 +1066,55 @@ boot_validate_slot(struct boot_loader_state *state, int slot, int rc; /* Check if version of secondary slot is sufficient */ - rc = boot_version_cmp( - &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, - &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \ + && defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION) + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SLOT_SECONDARY)); + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif + } +#else + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif +#endif if (rc < 0 && boot_check_header_erased(state, BOOT_SLOT_PRIMARY)) { BOOT_LOG_ERR("insufficient version in secondary slot"); boot_scramble_slot(fap, slot); @@ -994,6 +1145,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, * attempts to validate and boot it. */ } + #if !defined(__BOOTSIM__) BOOT_LOG_ERR("Image in the %s slot is not valid!", (slot == BOOT_SLOT_PRIMARY) ? "primary" : "secondary"); @@ -1008,18 +1160,69 @@ boot_validate_slot(struct boot_loader_state *state, int slot, * overwriting an application written to the incorrect slot. * This feature is only supported by ARM platforms. */ +#if MCUBOOT_IMAGE_NUMBER >= 3 + /* Currently the MCUboot can be configured for up to 3 image, where image number 2 is + * designated for XIP, where it is the second part of image stored in slots of image + * 0. This part of image is not bootable, as the XIP setup is done by the app in + * image 0 slot, and it does not carry the reset vector. + */ + if (fap == state->imgs[2][BOOT_SLOT_SECONDARY].area) { + goto out; + } +#endif if (fap == BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY)) { const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY); struct image_header *secondary_hdr = boot_img_hdr(state, slot); uint32_t reset_value = 0; uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value); + uint32_t min_addr, max_addr; + bool check_addresses = false; if (flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)) != 0) { fih_rc = FIH_NO_BOOTABLE_IMAGE; goto out; } - if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) { +#ifdef PM_CPUNET_APP_ADDRESS + /* The primary slot for the network core is emulated in RAM. + * Its flash_area hasn't got relevant boundaries. + * Therfore need to override its boundaries for the check. + */ + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + min_addr = PM_CPUNET_APP_ADDRESS; + max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; + check_addresses = true; + } else +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = PM_S0_ADDRESS; + max_addr = (PM_S0_ADDRESS + PM_S0_SIZE); +#else + min_addr = PM_S1_ADDRESS; + max_addr = (PM_S1_ADDRESS + PM_S1_SIZE); +#endif + check_addresses = true; + } else +#endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE)); +#else + min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE)); +#endif +#else + min_addr = pri_fa->fa_off; + max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + check_addresses = true; + } + + if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) { BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot"); BOOT_LOG_ERR("Erasing image from secondary slot"); @@ -1041,6 +1244,38 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } #ifdef MCUBOOT_HW_ROLLBACK_PROT +/** + * Checks if the specified image should have a security counter present on it or not + * + * @param image_index Index of the image to check. + * + * @return true if security counter should be present; false if otherwise + */ +fih_ret boot_nv_image_should_have_security_counter(uint32_t image_index) +{ +#if defined(PM_S1_ADDRESS) + if (owner_nsib[image_index]) { + /* + * Downgrade prevention on S0/S1 image is managed by NSIB, which is a software (not + * hardware) check + */ + return FIH_FAILURE; + } +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (image_index == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + /* + * Downgrade prevention on network core image is managed by NSIB which is a software (not + * hardware) check + */ + return FIH_FAILURE; + } +#endif + + return FIH_SUCCESS; +} + /** * Updates the stored security counter value with the image's security counter * value which resides in the given slot, only if it's greater than the stored @@ -1062,6 +1297,26 @@ boot_update_security_counter(struct boot_loader_state *state, int slot, int hdr_ uint32_t img_security_cnt; int rc; +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* + * Downgrade prevention on S0/S1 image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + /* + * Downgrade prevention on network core image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + fap = BOOT_IMG_AREA(state, slot); assert(fap != NULL); @@ -1081,6 +1336,109 @@ boot_update_security_counter(struct boot_loader_state *state, int slot, int hdr_ #endif /* MCUBOOT_HW_ROLLBACK_PROT */ #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ +(defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)) + +#define SEC_SLOT_VIRGIN 0 +#define SEC_SLOT_TOUCHED 1 +#define SEC_SLOT_ASSIGNED 2 + +static uint8_t sec_slot_assignment[MCUBOOT_IMAGE_NUMBER] = {0}; + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +static inline void sec_slot_untouch(struct boot_loader_state *state) +{ + sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_VIRGIN; + sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_VIRGIN; +} +#else +static inline void sec_slot_untouch(struct boot_loader_state *state) +{ +} +#endif + +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + if (sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + if (sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } +#endif + + if (sec_slot_assignment[BOOT_CURR_IMG(state)] == SEC_SLOT_VIRGIN) { + sec_slot_assignment[BOOT_CURR_IMG(state)] = SEC_SLOT_TOUCHED; + } +} + +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } +#endif + + sec_slot_assignment[BOOT_CURR_IMG(state)] = SEC_SLOT_ASSIGNED; +} + +/** + * Cleanup up all secondary slot which couldn't be assigned to any primary slot. + * + * This function erases content of each secondary slot which contains valid + * header but couldn't be assigned to any of supported primary images. + * + * This function is supposed to be called after boot_validated_swap_type() + * iterates over all the images in context_boot_go(). + */ +static void sec_slot_cleanup_if_unusable(void) +{ + uint8_t idx; + + for (idx = 0; idx < MCUBOOT_IMAGE_NUMBER; idx++) { + if (SEC_SLOT_TOUCHED == sec_slot_assignment[idx]) { + const struct flash_area *secondary_fa; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(idx, BOOT_SLOT_SECONDARY), + &secondary_fa); + if (!rc) { + rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + if (!rc) { + BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", idx); + } + } + + if (rc) { + BOOT_LOG_ERR("Failed to clean-up secondary slot of image %d: %d", idx, rc); + } + } + } +} +#else +static inline void sec_slot_untouch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +} +static inline void sec_slot_cleanup_if_unusable(void) +{ +} +#endif /* defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ + defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) */ + /** * Determines which swap operation to perform, if any. If it is determined * that a swap operation is required, the image in the secondary slot is checked @@ -1095,6 +1453,87 @@ boot_validated_swap_type(struct boot_loader_state *state, { int swap_type; FIH_DECLARE(fih_rc, FIH_FAILURE); + bool upgrade_valid = false; +#if defined(PM_S1_ADDRESS) + owner_nsib[BOOT_CURR_IMG(state)] = false; +#endif + +#if defined(PM_S1_ADDRESS) || defined(PM_CPUNET_B0N_ADDRESS) + const struct flash_area *secondary_fa = + BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); + struct image_header *hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY); + uint32_t reset_addr = 0; + int rc = 0; + /* Patch needed for NCS. Since image 0 (the app) and image 1 (the other + * B1 slot S0 or S1) share the same secondary slot, we need to check + * whether the update candidate in the secondary slot is intended for + * image 0 or image 1 primary by looking at the address of the reset + * vector. Note that there are good reasons for not using img_num from + * the swap info. + */ + + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = flash_area_read(secondary_fa, hdr->ih_hdr_size + + sizeof(uint32_t), &reset_addr, + sizeof(reset_addr)); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + sec_slot_touch(state); + +#ifdef PM_S1_ADDRESS +#ifdef PM_CPUNET_B0N_ADDRESS + if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS)) +#endif + { + const struct flash_area *primary_fa; + rc = flash_area_open(flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), BOOT_SLOT_PRIMARY), + &primary_fa); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Check start and end of primary slot for current image */ +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#else + if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + /* This is not the s0/s1 upgrade image but the application image, pretend + * there is no image so the NSIB update can be loaded + */ + return BOOT_SWAP_TYPE_NONE; + } + + owner_nsib[BOOT_CURR_IMG(state)] = true; +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + } else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#else + } else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#endif + /* NSIB upgrade but for the wrong slot, must be erased */ + BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image"); + flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + sec_slot_untouch(state); + BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", BOOT_CURR_IMG(state)); + return BOOT_SWAP_TYPE_FAIL; + } else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { + /* The image in the secondary slot is not intended for any */ + return BOOT_SWAP_TYPE_NONE; + } + + if ((primary_fa->fa_off == PM_S0_ADDRESS) || (primary_fa->fa_off == PM_S1_ADDRESS)) { + owner_nsib[BOOT_CURR_IMG(state)] = true; + } + } +#endif /* PM_S1_ADDRESS */ + sec_slot_mark_assigned(state); + } + +#endif /* PM_S1_ADDRESS || PM_CPUNET_B0N_ADDRESS */ swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); if (BOOT_IS_UPGRADE(swap_type)) { @@ -1108,7 +1547,42 @@ boot_validated_swap_type(struct boot_loader_state *state, } else { swap_type = BOOT_SWAP_TYPE_FAIL; } + } else { + upgrade_valid = true; } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) \ + && !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) && defined(CONFIG_PCD_APP) + /* If the update is valid, and it targets the network core: perform the + * update and indicate to the caller of this function that no update is + * available + */ + if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS && + reset_addr < PM_CPUNET_APP_END_ADDRESS) { + struct image_header *hdr = (struct image_header *)secondary_fa->fa_off; + uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size; + uint32_t *net_core_fw_addr = (uint32_t *)(vtable_addr); + uint32_t fw_size = hdr->ih_img_size; + BOOT_LOG_INF("Starting network core update"); + rc = pcd_network_core_update(net_core_fw_addr, fw_size); + + if (rc != 0) { + swap_type = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_LOG_INF("Done updating network core"); +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* swap_erase_trailer_sectors is undefined if upgrade only + * method is used. There is no need to erase sectors, because + * the image cannot be reverted. + */ + rc = swap_erase_trailer_sectors(state, + secondary_fa); +#endif + swap_type = BOOT_SWAP_TYPE_NONE; + } + } +#endif /* CONFIG_SOC_NRF5340_CPUAPP && PM_CPUNET_B0N_ADDRESS && + !CONFIG_NRF53_MULTI_IMAGE_UPDATE && CONFIG_PCD_APP */ } return swap_type; @@ -1328,6 +1802,9 @@ boot_copy_region(struct boot_loader_state *state, #else (void)state; #endif +#if defined(MCUBOOT_DECOMPRESS_IMAGES) && !defined(MCUBOOT_ENC_IMAGES) + struct image_header *hdr; +#endif TARGET_STATIC uint8_t buf[BUF_SZ] __attribute__((aligned(4))); @@ -1353,6 +1830,16 @@ boot_copy_region(struct boot_loader_state *state, } #endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY); + + if (MUST_DECOMPRESS(fap_src, BOOT_CURR_IMG(state), hdr)) { + /* Use alternative function for compressed images */ + return boot_copy_region_decompress(state, fap_src, fap_dst, off_src, off_dst, sz, buf, + BUF_SZ); + } +#endif + bytes_copied = 0; while (bytes_copied < sz) { if (sz - bytes_copied > sizeof buf) { @@ -1717,7 +2204,22 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) flash_area_close(fap); } - swap_run(state, bs, copy_size); +#if defined(PM_S1_ADDRESS) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (owner_nsib[BOOT_CURR_IMG(state)]) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* For NSIB, move the image instead of swapping it */ + nsib_swap_run(state, bs); + +#if defined(CONFIG_REBOOT) + /* Should also reboot at this point so the new S0/S1 update is applied */ + sys_reboot(SYS_REBOOT_COLD); +#endif + } + } else +#endif + { + swap_run(state, bs, copy_size); + } #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT extern int boot_status_fails; @@ -2163,6 +2665,26 @@ check_downgrade_prevention(struct boot_loader_state *state) uint32_t security_counter[2]; int rc; +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* + * Downgrade prevention on S0/S1 image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + /* + * Downgrade prevention on network core image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + if (MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER) { /* If there was security no counter in slot 0, allow swap */ rc = bootutil_get_img_security_cnt(state, BOOT_SLOT_PRIMARY, @@ -2269,6 +2791,9 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } } + /* cleanup secondary slots which were recognized unusable*/ + sec_slot_cleanup_if_unusable(); + #if (BOOT_IMAGE_NUMBER > 1) if (has_upgrade) { /* Iterate over all the images and verify whether the image dependencies @@ -2368,6 +2893,11 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } } +#if defined(CONFIG_BOOT_KEYS_REVOCATION) + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) { + allow_revoke(); + } +#endif /* Iterate over all the images. At this point all required update operations * have finished. By the end of the loop each image in the primary slot will * have been re-validated. @@ -2406,16 +2936,30 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) */ } -#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT - FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SLOT_PRIMARY, NULL, 0); - /* Check for all possible values is redundant in normal operation it - * is meant to prevent FI attack. +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. Image 1 primary is the currently + * executing MCUBoot image, and is therefore already validated by NSIB and + * does not need to also be validated by MCUBoot. */ - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || - FIH_EQ(fih_rc, FIH_FAILURE) || - FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { - FIH_SET(fih_rc, FIH_FAILURE); - goto out; + bool image_validated_by_nsib = BOOT_CURR_IMG(state) == + CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER; +#endif + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SLOT_PRIMARY, NULL, 0); + /* Check for all possible values is redundant in normal operation it + * is meant to prevent FI attack. + */ + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || + FIH_EQ(fih_rc, FIH_FAILURE) || + FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } } #else /* Even if we're not re-validating the primary slot, we could be booting @@ -2432,11 +2976,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { rc = boot_update_hw_rollback_protection(state); if (rc != 0) { FIH_SET(fih_rc, FIH_FAILURE); goto out; } + } rc = boot_add_shared_data(state, BOOT_SLOT_PRIMARY); if (rc != 0) { @@ -2457,6 +3006,13 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) fill_rsp(state, rsp); fih_rc = FIH_SUCCESS; +#if defined(CONFIG_BOOT_KEYS_REVOCATION) + rc = revoke(); + if (rc != BOOT_KEY_REVOKE_OK && + rc != BOOT_KEY_REVOKE_NOT_READY) { + FIH_SET(fih_rc, FIH_FAILURE); + } +#endif /* CONFIG_BOOT_KEYS_REVOCATION */ out: /* * Since the boot_status struct stores plaintext encryption keys, reset @@ -3048,6 +3604,11 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } } +#ifdef CONFIG_SOC_EARLY_RESET_HOOK + /* Designate the slot to be used by the PM_S2RAM resume module */ + s2ram_designate_slot((uint8_t)state->slot_usage[0].active_slot); +#endif + /* All image loaded successfully. */ #ifdef MCUBOOT_HAVE_LOGGING print_loaded_images(state); diff --git a/boot/bootutil/src/swap_move.c b/boot/bootutil/src/swap_move.c index 88a117f59..a183ce8b0 100644 --- a/boot/bootutil/src/swap_move.c +++ b/boot/bootutil/src/swap_move.c @@ -236,6 +236,28 @@ boot_slots_compatible(struct boot_loader_state *state) size_t sector_sz_sec = 0; size_t i; +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ +#if CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + return 1; + } +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + return 1; + } +#endif +#endif + num_sectors_pri = boot_img_num_sectors(state, BOOT_SLOT_PRIMARY); num_sectors_sec = boot_img_num_sectors(state, BOOT_SLOT_SECONDARY); diff --git a/boot/bootutil/src/swap_nsib.c b/boot/bootutil/src/swap_nsib.c new file mode 100644 index 000000000..f8d6e8a88 --- /dev/null +++ b/boot/bootutil/src/swap_nsib.c @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Licensed 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 +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs) +{ + uint32_t sector_sz; + uint8_t image_index; + const struct flash_area *fap_pri; + const struct flash_area *fap_sec; + int rc; + + BOOT_LOG_INF("Starting swap using nsib algorithm."); + + sector_sz = boot_img_sector_size(state, BOOT_SLOT_SECONDARY, 0); + +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + rc = flash_area_open(PM_S0_ID, &fap_pri); +#else + rc = flash_area_open(PM_S1_ID, &fap_pri); +#endif + assert (rc == 0); + image_index = BOOT_CURR_IMG(state); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec); + assert (rc == 0); + + rc = boot_erase_region(fap_pri, 0, fap_pri->fa_size, false); + assert(rc == 0); + + rc = boot_copy_region(state, fap_sec, fap_pri, 0, 0, fap_pri->fa_size); + assert(rc == 0); + + rc = swap_scramble_trailer_sectors(state, fap_sec); + assert(rc == 0); + + rc = boot_scramble_region(fap_sec, 0, MIN((fap_pri->fa_size + sector_sz), fap_sec->fa_size), false); + assert(rc == 0); + + flash_area_close(fap_pri); + flash_area_close(fap_sec); +} diff --git a/boot/bootutil/src/swap_offset.c b/boot/bootutil/src/swap_offset.c index f0b2d0634..27862ea00 100644 --- a/boot/bootutil/src/swap_offset.c +++ b/boot/bootutil/src/swap_offset.c @@ -310,6 +310,28 @@ int boot_slots_compatible(struct boot_loader_state *state) size_t sector_sz_sec = 0; size_t i; +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ +#if CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + return 1; + } +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + return 1; + } +#endif +#endif + num_sectors_pri = boot_img_num_sectors(state, BOOT_SLOT_PRIMARY); num_sectors_sec = boot_img_num_sectors(state, BOOT_SLOT_SECONDARY); diff --git a/boot/bootutil/src/swap_priv.h b/boot/bootutil/src/swap_priv.h index b564ea99e..10473a9cc 100644 --- a/boot/bootutil/src/swap_priv.h +++ b/boot/bootutil/src/swap_priv.h @@ -130,4 +130,12 @@ bool swap_write_block_size_check(struct boot_loader_state *state); */ int app_max_size(struct boot_loader_state *state); +#if defined(PM_S1_ADDRESS) && !defined(MCUBOOT_OVERWRITE_ONLY) && \ +CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +/** + * Performs an NSIB update + */ +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs); +#endif + #endif /* H_SWAP_PRIV_ */ diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c index 0a994d862..35fc8bf0c 100644 --- a/boot/bootutil/src/swap_scratch.c +++ b/boot/bootutil/src/swap_scratch.c @@ -281,6 +281,28 @@ boot_slots_compatible(struct boot_loader_state *state) size_t i, j; int8_t smaller; +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ +#if CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + return 1; + } +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + return 1; + } +#endif +#endif + num_sectors_primary = boot_img_num_sectors(state, BOOT_SLOT_PRIMARY); num_sectors_secondary = boot_img_num_sectors(state, BOOT_SLOT_SECONDARY); if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) || diff --git a/boot/bootutil/zephyr/CMakeLists.txt b/boot/bootutil/zephyr/CMakeLists.txt index f6d37441c..111cf4f1d 100644 --- a/boot/bootutil/zephyr/CMakeLists.txt +++ b/boot/bootutil/zephyr/CMakeLists.txt @@ -16,6 +16,11 @@ zephyr_library_named(mcuboot_util) zephyr_library_sources( ../src/bootutil_public.c ) +if(CONFIG_NRF_MCUBOOT_BOOT_REQUEST) + zephyr_library_sources_ifdef(CONFIG_NRF_MCUBOOT_BOOT_REQUEST_IMPL_RETENTION + src/boot_request_retention.c + ) +endif() # Sensitivity to the TEST_BOOT_IMAGE_ACCESS_HOOKS define is implemented for # allowing the test-build with the hooks feature enabled. @@ -40,7 +45,7 @@ if(CONFIG_BOOT_USE_PSA_CRYPTO) ) endif() -if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO) +if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO AND NOT CONFIG_NRF_SECURITY) zephyr_link_libraries(mbedTLS) endif() endif() diff --git a/boot/bootutil/zephyr/src/boot_request_retention.c b/boot/bootutil/zephyr/src/boot_request_retention.c new file mode 100644 index 000000000..e7fc393a0 --- /dev/null +++ b/boot/bootutil/zephyr/src/boot_request_retention.c @@ -0,0 +1,346 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Nordic Semiconductor ASA + */ +#include + +#include "bootutil/bootutil_log.h" +#include + +/** Special value of image number, indicating a request to the bootloader. */ +#define BOOT_REQUEST_IMG_BOOTLOADER 0xFF + +/** Additional memory used by the retention subsystem (2B - prefix, 4B - CRC).*/ +#define BOOT_REQUEST_ENTRY_METADATA_SIZE (2 + 4) + +BOOT_LOG_MODULE_REGISTER(bootloader_request); + +static const struct device *bootloader_request_dev = + DEVICE_DT_GET(DT_CHOSEN(nrf_bootloader_request)); + +enum boot_request_type { + /** Invalid request. */ + BOOT_REQUEST_INVALID = 0, + + /** Request a change in the bootloader boot mode. + * + * @details Use @p boot_request_mode as argument. + * @p BOOT_REQUEST_IMG_BOOTLOADER as image number. + * + * @note Used to trigger recovery through i.e. retention sybsystem. + */ + BOOT_REQUEST_BOOT_MODE = 1, + + /** Select the preferred image to be selected during boot or update. + * + * @details Use @p boot_request_slot_t as argument. + * + * @note Used in the Direct XIP mode. + */ + BOOT_REQUEST_IMG_PREFERENCE = 2, + + /** Request a confirmation of an image. + * + * @details Use @p boot_request_slot_t as argument. + * + * @note Used if the code cannot modify the image trailer directly. + */ + BOOT_REQUEST_IMG_CONFIRM = 3, +}; + +/* Entries inside the boot request shared memory. */ +enum boot_request_entry { + BOOT_REQUEST_ENTRY_BOOT_MODE = 0, + BOOT_REQUEST_ENTRY_IMAGE_0_PREFERENCE = 1, + BOOT_REQUEST_ENTRY_IMAGE_0_CONFIRM = 2, + BOOT_REQUEST_ENTRY_IMAGE_1_PREFERENCE = 3, + BOOT_REQUEST_ENTRY_IMAGE_1_CONFIRM = 4, + BOOT_REQUEST_ENTRY_MAX = 5, +}; + +/* Assert that all requests will fit within the retention area. */ +BUILD_ASSERT((BOOT_REQUEST_ENTRY_METADATA_SIZE + BOOT_REQUEST_ENTRY_MAX * sizeof(uint8_t)) < + DT_REG_SIZE_BY_IDX(DT_CHOSEN(nrf_bootloader_request), 0), + "nrf,bootloader-request area is too small for bootloader request struct"); + +enum boot_request_slot { + /** Unsupported value. */ + BOOT_REQUEST_SLOT_INVALID = 0, + /** Primary slot. */ + BOOT_REQUEST_SLOT_PRIMARY = 1, + /** Secondary slot. */ + BOOT_REQUEST_SLOT_SECONDARY = 2, +}; + +/** Alias type for the image and number. */ +typedef uint8_t boot_request_slot_t; + +enum boot_request_mode { + /** Execute a regular boot logic. */ + BOOT_REQUEST_MODE_REGULAR = 0, + /** Execute the recovery boot logic. */ + BOOT_REQUEST_MODE_RECOVERY = 1, + /** Execute the firmware loader logic. */ + BOOT_REQUEST_MODE_FIRMWARE_LOADER = 2, + /** Unsupported value. */ + BOOT_REQUEST_MODE_INVALID = 0xFF, +}; + +/** Alias type for the image number. */ +typedef uint8_t boot_request_img_t; + +/** + * @brief Find an entry for a given request. + * + * @param[in] type Type of request. + * @param[in] image Image number. Use @p BOOT_REQUEST_IMG_BOOTLOADER for generic requests. + * @param[out] entry Entry to use. + * + * @return 0 on success; nonzero on failure. + */ +static int boot_request_entry_find(enum boot_request_type type, boot_request_img_t image, + size_t *entry) +{ + if (entry == NULL) { + return -EINVAL; + } + + switch (type) { + case BOOT_REQUEST_BOOT_MODE: + *entry = BOOT_REQUEST_ENTRY_BOOT_MODE; + break; + case BOOT_REQUEST_IMG_PREFERENCE: + switch (image) { + case 0: + *entry = BOOT_REQUEST_ENTRY_IMAGE_0_PREFERENCE; + break; + case 1: + *entry = BOOT_REQUEST_ENTRY_IMAGE_1_PREFERENCE; + break; + default: + return -EINVAL; + } + break; + case BOOT_REQUEST_IMG_CONFIRM: + switch (image) { + case 0: + *entry = BOOT_REQUEST_ENTRY_IMAGE_0_CONFIRM; + break; + case 1: + *entry = BOOT_REQUEST_ENTRY_IMAGE_1_CONFIRM; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +int boot_request_init(void) +{ + if (!device_is_ready(bootloader_request_dev)) { + return -EIO; + } + + return 0; +} + +int boot_request_clear(void) +{ + return retention_clear(bootloader_request_dev); +} + +int boot_request_confirm_slot(uint8_t image, enum boot_slot slot) +{ + uint8_t value = BOOT_REQUEST_SLOT_INVALID; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_IMG_CONFIRM, image, &req_entry); + if (ret != 0) { + return ret; + } + + switch (slot) { + case BOOT_SLOT_PRIMARY: + value = BOOT_REQUEST_SLOT_PRIMARY; + break; + case BOOT_SLOT_SECONDARY: + value = BOOT_REQUEST_SLOT_SECONDARY; + break; + default: + return -EINVAL; + } + + return retention_write(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); +} + +bool boot_request_check_confirmed_slot(uint8_t image, enum boot_slot slot) +{ + uint8_t value = BOOT_REQUEST_SLOT_INVALID; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_IMG_CONFIRM, image, &req_entry); + if (ret != 0) { + return false; + } + + ret = retention_read(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); + if (ret != 0) { + return false; + } + + switch (value) { + case BOOT_REQUEST_SLOT_PRIMARY: + return (slot == BOOT_SLOT_PRIMARY); + case BOOT_REQUEST_SLOT_SECONDARY: + return (slot == BOOT_SLOT_SECONDARY); + default: + break; + } + + return false; +} + +int boot_request_set_preferred_slot(uint8_t image, enum boot_slot slot) +{ + uint8_t value = BOOT_REQUEST_SLOT_INVALID; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_IMG_PREFERENCE, image, &req_entry); + if (ret != 0) { + return ret; + } + + switch (slot) { + case BOOT_SLOT_PRIMARY: + value = BOOT_REQUEST_SLOT_PRIMARY; + break; + case BOOT_SLOT_SECONDARY: + value = BOOT_REQUEST_SLOT_SECONDARY; + break; + default: + return -EINVAL; + } + + return retention_write(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); +} + +#ifdef CONFIG_FIND_NEXT_SLOT_HOOKS +enum boot_slot boot_request_get_preferred_slot(uint8_t image) +{ + uint8_t value = BOOT_REQUEST_SLOT_INVALID; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_IMG_PREFERENCE, image, &req_entry); + if (ret != 0) { + return BOOT_SLOT_NONE; + } + + ret = retention_read(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); + if (ret != 0) { + return BOOT_SLOT_NONE; + } + + switch (value) { + case BOOT_REQUEST_SLOT_PRIMARY: + return BOOT_SLOT_PRIMARY; + case BOOT_REQUEST_SLOT_SECONDARY: + return BOOT_SLOT_SECONDARY; + default: + break; + } + + return BOOT_SLOT_NONE; +} +#endif /* CONFIG_FIND_NEXT_SLOT_HOOKS */ + +int boot_request_enter_recovery(void) +{ + uint8_t value = BOOT_REQUEST_MODE_RECOVERY; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER, + &req_entry); + if (ret != 0) { + return ret; + } + + return retention_write(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); +} + +#ifdef CONFIG_NRF_BOOT_SERIAL_BOOT_REQ +bool boot_request_detect_recovery(void) +{ + uint8_t value = BOOT_REQUEST_MODE_INVALID; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER, + &req_entry); + if (ret != 0) { + return false; + } + + ret = retention_read(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); + if ((ret == 0) && (value == BOOT_REQUEST_MODE_RECOVERY)) { + return true; + } + + return false; +} +#endif /* CONFIG_NRF_BOOT_SERIAL_BOOT_REQ */ + +int boot_request_enter_firmware_loader(void) +{ + uint8_t value = BOOT_REQUEST_MODE_FIRMWARE_LOADER; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER, + &req_entry); + if (ret != 0) { + return ret; + } + + return retention_write(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); +} + +#ifdef CONFIG_NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ +bool boot_request_detect_firmware_loader(void) +{ + uint8_t value = BOOT_REQUEST_MODE_INVALID; + size_t req_entry; + int ret; + + ret = boot_request_entry_find(BOOT_REQUEST_BOOT_MODE, BOOT_REQUEST_IMG_BOOTLOADER, + &req_entry); + if (ret != 0) { + return false; + } + + ret = retention_read(bootloader_request_dev, req_entry * sizeof(value), (void *)&value, + sizeof(value)); + if ((ret == 0) && (value == BOOT_REQUEST_MODE_FIRMWARE_LOADER)) { + return true; + } + + return false; +} +#endif /* CONFIG_NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ */ diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index d5964efe9..b34824def 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -35,24 +35,24 @@ if(NOT CONFIG_MBEDTLS_BUILTIN AND NOT CONFIG_BOOT_KEY_IMPORT_BYPASS_ASN) set(MBEDTLS_ASN1_DIR "${MCUBOOT_DIR}/ext/mbedtls-asn1") assert_exists(MBEDTLS_ASN1_DIR) endif() -set(NRF_DIR "${MCUBOOT_DIR}/ext/nrf") +set(MCUBOOT_NRF_EXT_DIR "${MCUBOOT_DIR}/ext/nrf") if(CONFIG_BOOT_USE_NRF_CC310_BL) -set(NRFXLIB_DIR ${ZEPHYR_BASE}/../nrfxlib) -if(NOT EXISTS ${NRFXLIB_DIR}) - message(FATAL_ERROR " + if(NOT EXISTS ${ZEPHYR_NRFXLIB_MODULE_DIR}) + message(FATAL_ERROR " ------------------------------------------------------------------------ - No such file or directory: ${NRFXLIB_DIR} + No such file or directory: ${ZEPHYR_NRFXLIB_MODULE_DIR} The current configuration enables nRF CC310 crypto accelerator hardware with the `CONFIG_BOOT_USE_NRF_CC310_BL` option. Please follow `ext/nrf/README.md` guide to fix your setup or use tinycrypt instead of the HW accelerator. To use the tinycrypt set `CONFIG_BOOT_ECDSA_TINYCRYPT` to y. ------------------------------------------------------------------------") + endif() endif() -# Don't include this if we are using west - add_subdirectory(${NRFXLIB_DIR} ${PROJECT_BINARY_DIR}/nrfxlib) -endif() + +# Include the UUID generation code +add_subdirectory(uuid) zephyr_library_include_directories( include @@ -67,12 +67,17 @@ endif() # Zephyr port-specific sources. zephyr_library_sources( main.c - io.c flash_map_extended.c os.c keys.c ) +if(CONFIG_NCS_BM) + zephyr_library_sources(io_bm.c) +else() + zephyr_library_sources(io.c) +endif() + if(DEFINED CONFIG_ENABLE_MGMT_PERUSER) zephyr_library_sources( boot_serial_extensions.c @@ -96,6 +101,12 @@ if(DEFINED CONFIG_BOOT_SHARE_BACKEND_RETENTION) ) endif() +if(DEFINED CONFIG_BOOT_KEYS_REVOCATION) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/key_revocation.c +) +endif() + # Generic bootutil sources and includes. zephyr_library_include_directories(${BOOT_DIR}/bootutil/include) zephyr_library_sources( @@ -151,9 +162,15 @@ elseif(CONFIG_SINGLE_APPLICATION_SLOT) ) zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) elseif(CONFIG_BOOT_FIRMWARE_LOADER) - zephyr_library_sources( - ${BOOT_DIR}/zephyr/firmware_loader.c - ) + if(CONFIG_NCS_BM) + zephyr_library_sources( + ${BOOT_DIR}/zephyr/firmware_loader_bm.c + ) + else() + zephyr_library_sources( + ${BOOT_DIR}/zephyr/firmware_loader.c + ) + endif() zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) else() zephyr_library_sources( @@ -181,6 +198,12 @@ else() ) endif() endif() + + if(NOT CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER EQUAL "-1" AND NOT CONFIG_BOOT_UPGRADE_ONLY) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_nsib.c + ) + endif() endif() if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) @@ -212,9 +235,11 @@ if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) ${TINYCRYPT_DIR}/source/utils.c ) elseif(CONFIG_BOOT_USE_NRF_CC310_BL) - zephyr_library_sources(${NRF_DIR}/cc310_glue.c) - zephyr_library_include_directories(${NRF_DIR}) + zephyr_library_sources(${MCUBOOT_NRF_EXT_DIR}/cc310_glue.c) + zephyr_library_include_directories(${MCUBOOT_NRF_EXT_DIR}) zephyr_link_libraries(nrfxlib_crypto) + elseif(CONFIG_BOOT_USE_NRF_EXTERNAL_CRYPTO) + zephyr_include_directories(${BL_CRYPTO_DIR}/../include) endif() if(CONFIG_MBEDTLS_CFG_FILE) @@ -315,6 +340,12 @@ if(CONFIG_BOOT_ENCRYPT_EC256 AND NOT CONFIG_BOOT_ECDSA_PSA) ) endif() +if(CONFIG_BOOT_DECOMPRESSION) + zephyr_library_sources( + decompression.c + ) +endif() + if(CONFIG_MCUBOOT_SERIAL) zephyr_sources(${BOOT_DIR}/zephyr/serial_adapter.c) zephyr_sources(${BOOT_DIR}/boot_serial/src/boot_serial.c) @@ -336,7 +367,7 @@ if(CONFIG_MCUBOOT_SERIAL) endif() endif() -if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") +if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") # CONF_FILE points to the KConfig configuration files of the bootloader. foreach (filepath ${CONF_FILE}) file(READ ${filepath} temp_text) @@ -360,6 +391,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") endif() message("MCUBoot bootloader key file: ${KEY_FILE}") + set_property( + GLOBAL + PROPERTY + KEY_FILE + ${KEY_FILE} + ) + set(mcuboot_default_signature_files ${MCUBOOT_DIR}/root-ec-p256-pkcs8.pem ${MCUBOOT_DIR}/root-ec-p384.pem @@ -458,6 +496,11 @@ if(CONFIG_MCUBOOT_CLEANUP_ARM_CORE) ) endif() +if(CONFIG_SOC_EARLY_RESET_HOOK) + zephyr_library_sources(${BOOT_DIR}/zephyr/nrf54h20_custom_s2ram.S) + zephyr_library_sources(${BOOT_DIR}/zephyr/nrf54h20_custom_s2ram.c) +endif() + if(CONFIG_MCUBOOT_BOOT_BANNER) # Replace Zephyr's boot banner with the MCUboot one zephyr_sources(kernel/banner.c) @@ -729,3 +772,20 @@ if(SYSBUILD) set(mcuboot_image_footer_size ${required_size} CACHE INTERNAL "Estimated MCUboot image trailer size" FORCE) set(mcuboot_image_upgrade_footer_size ${required_upgrade_size} CACHE INTERNAL "Estimated MCUboot update image trailer size" FORCE) endif() + +if(CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL) +zephyr_library_sources( + ${BOOT_DIR}/zephyr/nrf_cleanup.c +) +endif() + +if(SYSBUILD AND CONFIG_PCD_APP) + # Sysbuild requires details of the RAM flash device are stored to the cache of MCUboot so + # that they can be read when running partition manager + dt_nodelabel(ram_flash_dev NODELABEL flash_sim0) + dt_reg_addr(ram_flash_addr PATH ${ram_flash_dev}) + dt_reg_size(ram_flash_size PATH ${ram_flash_dev}) + + set(RAM_FLASH_ADDR "${ram_flash_addr}" CACHE STRING "" FORCE) + set(RAM_FLASH_SIZE "${ram_flash_size}" CACHE STRING "" FORCE) +endif() diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 467b848dc..f738ac66a 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -9,6 +9,8 @@ mainmenu "MCUboot configuration" comment "MCUboot-specific configuration options" +source "$(ZEPHYR_NRF_MODULE_DIR)/modules/mcuboot/boot/zephyr/Kconfig" + # Hidden option to mark a project as MCUboot config MCUBOOT default y @@ -16,6 +18,7 @@ config MCUBOOT select MPU_ALLOW_FLASH_WRITE if ARM_MPU select USE_DT_CODE_PARTITION if HAS_FLASH_LOAD_OFFSET select MCUBOOT_BOOTUTIL_LIB + select REBOOT if SECURE_BOOT config BOOT_USE_MBEDTLS bool @@ -85,10 +88,9 @@ config BOOT_PSA_IMG_HASH_ALG_SHA512_DEPENDENCIES config BOOT_ED25519_PSA_DEPENDENCIES bool select PSA_WANT_ALG_PURE_EDDSA - # Seems that upstream mbedTLS does not have TE - #select PSA_WANT_ECC_TWISTED_EDWARDS_255 + select PSA_WANT_ECC_TWISTED_EDWARDS_255 select PSA_WANT_ECC_MONTGOMERY_255 - select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT if !PSA_CORE_LITE help Dependencies for ed25519 signature @@ -116,7 +118,7 @@ endif # BOOT_ENCRYPT_IMAGE config BOOT_ECDSA_PSA_DEPENDENCIES bool select PSA_WANT_ALG_ECDSA - select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT if !PSA_CORE_LITE select PSA_WANT_ECC_SECP_R1_256 help Dependencies for ECDSA signature @@ -183,6 +185,7 @@ config BOOT_IMG_HASH_ALG_SHA512_ALLOW config BOOT_IMG_HASH_DIRECTLY_ON_STORAGE bool "Hash calculation functions access storage through address space" + default y if NRF_SECURITY && SOC_NRF54H20 depends on !BOOT_ENCRYPT_IMAGE help When possible to map storage device, at least for read operations, @@ -240,12 +243,13 @@ config BOOT_SIGNATURE_TYPE_PURE_ALLOW choice BOOT_SIGNATURE_TYPE prompt "Signature type" - default BOOT_SIGNATURE_TYPE_ED25519 if SOC_NRF54L15_CPUAPP + default BOOT_SIGNATURE_TYPE_ED25519 if SOC_NRF54L15_CPUAPP || SOC_NRF54H20_CPUAPP default BOOT_SIGNATURE_TYPE_RSA config BOOT_SIGNATURE_TYPE_NONE bool "No signature; use only hash check" - select BOOT_USE_TINYCRYPT + select BOOT_USE_TINYCRYPT if !SOC_SERIES_NRF54LX + select BOOT_USE_PSA_CRYPTO if SOC_SERIES_NRF54LX select BOOT_IMG_HASH_ALG_SHA256_ALLOW config BOOT_SIGNATURE_TYPE_RSA @@ -258,6 +262,8 @@ config BOOT_SIGNATURE_TYPE_RSA select MBEDTLS_PKCS1_V15 if MBEDTLS_BUILTIN select MBEDTLS_PKCS1_V21 if MBEDTLS_BUILTIN select MBEDTLS_KEY_EXCHANGE_RSA_ENABLED if MBEDTLS_BUILTIN + select MBEDTLS_PLATFORM_NO_STD_FUNCTIONS if MBEDTLS_BUILTIN + select MBEDTLS_PLATFORM_SNPRINTF_ALT if MBEDTLS_BUILTIN select BOOT_ENCRYPTION_SUPPORT select BOOT_IMG_HASH_ALG_SHA256_ALLOW select BOOT_AES_MBEDTLS_DEPENDENCIES if MBEDTLS_BUILTIN && BOOT_ENCRYPT_IMAGE @@ -277,6 +283,7 @@ config BOOT_SIGNATURE_TYPE_ECDSA_P256 if BOOT_SIGNATURE_TYPE_ECDSA_P256 choice BOOT_ECDSA_IMPLEMENTATION prompt "Ecdsa implementation" + default BOOT_ECDSA_PSA if NRF_SECURITY default BOOT_ECDSA_TINYCRYPT config BOOT_ECDSA_TINYCRYPT @@ -293,11 +300,12 @@ config BOOT_ECDSA_CC310 config BOOT_ECDSA_PSA bool "Use psa cryptoo" + depends on NRF_SECURITY select BOOT_USE_PSA_CRYPTO select PSA_CRYPTO_CLIENT select PSA_CRYPTO_C - select BOOT_IMG_HASH_ALG_SHA256_ALLOW - select BOOT_IMG_HASH_ALG_SHA512_ALLOW + select BOOT_IMG_HASH_ALG_SHA256_ALLOW if !PSA_CORE_LITE + select BOOT_IMG_HASH_ALG_SHA512_ALLOW if !PSA_CORE_LITE select BOOT_ECDSA_PSA_DEPENDENCIES endchoice # Ecdsa implementation @@ -329,6 +337,7 @@ config BOOT_SIGNATURE_TYPE_PURE choice BOOT_ED25519_IMPLEMENTATION prompt "Ecdsa implementation" + default BOOT_ED25519_PSA if NRF_SECURITY default BOOT_ED25519_TINYCRYPT config BOOT_ED25519_TINYCRYPT @@ -349,15 +358,15 @@ config BOOT_ED25519_MBEDTLS config BOOT_ED25519_PSA bool "Use PSA crypto" - select MBEDTLS + depends on NRF_SECURITY select BOOT_USE_PSA_CRYPTO select PSA_CRYPTO_CLIENT select PSA_CRYPTO_C select MBEDTLS_PSA_CRYPTO_C select MBEDTLS_ASN1_PARSE_C if MBEDTLS_BUILTIN - select MBEDTLS_ENABLE_HEAP - select BOOT_IMG_HASH_ALG_SHA256_ALLOW - select BOOT_IMG_HASH_ALG_SHA512_ALLOW + select MBEDTLS_ENABLE_HEAP if !PSA_CORE_LITE + select BOOT_IMG_HASH_ALG_SHA256_ALLOW if !PSA_CORE_LITE + select BOOT_IMG_HASH_ALG_SHA512_ALLOW if !PSA_CORE_LITE select BOOT_ED25519_PSA_DEPENDENCIES select BOOT_X25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE @@ -388,6 +397,64 @@ config BOOT_BYPASS_KEY_MATCH Enabling this option turns off key matching, slightly reducing MCUboot code and boot time. +config BOOT_SIGNATURE_USING_KMU + bool "Use KMU stored keys for signature verification" + depends on NRF_SECURITY + depends on CRACEN_LIB_KMU + select PSA_WANT_ALG_GCM + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_AES_KEY_SIZE_256 + select PSA_WANT_ALG_SP800_108_COUNTER_CMAC + select PSA_WANT_ALG_CMAC + select PSA_WANT_ALG_ECB_NO_PADDING + help + MCUboot will use keys provisioned to the device key management unit for signature + verification instead of compiling in key data from a file. + +if BOOT_SIGNATURE_USING_KMU + +config BOOT_SIGNATURE_KMU_SLOTS + int "KMU key slots" + range 1 3 + default 1 + help + Selects the number of KMU key slots (also known as generations) to use when verifying + an image. + +config NCS_BOOT_SIGNATURE_KMU_UROT_MAPPING + bool "Use original mapping [DEPRECATED]" + depends on SOC_SERIES_NRF54LX + depends on MCUBOOT_MCUBOOT_IMAGE_NUMBER = -1 + select DEPRECATED + help + When this option is enabled, it will use the UROT_PUBKEY key slot IDs for the MCUboot + image which are assigned for the non-immutable bootloader IDs, otherwise it will use + the key set for the mode that MCUboot is used in (non-immutable slots when b0 is + enabled, or immutable slots when b0 is not enabled). + +endif + +config BOOT_KEYS_REVOCATION + bool "Auto revoke previous gen key" + help + Automatically revoke previous generation key upon new valid key usage. + +config BOOT_KMU_KEYS_REVOCATION + bool + depends on BOOT_KEYS_REVOCATION + default y if BOOT_SIGNATURE_USING_KMU + help + Enabling KMU key revocation backend. + +config NCS_BOOT_SIGNATURE_USING_ITS + bool "Use ITS stored keys for signature verification" + depends on NRF_SECURITY + help + MCUboot will use keys provisioned to the internal trusted storage for signature + verification instead of compiling in key data from a file. + +if !BOOT_SIGNATURE_USING_KMU && !NCS_BOOT_SIGNATURE_USING_ITS + config BOOT_SIGNATURE_KEY_FILE string "PEM key file" default "root-ec-p256.pem" if BOOT_SIGNATURE_TYPE_ECDSA_P256 @@ -405,6 +472,8 @@ config BOOT_SIGNATURE_KEY_FILE with the public key information will be written in a format expected by MCUboot. +endif + config MCUBOOT_CLEANUP_ARM_CORE bool "Perform core cleanup before chain-load the application" depends on CPU_CORTEX_M || ARMV7_R @@ -427,6 +496,13 @@ config MCUBOOT_CLEANUP_RAM help Sets contents of memory to 0 before jumping to application. +config NCS_MCUBOOT_DISABLE_SELF_RWX + bool "Disable read and execution on self NVM" + depends on (SOC_NRF54L15_CPUAPP || SOC_NRF54L10_CPUAPP || SOC_NRF54L05_CPUAPP) && !FPROTECT + help + Sets RRAMC's region no.4 protection before jumping to application. + It disables reads writes and execution memory area which holds MCUBOOT. + config MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP bool "Infinite loop after RAM cleanup" depends on MCUBOOT_CLEANUP_RAM @@ -434,6 +510,14 @@ config MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP Verification option that keeps execution in infinite loop after RAM cleanup has been performed. +# Disable MBEDTLS from being selected if NRF_SECURITY is enabled, and use default NRF_SECURITY +# configuration file for MBEDTLS +config MBEDTLS + depends on !NRF_SECURITY + +config NRF_SECURITY + select MBEDTLS_PROMPTLESS + config MBEDTLS_CFG_FILE # It might be awkward to define an Mbed TLS header file when TinyCrypt # is used, but the fact is that Mbed TLS' ASN1 parse module is used @@ -711,6 +795,7 @@ if BOOT_ENCRYPT_X25519 && BOOT_USE_PSA_CRYPTO choice BOOT_HMAC_SHA prompt "SHA used for HMAC and HKDF in encryption key exchange" + default BOOT_HMAC_SHA512 if BOOT_ENCRYPT_X25519 && SOC_SERIES_NRF54LX default BOOT_HMAC_SHA256 help HMAC/HKDF sha algorithm may be selected to synchronize sha @@ -755,7 +840,7 @@ config BOOT_ENCRYPTION_KEY_FILE config BOOT_MAX_IMG_SECTORS_AUTO bool "Calculate maximum sectors automatically" - default y + default y if !PARTITION_MANAGER_ENABLED help If this option is enabled then the maximum number of supported sectors per image will be calculated automatically from the flash erase sizes and size of each partition for @@ -934,6 +1019,7 @@ config BOOT_USB_DFU_WAIT select IMG_MANAGER select STREAM_FLASH select MULTITHREADING + imply DEPRECATION_TEST help If y, MCUboot waits for a prescribed duration of time to allow for USB DFU to be invoked. Please note DFU always updates the @@ -946,6 +1032,7 @@ config BOOT_USB_DFU_GPIO select IMG_MANAGER select STREAM_FLASH select MULTITHREADING + imply DEPRECATION_TEST help If y, MCUboot uses GPIO to detect whether to invoke USB DFU. @@ -1061,6 +1148,7 @@ config MCUBOOT_HW_DOWNGRADE_PREVENTION config MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_LIMITED bool "HW based downgrade prevention counter has limited number of updates" depends on MCUBOOT_HW_DOWNGRADE_PREVENTION + default y if SOC_NRF5340_CPUAPP || SOC_SERIES_NRF91X || SOC_SERIES_NRF54LX help When this option is set, the hardware downgrade prevention counter has limited number of updates. This option will enable checking @@ -1083,6 +1171,8 @@ config MCUBOOT_UUID_CID Provide an image class identification scheme to prevent processing images for a different CPU or device produced by the same vendor. +rsource "uuid/Kconfig" + config BOOT_WATCHDOG_FEED bool "Feed the watchdog while doing swap" default y if WATCHDOG @@ -1155,6 +1245,7 @@ config BOOT_DISABLE_CACHES config MCUBOOT_BOOT_BANNER bool "Use MCUboot boot banner" depends on BOOT_BANNER + depends on !NCS_BOOT_BANNER depends on "$(APP_VERSION_EXTENDED_STRING)" != "" default y help @@ -1183,13 +1274,19 @@ config MCUBOOT_USE_TLV_ALLOW_LIST config BOOT_DECOMPRESSION_SUPPORT bool + depends on NRF_COMPRESS && NRF_COMPRESS_DECOMPRESSION && (NRF_COMPRESS_LZMA_VERSION_LZMA1 || NRF_COMPRESS_LZMA_VERSION_LZMA2) + depends on !SINGLE_APPLICATION_SLOT && BOOT_UPGRADE_ONLY + default y help Hidden symbol which should be selected if a system provided decompression support. if BOOT_DECOMPRESSION_SUPPORT menuconfig BOOT_DECOMPRESSION - bool "Decompression" + bool "Decompression [EXPERIMENTAL]" + select NRF_COMPRESS_CLEANUP + select PM_USE_CONFIG_SRAM_SIZE if SOC_NRF54L15_CPUAPP + select EXPERIMENTAL help If enabled, will include support for compressed images being loaded to the secondary slot which then get decompressed into the primary slot. This mode allows the secondary slot to @@ -1198,9 +1295,9 @@ menuconfig BOOT_DECOMPRESSION if BOOT_DECOMPRESSION config BOOT_DECOMPRESSION_BUFFER_SIZE - int "Write buffer size" + int range 16 16384 - default 4096 + default NRF_COMPRESS_CHUNK_SIZE help The size of a secondary buffer used for writing decompressed data to the storage device. @@ -1308,6 +1405,10 @@ config USB_DEVICE_PRODUCT config MCUBOOT_BOOTUTIL_LIB_OWN_LOG bool +config NRF_MCUBOOT_BOOT_REQUEST + bool + imply FIND_NEXT_SLOT_HOOKS if BOOT_DIRECT_XIP || BOOT_RAM_LOAD + config MCUBOOT_VERIFY_IMG_ADDRESS bool "Verify reset address of image in secondary slot" depends on UPDATEABLE_IMAGE_NUMBER > 1 @@ -1326,4 +1427,32 @@ config MCUBOOT_VERIFY_IMG_ADDRESS also be useful when BOOT_DIRECT_XIP is enabled, to ensure that the image linked at the correct address is loaded. +config NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT + int "Number of image validation attempts" + default 1 + help + Number of image validation attempts performed before an image is considered invalid. + A wait is done between each attempt to allow for recovery from a temporary disruption. + This can prevent erasing an image when initial validation fails. + Wait time is controlled by MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS. + +config NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS + int "Time between image validation attempts" + depends on NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + default 5000 + help + Time between image validation attempts, in milliseconds. + Allows for recovery from transient bit flips or similar situations. + +config NCS_MCUBOOT_BOOT_REQUEST_TEST_SETS_BOOT_PREFERENCE + bool "Set boot preference if a slot is marked for test" + help + This option allows to verify boot preference requests through issuing + the image test. + Using this option is not recommended in production systems, because + it will boot any newly transferred image, even if it has a lower + version than the current one. + The rollback protection (using security counters) will still be + effective. + source "Kconfig.zephyr" diff --git a/boot/zephyr/Kconfig.firmware_loader b/boot/zephyr/Kconfig.firmware_loader index 1ba223949..376dc06f7 100644 --- a/boot/zephyr/Kconfig.firmware_loader +++ b/boot/zephyr/Kconfig.firmware_loader @@ -8,7 +8,7 @@ menu "Firmware loader entrance methods" menuconfig BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO bool "GPIO" - depends on GPIO + depends on GPIO || NCS_BM help Use a GPIO to enter firmware loader mode. @@ -42,6 +42,12 @@ config BOOT_FIRMWARE_LOADER_PIN_RESET Checks if the module reset was caused by the reset pin and will remain in bootloader firmware loader mode if it was. +config NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ + bool "Check boot mode via bootloader request" + depends on NRF_MCUBOOT_BOOT_REQUEST + help + Allows for entering firmware loader mode by using bootloader rquests. + endmenu endif diff --git a/boot/zephyr/Kconfig.serial_recovery b/boot/zephyr/Kconfig.serial_recovery index 45d252408..c57e48197 100644 --- a/boot/zephyr/Kconfig.serial_recovery +++ b/boot/zephyr/Kconfig.serial_recovery @@ -38,6 +38,7 @@ config BOOT_SERIAL_UART config BOOT_SERIAL_CDC_ACM bool "CDC ACM" select USB_DEVICE_STACK + imply DEPRECATION_TEST help This setting will choose CDC ACM for serial recovery unless chosen "zephyr,uart-mcumgr" is present, in which case the chosen takes @@ -46,9 +47,14 @@ config BOOT_SERIAL_CDC_ACM endchoice +DT_COMPAT_SIM_FLASH:= zephyr,sim-flash +DT_SIM_FLASH_PATH := $(dt_nodelabel_path,flash_sim0) + config MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD bool "Allow to select image number for DFU" - depends on !SINGLE_APPLICATION_SLOT + # Allow this option to be selected in cases where support for direct uploading to nRF5340 + # network core should be supported + depends on !SINGLE_APPLICATION_SLOT || (SINGLE_APPLICATION_SLOT && SOC_NRF5340_CPUAPP && BOOT_IMAGE_ACCESS_HOOK_NRF5340 && FLASH_SIMULATOR && $(dt_compat_enabled,$(DT_COMPAT_SIM_FLASH))) help With the option enabled, the mcuboot serial recovery will respect the "image" field in mcumgr image update frame @@ -186,6 +192,12 @@ config BOOT_SERIAL_PIN_RESET Checks if the module reset was caused by the reset pin and will remain in bootloader serial recovery mode if it was. +config NRF_BOOT_SERIAL_BOOT_REQ + bool "Check boot mode via bootloader request subsystem" + depends on NRF_MCUBOOT_BOOT_REQUEST + help + Allows for entering serial recovery mode by using bootloader requests. + endmenu config BOOT_SERIAL_IMG_GRP_HASH diff --git a/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp.conf index 30594ff39..cddf7b646 100644 --- a/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp.conf +++ b/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -6,6 +6,9 @@ # Ensure that the SPI NOR driver is disabled by default CONFIG_SPI_NOR=n +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + CONFIG_BOOT_WATCHDOG_FEED=n # Power domains forced on by default on boot, no need diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf new file mode 100644 index 000000000..15a9a228d --- /dev/null +++ b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf @@ -0,0 +1,8 @@ +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_FLASH=y +CONFIG_MAIN_STACK_SIZE=20480 +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay new file mode 100644 index 000000000..ba6274221 --- /dev/null +++ b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay @@ -0,0 +1,48 @@ +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + zephyr,code-partition = &boot_partition; + }; +}; + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +/delete-node/ &storage_partition; + +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; + + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00014000>; + }; + + slot0_partition: partition@14000 { + label = "image-0"; + reg = <0x000014000 0x0015A000>; + }; + + storage_partition: partition@16E000 { + label = "storage"; + reg = < 0x16E000 0x9000 >; + }; + }; +}; + +&mx25r64 { + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x000000000 0x0015A000>; + }; + }; +}; diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf b/boot/zephyr/boards/nrf54lm20dk_nrf54lm20a_cpuapp.conf similarity index 58% rename from boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf rename to boot/zephyr/boards/nrf54lm20dk_nrf54lm20a_cpuapp.conf index c8fcd32c3..816560405 100644 --- a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf +++ b/boot/zephyr/boards/nrf54lm20dk_nrf54lm20a_cpuapp.conf @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Nordic Semiconductor ASA +# Copyright (c) 2025 Nordic Semiconductor ASA # # SPDX-License-Identifier: Apache-2.0 # @@ -7,7 +7,7 @@ CONFIG_BOOT_MAX_IMG_SECTORS=256 # Ensure that the SPI NOR driver is disabled by default CONFIG_SPI_NOR=n -CONFIG_BOOT_WATCHDOG_FEED=n +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n -# Ensure the fastest RRAM write operations -CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/boot/zephyr/boards/nrf54lm20pdk_nrf54lm20a_cpuapp.conf b/boot/zephyr/boards/nrf54lm20pdk_nrf54lm20a_cpuapp.conf new file mode 100644 index 000000000..816560405 --- /dev/null +++ b/boot/zephyr/boards/nrf54lm20pdk_nrf54lm20a_cpuapp.conf @@ -0,0 +1,13 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Ensure that the SPI NOR driver is disabled by default +CONFIG_SPI_NOR=n + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf b/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf index f2e42fd64..4c27c5da6 100644 --- a/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf +++ b/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf @@ -1,3 +1,78 @@ -CONFIG_NORDIC_QSPI_NOR=n -CONFIG_SPI=n +CONFIG_SIZE_OPTIMIZATIONS=y + +CONFIG_SYSTEM_CLOCK_NO_WAIT=y +CONFIG_PM=n + +CONFIG_MAIN_STACK_SIZE=10240 +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y + +# Flash +CONFIG_FLASH=y +CONFIG_BOOT_ERASE_PROGRESSIVELY=y +CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS=y +CONFIG_FPROTECT=y + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + +# MCUBoot serial +CONFIG_GPIO=y +CONFIG_GPIO_NRFX_INTERRUPT=n +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by QSPI +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 + +# Required by USB CONFIG_MULTITHREADING=y + +# USB +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n +CONFIG_USB_DEVICE_REMOTE_WAKEUP=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor ASA" +CONFIG_USB_DEVICE_PRODUCT="Bootloader Thingy:53" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x5300 +CONFIG_USB_CDC_ACM=y + +# Decrease memory footprint +CONFIG_CBPRINTF_NANO=y +CONFIG_TIMESLICING=n +CONFIG_BOOT_BANNER=n +CONFIG_NCS_BOOT_BANNER=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n +CONFIG_LOG=n +CONFIG_ERRNO=n +CONFIG_PRINTK=n +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_SPI=n +CONFIG_I2C=n +CONFIG_UART_NRFX=n + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 +CONFIG_BOOT_UPGRADE_ONLY=y +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +# Enable custom command to erase settings partition. +CONFIG_ENABLE_MGMT_PERUSER=y +CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y + +CONFIG_ISR_TABLES_LOCAL_DECLARATION=y +CONFIG_LTO=y diff --git a/boot/zephyr/boards/thingy91_nrf52840.conf b/boot/zephyr/boards/thingy91_nrf52840.conf new file mode 100644 index 000000000..c0d183401 --- /dev/null +++ b/boot/zephyr/boards/thingy91_nrf52840.conf @@ -0,0 +1,34 @@ +# Disable Zephyr console +CONFIG_LOG=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# The build won't fit on the partition allocated for it without size +# optimizations. +CONFIG_SIZE_OPTIMIZATIONS=y +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x12000 + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_NRFX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUboot serial recovery +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_COMPOSITE_DEVICE=y +CONFIG_USB_MASS_STORAGE=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x520F diff --git a/boot/zephyr/boards/thingy91_nrf9160.conf b/boot/zephyr/boards/thingy91_nrf9160.conf new file mode 100644 index 000000000..1bf2e424d --- /dev/null +++ b/boot/zephyr/boards/thingy91_nrf9160.conf @@ -0,0 +1,13 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Disable Flash protection +CONFIG_FPROTECT=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y diff --git a/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf b/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf new file mode 100644 index 000000000..d3e253b65 --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf @@ -0,0 +1,63 @@ +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=110 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +# Disable Zephyr console +CONFIG_LOG=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_NRFX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUboot serial recovery +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_COMPOSITE_DEVICE=y +CONFIG_USB_MASS_STORAGE=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x910A + +CONFIG_BOOT_SERIAL_BOOT_MODE=y + +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x13E00 + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 +CONFIG_BOOT_UPGRADE_ONLY=y +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +CONFIG_BOOT_IMAGE_ACCESS_HOOKS=y + +# Makes it possible to update the network core using the flash simulator +CONFIG_NRF53_RECOVERY_NETWORK_CORE=y + +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y + +# Skip checks on the secondary image to make it possible to update MCUBoot on S1/S0 +CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS=n + +CONFIG_BOOT_SERIAL_NO_APPLICATION=y +CONFIG_FW_INFO_FIRMWARE_VERSION=2 diff --git a/boot/zephyr/boards/thingy91x_nrf9151.conf b/boot/zephyr/boards/thingy91x_nrf9151.conf new file mode 100644 index 000000000..8088686e0 --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf9151.conf @@ -0,0 +1,20 @@ +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=512 + +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_SPI_NOR_SFDP_DEVICETREE=y + +# Disable Zephyr console and use UART for MCUboot serial recovery instead +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y + +CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y +CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y + +CONFIG_FW_INFO_FIRMWARE_VERSION=2 diff --git a/boot/zephyr/boards/thingy91x_nrf9151.overlay b/boot/zephyr/boards/thingy91x_nrf9151.overlay new file mode 100644 index 000000000..7f2818c0d --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf9151.overlay @@ -0,0 +1,4 @@ +&uart0 { + status = "okay"; + current-speed = < 1000000 >; +}; diff --git a/boot/zephyr/decompression.c b/boot/zephyr/decompression.c new file mode 100644 index 000000000..c8b279a75 --- /dev/null +++ b/boot/zephyr/decompression.c @@ -0,0 +1,1524 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include "compression/decompression.h" +#include "bootutil/crypto/sha.h" +#include "bootutil/bootutil_log.h" + +#if !defined(__BOOTSIM__) +#define TARGET_STATIC static +#else +#define TARGET_STATIC +#endif + +#if defined(MCUBOOT_SIGN_RSA) +#if MCUBOOT_SIGN_RSA_LEN == 2048 +#define EXPECTED_SIG_TLV IMAGE_TLV_RSA2048_PSS +#elif MCUBOOT_SIGN_RSA_LEN == 3072 +#define EXPECTED_SIG_TLV IMAGE_TLV_RSA3072_PSS +#endif +#elif defined(MCUBOOT_SIGN_EC256) || \ + defined(MCUBOOT_SIGN_EC384) || \ + defined(MCUBOOT_SIGN_EC) +#define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA_SIG +#elif defined(MCUBOOT_SIGN_ED25519) +#define EXPECTED_SIG_TLV IMAGE_TLV_ED25519 +#endif + +#define DECOMP_BUF_SIZE CONFIG_BOOT_DECOMPRESSION_BUFFER_SIZE +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) +/* Extra buffer space for being able to writeback ARM thumb decompression output, + * which may be of +2 bytes more size than its input. + */ +#define DECOMP_BUF_EXTRA_SIZE 2 +#else +#define DECOMP_BUF_EXTRA_SIZE 0 +#endif +#define DECOMP_BUF_ALLOC_SIZE (DECOMP_BUF_SIZE + DECOMP_BUF_EXTRA_SIZE) + +#define DECRYPTION_BLOCK_SIZE_AES128 16 +#define DECRYPTION_BLOCK_SIZE_AES256 32 + +/* Number of times that consumed data by decompression system can be 0 in a row before aborting */ +#define OFFSET_ZERO_CHECK_TIMES 3 + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static int boot_sha_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, uint32_t protected_size, + uint8_t *buf, size_t buf_size, bootutil_sha_context *sha_ctx); + +bool boot_is_compressed_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state) +{ + /* Image is compressed in secondary slot, need to check if fits into the primary slot */ + bool opened_flash_area = false; + int primary_fa_id; + int rc; + int size_check; + int size; + uint32_t protected_tlvs_size; + uint32_t decompressed_size; + + primary_fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), BOOT_SLOT_PRIMARY); + + if (primary_fa_id == fap->fa_id) { + BOOT_LOG_ERR("Primary slots cannot be compressed, image: %d", BOOT_CURR_IMG(state)); + return false; + } + + if (BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY) == NULL) { + opened_flash_area = true; + } + + rc = flash_area_open(primary_fa_id, &BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)); + assert(rc == 0); + + size_check = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)); + + if (opened_flash_area) { + (void)flash_area_close(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)); + } + + rc = bootutil_get_img_decomp_size(hdr, fap, &decompressed_size); + + if (rc) { + return false; + } + + if (!boot_u32_safe_add(&size, decompressed_size, hdr->ih_hdr_size)) { + return false; + } + + rc = boot_size_protected_tlvs(hdr, fap, &protected_tlvs_size); + + if (rc) { + return false; + } + + if (!boot_u32_safe_add(&size, size, protected_tlvs_size)) { + return false; + } + + if (size >= size_check) { + BOOT_LOG_ERR("Compressed image too large, decompressed image size: 0x%x, slot size: 0x%x", + size, size_check); + return false; + } + + return true; +} + +static bool is_compression_object_valid(struct nrf_compress_implementation *compression) +{ + if (compression == NULL || compression->init == NULL || compression->deinit == NULL || + compression->decompress_bytes_needed == NULL || compression->decompress == NULL) { + return false; + } + + return true; +} + +#ifdef MCUBOOT_ENC_IMAGES +int bootutil_get_img_decrypted_comp_size(const struct image_header *hdr, + const struct flash_area *fap, uint32_t *img_comp_size) +{ + if (hdr == NULL || fap == NULL || img_comp_size == NULL) { + return BOOT_EBADARGS; + } else if (hdr->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + if (!IS_ENCRYPTED(hdr)) { + /* Update is not encrypted so use size from header */ + *img_comp_size = hdr->ih_img_size; + } else { + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_COMP_DEC_SIZE, true); + + if (rc) { + return rc; + } + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + + if (rc != 0) { + return -1; + } + + if (len != sizeof(*img_comp_size)) { + BOOT_LOG_ERR("Invalid decompressed image size TLV: %d", len); + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, img_comp_size, len); + + if (rc) { + BOOT_LOG_ERR("Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off, len, fap->fa_id, rc); + return BOOT_EFLASH; + } + } + + return 0; +} +#endif + +int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, uint8_t *tmp_buf, + uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len) +{ + int rc; + uint32_t read_pos = 0; + uint32_t write_pos = 0; + uint32_t protected_tlv_size = 0; + uint32_t decompressed_image_size; + uint32_t output_size_total = 0; + struct nrf_compress_implementation *compression_lzma = NULL; + struct nrf_compress_implementation *compression_arm_thumb = NULL; + TARGET_STATIC struct image_header modified_hdr; + bootutil_sha_context sha_ctx; + +#ifdef MCUBOOT_ENC_IMAGES + struct enc_key_data *enc_state; + int image_index; + uint32_t comp_size = 0; + uint8_t decryption_block_size = 0; + + rc = bootutil_get_img_decrypted_comp_size(hdr, fap, &comp_size); + + if (rc) { + BOOT_LOG_ERR("Invalid/missing image decrypted compressed size value"); + rc = BOOT_EBADIMAGE; + goto finish_end; + } + + if (state == NULL) { + enc_state = NULL; + image_index = 0; + } else { + enc_state = BOOT_CURR_ENC(state); + image_index = BOOT_CURR_IMG(state); + } + + /* Encrypted images only exist in the secondary slot */ + if (MUST_DECRYPT(fap, image_index, hdr) && + !boot_enc_valid(enc_state, 1)) { + return -1; + } + + if (MUST_DECRYPT(fap, image_index, hdr)) { + if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES128; + } else if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES256; + } else { + LOG_ERR("Unknown decryption block size"); + rc = BOOT_EBADIMAGE; + goto finish_end; + } + } +#endif + + bootutil_sha_init(&sha_ctx); + + /* Setup decompression system */ +#if CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA1 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1)) { +#elif CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA2 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) { +#endif + /* Compressed image does not use the correct compression type which is supported by this + * build + */ + BOOT_LOG_ERR("Invalid image compression flags: no supported compression found"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + compression_lzma = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_LZMA); + compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); + + if (!is_compression_object_valid(compression_lzma) || + !is_compression_object_valid(compression_arm_thumb)) { + /* Compression library missing or missing required function pointer */ + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + /* We need a modified header which has the updated sizes, start with the original header */ + memcpy(&modified_hdr, hdr, sizeof(modified_hdr)); + + /* Extract the decompressed image size from the protected TLV, set it and remove the + * compressed image flags + */ + rc = bootutil_get_img_decomp_size(hdr, fap, &decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine decompressed size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + rc = compression_lzma->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + rc = compression_arm_thumb->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Calculate the protected TLV size, these will not include the decompressed + * sha/size/signature entries + */ + rc = boot_size_protected_tlvs(hdr, fap, &protected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine protected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_protect_tlv_size = protected_tlv_size; + bootutil_sha_update(&sha_ctx, &modified_hdr, sizeof(modified_hdr)); + read_pos = sizeof(modified_hdr); + + while (read_pos < modified_hdr.ih_hdr_size) { + uint32_t copy_size = tmp_buf_sz; + + if ((read_pos + copy_size) > modified_hdr.ih_hdr_size) { + copy_size = modified_hdr.ih_hdr_size - read_pos; + } + + rc = flash_area_read(fap, read_pos, tmp_buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (hdr->ih_hdr_size + read_pos), copy_size, fap->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + bootutil_sha_update(&sha_ctx, tmp_buf, copy_size); + read_pos += copy_size; + } + + /* Read in compressed data, decompress and add to hash calculation */ + read_pos = 0; + +#ifdef MCUBOOT_ENC_IMAGES + while (read_pos < comp_size) { + uint32_t copy_size = comp_size - read_pos; +#else + while (read_pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - read_pos; +#endif + uint32_t tmp_off = 0; + uint8_t offset_zero_check = 0; + + if (copy_size > tmp_buf_sz) { + copy_size = tmp_buf_sz; + } + + rc = flash_area_read(fap, (hdr->ih_hdr_size + read_pos), tmp_buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (hdr->ih_hdr_size + read_pos), copy_size, fap->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + +#ifdef MCUBOOT_ENC_IMAGES + if (MUST_DECRYPT(fap, image_index, hdr)) { + uint8_t dummy_bytes = 0; + + if ((copy_size % decryption_block_size)) { + dummy_bytes = decryption_block_size - (copy_size % decryption_block_size); + memset(&tmp_buf[copy_size], 0x00, dummy_bytes); + } + + boot_enc_decrypt(enc_state, 1, read_pos, (copy_size + dummy_bytes), (read_pos & 0xf), + tmp_buf); + } +#endif + + /* Decompress data in chunks, writing it back with a larger write offset of the primary + * slot than read size of the secondary slot + */ + while (tmp_off < copy_size) { + uint32_t offset = 0; + uint8_t *output = NULL; + uint32_t output_size = 0; + uint32_t chunk_size; + bool last_packet = false; + + chunk_size = compression_lzma->decompress_bytes_needed(NULL); + + if (chunk_size > (copy_size - tmp_off)) { + chunk_size = (copy_size - tmp_off); + } + +#ifdef MCUBOOT_ENC_IMAGES + if ((read_pos + tmp_off + chunk_size) >= comp_size) { +#else + if ((read_pos + tmp_off + chunk_size) >= hdr->ih_img_size) { +#endif + last_packet = true; + } + + rc = compression_lzma->decompress(NULL, &tmp_buf[tmp_off], chunk_size, last_packet, + &offset, &output, &output_size); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + write_pos += output_size; + + if (write_pos > decompressed_image_size) { + BOOT_LOG_ERR("Decompressed image larger than claimed TLV size, at least: %d", + write_pos); + rc = BOOT_EBADIMAGE; + goto finish; + } + + /* Additional dry-run validity checks */ + if (last_packet == true && write_pos == 0) { + /* Last packet and we still have no output, this is a faulty update */ + BOOT_LOG_ERR("All compressed data consumed without any output, image not valid"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + if (offset == 0) { + /* If the decompression system continually consumes 0 bytes, then there is a + * problem with this update image, abort and mark image as bad + */ + if (offset_zero_check >= OFFSET_ZERO_CHECK_TIMES) { + BOOT_LOG_ERR("Decompression system returning no output data, image not valid"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + ++offset_zero_check; + + break; + } else { + offset_zero_check = 0; + } + + /* Copy data to secondary buffer for calculating hash */ + if (output_size > 0) { + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + /* Run this through the ARM thumb filter */ + uint32_t offset_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + uint32_t processed_size = 0; + uint32_t output_size_arm_thumb = 0; + + while (processed_size < output_size) { + uint32_t current_size = output_size - processed_size; + bool arm_thumb_last_packet = false; + + if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) { + current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE; + } + + if (last_packet && (processed_size + current_size) == + output_size) { + arm_thumb_last_packet = true; + } + + rc = compression_arm_thumb->decompress(NULL, &output[processed_size], + current_size, arm_thumb_last_packet, + &offset_arm_thumb, + &output_arm_thumb, + &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + bootutil_sha_update(&sha_ctx, output_arm_thumb, output_size_arm_thumb); + output_size_total += output_size_arm_thumb; + processed_size += current_size; + } + } else { + bootutil_sha_update(&sha_ctx, output, output_size); + output_size_total += output_size; + } + } + + tmp_off += offset; + } + + read_pos += copy_size; + } + + if (modified_hdr.ih_img_size != output_size_total) { + BOOT_LOG_ERR("Decompression expected output_size mismatch: %d vs %d", + modified_hdr.ih_img_size, output_size_total); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* If there are any protected TLVs present, add them after the main decompressed image */ + if (modified_hdr.ih_protect_tlv_size > 0) { + rc = boot_sha_protected_tlvs(hdr, fap, modified_hdr.ih_protect_tlv_size, tmp_buf, + tmp_buf_sz, &sha_ctx); + } + + bootutil_sha_finish(&sha_ctx, hash_result); + +finish: + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + +finish_without_clean: + bootutil_sha_drop(&sha_ctx); + +#ifdef MCUBOOT_ENC_IMAGES +finish_end: +#endif + return rc; +} + +static int boot_copy_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_dst, + uint32_t protected_size, uint8_t *buf, size_t buf_size, + uint16_t *buf_pos, uint32_t *written) +{ + int rc; + uint32_t off; + uint32_t write_pos = 0; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_PROT_INFO_MAGIC, + .it_tlv_tot = protected_size, + }; + uint16_t info_size_left = sizeof(tlv_info_header); + + while (info_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (info_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_info_header_address = (uint8_t *)&tlv_info_header; + + if (single_copy_size > info_size_left) { + single_copy_size = info_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_info_header_address[sizeof(tlv_info_header) - + info_size_left], single_copy_size); + *buf_pos += single_copy_size; + info_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Skip these TLVs as they are not needed */ + continue; + } else { + uint16_t header_size_left = sizeof(tlv_header); + uint16_t data_size_left = len; + + tlv_header.it_type = type; + tlv_header.it_len = len; + + while (header_size_left > 0 || data_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + uint8_t *tlv_header_address = (uint8_t *)&tlv_header; + + if (header_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > header_size_left) { + single_copy_size = header_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_header_address[sizeof(tlv_header) - + header_size_left], + single_copy_size); + *buf_pos += single_copy_size; + copy_size -= single_copy_size; + header_size_left -= single_copy_size; + } + + if (data_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > data_size_left) { + single_copy_size = data_size_left; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + (len - data_size_left)), + &buf[*buf_pos], single_copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + (len - data_size_left)), single_copy_size, fap_src->fa_id, rc); + goto out; + } + + *buf_pos += single_copy_size; + data_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + } + } + + *written = write_pos; + +out: + return rc; +} + +static int boot_sha_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, uint32_t protected_size, + uint8_t *buf, size_t buf_size, bootutil_sha_context *sha_ctx) +{ + int rc; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_PROT_INFO_MAGIC, + .it_tlv_tot = protected_size, + }; + + bootutil_sha_update(sha_ctx, &tlv_info_header, sizeof(tlv_info_header)); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, true); + if (rc) { + goto out; + } + + while (true) { + uint32_t read_off = 0; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Skip these TLVs as they are not needed */ + continue; + } + + tlv_header.it_type = type; + tlv_header.it_len = len; + + bootutil_sha_update(sha_ctx, &tlv_header, sizeof(tlv_header)); + + while (read_off < len) { + uint32_t copy_size = buf_size; + + if (copy_size > (len - read_off)) { + copy_size = len - read_off; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + read_off), buf, copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + read_off), copy_size, fap_src->fa_id, rc); + goto out; + } + + bootutil_sha_update(sha_ctx, buf, copy_size); + read_off += copy_size; + } + } + +out: + return rc; +} + +int boot_size_protected_tlvs(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *sz) +{ + int rc = 0; + uint32_t tlv_size; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + + *sz = 0; + tlv_size = hdr->ih_protect_tlv_size; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Exclude these TLVs as they will be copied to the unprotected area */ + tlv_size -= len + sizeof(struct image_tlv); + } + } + + if (!rc) { + if (tlv_size == sizeof(struct image_tlv_info)) { + /* If there are no entries then omit protected TLV section entirely */ + tlv_size = 0; + } + + *sz = tlv_size; + } + +out: + return rc; +} + +int boot_size_unprotected_tlvs(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *sz) +{ + int rc = 0; + uint32_t tlv_size; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + + *sz = 0; + tlv_size = sizeof(struct image_tlv_info); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } else if (bootutil_tlv_iter_is_prot(&it, off) && type != IMAGE_TLV_DECOMP_SHA && + type != IMAGE_TLV_DECOMP_SIGNATURE) { + /* Include size of protected hash and signature as these will be replacing the + * original ones + */ + continue; + } else if (type == EXPECTED_HASH_TLV || type == EXPECTED_SIG_TLV || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Exclude the original unprotected TLVs for signature and hash, the length of the + * signature of the compressed data might not be the same size as the signaute of the + * decompressed data, as is the case when using ECDSA-P256 + */ + continue; + } + + tlv_size += len + sizeof(struct image_tlv); + } + + if (!rc) { + if (tlv_size == sizeof(struct image_tlv_info)) { + /* If there are no entries in the unprotected TLV section then there is something wrong + * with this image + */ + BOOT_LOG_ERR("No unprotected TLVs in post-decompressed image output, image is invalid"); + rc = BOOT_EBADIMAGE; + goto out; + } + + *sz = tlv_size; + } + +out: + return rc; +} + +static int boot_copy_unprotected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_dst, + uint32_t unprotected_size, uint8_t *buf, size_t buf_size, + uint16_t *buf_pos, uint32_t *written) +{ + int rc; + uint32_t write_pos = 0; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv_iter it_protected; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_INFO_MAGIC, + .it_tlv_tot = unprotected_size, + }; + uint16_t info_size_left = sizeof(tlv_info_header); + + while (info_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (info_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_info_header_address = (uint8_t *)&tlv_info_header; + + if (single_copy_size > info_size_left) { + single_copy_size = info_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_info_header_address[sizeof(tlv_info_header) - + info_size_left], single_copy_size); + *buf_pos += single_copy_size; + info_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, false); + if (rc) { + goto out; + } + + while (true) { + uint16_t header_size_left = sizeof(tlv_header); + uint16_t data_size_left; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } else if (bootutil_tlv_iter_is_prot(&it, off)) { + /* Skip protected TLVs */ + continue; + } + + /* Change the values of these fields from having the data in the compressed image + * unprotected TLV (which is valid only for the compressed image data) to having the + * fields in the protected TLV section (which is valid for the decompressed image data). + * The compressed data is no longer needed + */ + if (type == EXPECTED_HASH_TLV || type == EXPECTED_SIG_TLV) { + rc = bootutil_tlv_iter_begin(&it_protected, hdr, fap_src, (type == EXPECTED_HASH_TLV ? + IMAGE_TLV_DECOMP_SHA : + IMAGE_TLV_DECOMP_SIGNATURE), + true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it_protected, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + } + + if (type == IMAGE_TLV_DECOMP_SHA) { + type = EXPECTED_HASH_TLV; + } else { + type = EXPECTED_SIG_TLV; + } + } + + data_size_left = len; + tlv_header.it_type = type; + tlv_header.it_len = len; + + while (header_size_left > 0 || data_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (header_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_header_address = (uint8_t *)&tlv_header; + + if (single_copy_size > header_size_left) { + single_copy_size = header_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_header_address[sizeof(tlv_header) - header_size_left], + single_copy_size); + *buf_pos += single_copy_size; + copy_size -= single_copy_size; + header_size_left -= single_copy_size; + } + + if (data_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > data_size_left) { + single_copy_size = data_size_left; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + len - data_size_left), + &buf[*buf_pos], single_copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + (len - data_size_left)), single_copy_size, fap_src->fa_id, rc); + goto out; + } + + *buf_pos += single_copy_size; + data_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + } + + *written = write_pos; + +out: + return rc; +} + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) +/** + * @brief Helper function for in-place ARM Thumb filtering. + * This function places the decompressed data back into the same buffer + * at the beginning, overwriting the compressed data. WARNING: because + * ARM Thumb filtering can return +-2 more/less bytes than the input, + * the buffer provided needs to have free DECOMP_BUF_EXTRA_SIZE bytes at + * the beginning and provide valid data for filtering after these. + * + * @param[in] arm_thumb_impl Pointer to the ARM Thumb decompression implementation. + * @param[in,out] buf Pointer to the buffer containing the compressed data / filtered data. + * @param[in] buf_size Size of the buffer (including DECOMP_BUF_EXTRA_SIZE bytes at the beginning). + * @param[out] out_size Pointer to a variable where the size of the filtered data will be stored. + * @param[in] last_part Indicates if this is the last part of the data to be filtered. + * + * @return 0 on success, BOOT_EBADSTATUS on error. + */ +static int boot_arm_thumb_filter(struct nrf_compress_implementation * const arm_thumb_impl, + uint8_t *buf, size_t buf_size, size_t *out_size, bool last_part) { + + uint32_t filter_writeback_pos = 0; + uint32_t processed_size = 0; + int rc; + + while (processed_size < (buf_size - DECOMP_BUF_EXTRA_SIZE)) { + uint32_t offset_arm_thumb = 0; + uint32_t output_size_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + uint32_t current_size = (buf_size - DECOMP_BUF_EXTRA_SIZE - processed_size); + bool arm_thumb_last_packet = false; + + if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) { + current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE; + } + + if (last_part && (processed_size + current_size) == (buf_size - DECOMP_BUF_EXTRA_SIZE)) { + arm_thumb_last_packet = true; + } + + rc = arm_thumb_impl->decompress(NULL, + &buf[processed_size + + DECOMP_BUF_EXTRA_SIZE], + current_size, + arm_thumb_last_packet, + &offset_arm_thumb, + &output_arm_thumb, + &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + return BOOT_EBADSTATUS; + } + + if (output_size_arm_thumb > (buf_size - filter_writeback_pos)) { + BOOT_LOG_ERR("Filter writeback position exceeds buffer size"); + return BOOT_EBADSTATUS; + } + + memcpy(&buf[filter_writeback_pos], output_arm_thumb, + output_size_arm_thumb); + filter_writeback_pos += output_size_arm_thumb; + processed_size += offset_arm_thumb; + } + *out_size = filter_writeback_pos; + + return 0; +} +#endif /* CONFIG_NRF_COMPRESS_ARM_THUMB */ + +int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_src, + uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size) +{ + int rc; + uint32_t pos = 0; + uint16_t decomp_buf_size = 0; + uint16_t write_alignment; + uint32_t write_pos = 0; + uint32_t protected_tlv_size = 0; + uint32_t unprotected_tlv_size = 0; + uint32_t tlv_write_size = 0; + uint32_t decompressed_image_size; + struct nrf_compress_implementation *compression_lzma = NULL; + struct nrf_compress_implementation *compression_arm_thumb = NULL; + struct image_header *hdr; + TARGET_STATIC uint8_t decomp_buf[DECOMP_BUF_ALLOC_SIZE] __attribute__((aligned(4))); + TARGET_STATIC struct image_header modified_hdr; + uint16_t decomp_buf_max_size; + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + uint8_t unaligned_data_length = 0; +#endif + +#ifdef MCUBOOT_ENC_IMAGES + uint32_t comp_size = 0; + uint8_t decryption_block_size = 0; +#endif + + hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY); + +#ifdef MCUBOOT_ENC_IMAGES + rc = bootutil_get_img_decrypted_comp_size(hdr, fap_src, &comp_size); + + if (rc) { + BOOT_LOG_ERR("Invalid/missing image decrypted compressed size value"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + if (IS_ENCRYPTED(hdr)) { + if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES128; + } else if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES256; + } + } +#endif + + /* Setup decompression system */ +#if CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA1 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1)) { +#elif CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA2 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) { +#endif + /* Compressed image does not use the correct compression type which is supported by this + * build + */ + BOOT_LOG_ERR("Invalid image compression flags: no supported compression found"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + compression_lzma = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_LZMA); + compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); + + if (!is_compression_object_valid(compression_lzma) || + !is_compression_object_valid(compression_arm_thumb)) { + /* Compression library missing or missing required function pointer */ + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + write_alignment = flash_area_align(fap_dst); + + decomp_buf_max_size = DECOMP_BUF_SIZE - (DECOMP_BUF_SIZE % write_alignment); + + memcpy(&modified_hdr, hdr, sizeof(modified_hdr)); + + rc = bootutil_get_img_decomp_size(hdr, fap_src, &decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine decompressed size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + rc = compression_lzma->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + rc = compression_arm_thumb->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Calculate protected TLV size for target image once items are removed */ + rc = boot_size_protected_tlvs(hdr, fap_src, &protected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine protected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_protect_tlv_size = protected_tlv_size; + + rc = boot_size_unprotected_tlvs(hdr, fap_src, &unprotected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine unprotected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + /* Write out the image header first, this should be a multiple of the write size */ + rc = flash_area_write(fap_dst, off_dst, &modified_hdr, sizeof(modified_hdr)); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off_dst, sizeof(modified_hdr), fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + /* Read in, decompress and write out data */ +#ifdef MCUBOOT_ENC_IMAGES + while (pos < comp_size) { + uint32_t copy_size = comp_size - pos; +#else + while (pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - pos; +#endif + uint32_t tmp_off = 0; + + if (copy_size > buf_size) { + copy_size = buf_size; + } + + rc = flash_area_read(fap_src, off_src + hdr->ih_hdr_size + pos, buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_src + hdr->ih_hdr_size + pos), copy_size, fap_src->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(hdr)) { + uint8_t dummy_bytes = 0; + + if ((copy_size % decryption_block_size)) { + dummy_bytes = decryption_block_size - (copy_size % decryption_block_size); + memset(&buf[copy_size], 0x00, dummy_bytes); + } + + boot_enc_decrypt(BOOT_CURR_ENC(state), 1, pos, (copy_size + dummy_bytes), (pos & 0xf), buf); + } +#endif + + /* Decompress data in chunks, writing it back with a larger write offset of the primary + * slot than read size of the secondary slot + */ + while (tmp_off < copy_size) { + uint32_t offset = 0; + uint32_t output_size = 0; + uint32_t chunk_size; + uint32_t compression_buffer_pos = 0; + uint8_t *output = NULL; + bool last_packet = false; + + chunk_size = compression_lzma->decompress_bytes_needed(NULL); + + if (chunk_size > (copy_size - tmp_off)) { + chunk_size = (copy_size - tmp_off); + } + +#ifdef MCUBOOT_ENC_IMAGES + if ((pos + tmp_off + chunk_size) >= comp_size) { +#else + if ((pos + tmp_off + chunk_size) >= hdr->ih_img_size) { +#endif + last_packet = true; + } + + rc = compression_lzma->decompress(NULL, &buf[tmp_off], chunk_size, last_packet, + &offset, &output, &output_size); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Copy data to secondary buffer for writing out */ + while (output_size > 0) { + uint32_t data_size = (decomp_buf_max_size - decomp_buf_size); + + if (data_size > output_size) { + data_size = output_size; + } + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + memcpy(&decomp_buf[decomp_buf_size + DECOMP_BUF_EXTRA_SIZE], + &output[compression_buffer_pos], data_size); + } else +#endif + { + memcpy(&decomp_buf[decomp_buf_size], &output[compression_buffer_pos], + data_size); + } + + compression_buffer_pos += data_size; + + decomp_buf_size += data_size; + output_size -= data_size; + + /* Write data out from secondary buffer when it is full */ + if (decomp_buf_size == decomp_buf_max_size) { +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + + uint32_t filter_output_size; + + /* Run this through the ARM thumb filter */ + rc = boot_arm_thumb_filter(compression_arm_thumb, + &decomp_buf[unaligned_data_length], + decomp_buf_size - unaligned_data_length + DECOMP_BUF_EXTRA_SIZE, + &filter_output_size, + last_packet && output_size == 0); + + if (rc) { + goto finish; + } + + decomp_buf_size = filter_output_size + unaligned_data_length; + unaligned_data_length = decomp_buf_size % write_alignment; + + rc = flash_area_write(fap_dst, + (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, + (decomp_buf_size - unaligned_data_length)); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), + (decomp_buf_size - unaligned_data_length), + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + memmove(decomp_buf, + &decomp_buf[decomp_buf_size - unaligned_data_length], + unaligned_data_length); + write_pos += decomp_buf_size - unaligned_data_length; + decomp_buf_size = unaligned_data_length; + } else +#endif + { + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, decomp_buf_max_size); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_max_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += decomp_buf_max_size; + decomp_buf_size = 0; + } + } + } + + tmp_off += offset; + } + + pos += copy_size; + } + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT && decomp_buf_size > 0) { + /* Extra data that has not been written out that needs ARM thumb filter applied */ + + uint32_t filter_output_size; + + rc = boot_arm_thumb_filter(compression_arm_thumb, + &decomp_buf[unaligned_data_length], + decomp_buf_size - unaligned_data_length + DECOMP_BUF_EXTRA_SIZE, + &filter_output_size, + true); + + if (rc) { + goto finish; + } + + decomp_buf_size = filter_output_size + unaligned_data_length; + + if (decomp_buf_size > decomp_buf_max_size) { + /* It can happen if ARM thumb decompression returned +2 bytes and we had near full + * decomp_buf. We still can hold these additional 2 bytes because of + * DECOMP_BUF_EXTRA_SIZE allocated. */ + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, decomp_buf_max_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_max_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + memmove(decomp_buf, &decomp_buf[decomp_buf_max_size], + (decomp_buf_size - decomp_buf_max_size)); + + decomp_buf_size = decomp_buf_size - decomp_buf_max_size; + write_pos += decomp_buf_max_size; + } + } +#endif + + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + + if (protected_tlv_size > 0) { + rc = boot_copy_protected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size + + write_pos), protected_tlv_size, + decomp_buf, decomp_buf_max_size, &decomp_buf_size, + &tlv_write_size); + + if (rc) { + BOOT_LOG_ERR("Protected TLV copy failure: %d", rc); + goto finish; + } + + write_pos += tlv_write_size; + } + + tlv_write_size = 0; + rc = boot_copy_unprotected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size + + write_pos), unprotected_tlv_size, + decomp_buf, decomp_buf_max_size, &decomp_buf_size, + &tlv_write_size); + + if (rc) { + BOOT_LOG_ERR("Protected TLV copy failure: %d", rc); + goto finish; + } + + write_pos += tlv_write_size; + + /* Check if we have unwritten data buffered up and, if so, write it out */ + if (decomp_buf_size > 0) { + uint32_t write_padding_size = write_alignment - (decomp_buf_size % write_alignment); + + /* Check if additional write padding should be applied to meet the minimum write size */ + if (write_alignment > 1 && write_padding_size) { + uint8_t flash_erased_value; + + flash_erased_value = flash_area_erased_val(fap_dst); + memset(&decomp_buf[decomp_buf_size], flash_erased_value, write_padding_size); + decomp_buf_size += write_padding_size; + } + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf, + decomp_buf_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += decomp_buf_size; + decomp_buf_size = 0; + } + +finish: + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + +finish_without_clean: + memset(decomp_buf, 0, sizeof(decomp_buf)); + + return rc; +} + +int bootutil_get_img_decomp_size(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *img_decomp_size) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + if (hdr == NULL || fap == NULL || img_decomp_size == NULL) { + return BOOT_EBADARGS; + } else if (hdr->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SIZE, true); + + if (rc) { + return rc; + } + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + + if (rc != 0) { + return -1; + } + + if (len != sizeof(*img_decomp_size)) { + BOOT_LOG_ERR("Invalid decompressed image size TLV: %d", len); + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, img_decomp_size, len); + + if (rc) { + BOOT_LOG_ERR("Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off, len, fap->fa_id, rc); + return BOOT_EFLASH; + } + + return 0; +} diff --git a/boot/zephyr/external_crypto.conf b/boot/zephyr/external_crypto.conf new file mode 100644 index 000000000..8181ad51c --- /dev/null +++ b/boot/zephyr/external_crypto.conf @@ -0,0 +1,20 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# These configurations should be used when using nrf/samples/bootloader +# as the immutable bootloader (B0), and MCUBoot as the second stage updateable +# bootloader. + +# Set ECDSA as signing mechanism +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +# Use crypto backend from B0 +CONFIG_BOOT_NRF_EXTERNAL_CRYPTO=y +CONFIG_SECURE_BOOT_CRYPTO=y +CONFIG_SB_CRYPTO_CLIENT_ECDSA_SECP256R1=y +CONFIG_SB_CRYPTO_CLIENT_SHA256=y +CONFIG_BL_SHA256_EXT_API_REQUIRED=y +CONFIG_BL_SECP256R1_EXT_API_REQUIRED=y diff --git a/boot/zephyr/firmware_loader.c b/boot/zephyr/firmware_loader.c index 18070bc25..217336755 100644 --- a/boot/zephyr/firmware_loader.c +++ b/boot/zephyr/firmware_loader.c @@ -17,6 +17,9 @@ #include "io/io.h" #include "mcuboot_config/mcuboot_config.h" +#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST +#include +#endif /* CONFIG_NRF_MCUBOOT_BOOT_REQUEST */ BOOT_LOG_MODULE_DECLARE(mcuboot); @@ -183,6 +186,12 @@ boot_go(struct boot_rsp *rsp) } #endif +#ifdef CONFIG_NRF_BOOT_FIRMWARE_LOADER_BOOT_REQ + if (boot_request_detect_firmware_loader()) { + boot_firmware_loader = true; + } +#endif + /* Check if firmware loader button is pressed. TODO: check all entrance methods */ if (boot_firmware_loader == true) { FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp); diff --git a/boot/zephyr/firmware_loader_bm.c b/boot/zephyr/firmware_loader_bm.c new file mode 100644 index 000000000..14d5c96bd --- /dev/null +++ b/boot/zephyr/firmware_loader_bm.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" +#include + +#include "io/io.h" +#include "mcuboot_config/mcuboot_config.h" + +#define IMAGE_TLV_INSTALLER_IMAGE 0xa0 + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static struct flash_area fa_app_installer = { + .fa_id = 1, + .fa_off = FIXED_PARTITION_OFFSET(slot0_partition), + .fa_size = FIXED_PARTITION_SIZE(slot0_partition), + .fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)), +}; + +static struct image_header hdr_app_installer = { 0 }; + +static struct flash_area fa_softdevice = { + .fa_id = 2, + .fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)), +}; + +static struct image_header hdr_softdevice = { 0 }; + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER +static struct flash_area fa_firmware_loader = { + .fa_id = 3, + .fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)), +}; + +static struct image_header hdr_firmware_loader = { 0 }; +#endif + +/** + * Validate hash of a primary boot image. + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +static fih_ret validate_image(const struct flash_area *fap, struct image_header *hdr) +{ + static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL); + FIH_RET(fih_rc); +} + +/** + * Gather information on image and prepare for booting. Will boot from main + * image if none of the enabled entrance modes for the firmware loader are set, + * otherwise will boot the firmware loader. Note: firmware loader must be a + * valid signed image with the same signing key as the application image. + * + * @param[out] rsp Parameters for booting image, on success + * + * @return FIH_SUCCESS on success; non-zero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + bool boot_firmware_loader = false; + FIH_DECLARE(fih_rc, FIH_FAILURE); + bool softdevice_area_valid = false; + bool firmware_loader_area_valid = false; + int rc; + bool app_installer_image_valid = false; + bool softdevice_image_valid = false; + bool firmware_loader_image_valid = false; + bool app_installer_is_installer_image = false; + + bm_installs_init(); + + if (bm_installs_is_valid()) { + off_t start_address = 0; + size_t image_size = 0; + + rc = bm_installs_get_image_data(BM_INSTALLS_IMAGE_INDEX_SOFTDEVICE, &start_address, + &image_size); + + if (!rc) { + fa_softdevice.fa_off = start_address; + fa_softdevice.fa_size = image_size; + + if (start_address < fa_app_installer.fa_off) { + /* Invalid start address for SoftDevice */ + goto invalid_softdevice; + } + + fa_app_installer.fa_size = start_address - fa_app_installer.fa_off; + + rc = boot_image_load_header(&fa_softdevice, &hdr_softdevice); + + if (!rc) { + softdevice_area_valid = true; + } + } + +invalid_softdevice: +#ifdef CONFIG_BOOT_FIRMWARE_LOADER + start_address = 0; + image_size = 0; + rc = bm_installs_get_image_data(BM_INSTALLS_IMAGE_INDEX_FIRMWARE_LOADER, &start_address, + &image_size); + + if (!rc) { + fa_firmware_loader.fa_off = start_address; + fa_firmware_loader.fa_size = image_size; + + if (start_address < fa_app_installer.fa_off) { + /* Invalid start address for firmware loader */ + goto invalid_firmware_loader; + } + + fa_app_installer.fa_size = start_address - fa_app_installer.fa_off; + + rc = boot_image_load_header(&fa_firmware_loader, &hdr_softdevice); + + if (!rc) { + firmware_loader_area_valid = true; + } + } +#endif + } + +invalid_firmware_loader: + rc = boot_image_load_header(&fa_app_installer, &hdr_app_installer); + + if (rc) { + BOOT_LOG_ERR("Failed loading application/installer image header: %d", rc); + } else { + FIH_CALL(validate_image, fih_rc, &fa_app_installer, &hdr_app_installer); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + struct image_tlv_iter it; + uint32_t off2; + uint16_t len2; + + app_installer_image_valid = true; + + if (hdr_app_installer.ih_protect_tlv_size > 0) { + rc = bootutil_tlv_iter_begin(&it, &hdr_app_installer, &fa_app_installer, + IMAGE_TLV_INSTALLER_IMAGE, true); + + if (rc == 0) { + rc = bootutil_tlv_iter_next(&it, &off2, &len2, NULL); + + if (rc == 0 && len2 == sizeof(app_installer_is_installer_image)) { + rc = LOAD_IMAGE_DATA(&hdr_app_installer, &fa_app_installer, off2, + &app_installer_is_installer_image, len2); + + if (rc != 0) { + app_installer_is_installer_image = false; + } + } + } + } + } + } + + if (softdevice_area_valid) { + fih_rc = FIH_FAILURE; + rc = boot_image_load_header(&fa_softdevice, &hdr_softdevice); + + if (rc) { + BOOT_LOG_ERR("Failed loading SoftDevice image header: %d", rc); + } else { + FIH_CALL(validate_image, fih_rc, &fa_softdevice, &hdr_softdevice); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + softdevice_image_valid = true; + } + } + } + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER + if (firmware_loader_area_valid) { + fih_rc = FIH_FAILURE; + rc = boot_image_load_header(&fa_firmware_loader, &hdr_firmware_loader); + + if (rc) { + BOOT_LOG_ERR("Failed loading firmware loader image header: %d", rc); + } else { + FIH_CALL(validate_image, fih_rc, &fa_firmware_loader, &hdr_firmware_loader); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + firmware_loader_image_valid = true; + } + } + } +#endif + + BOOT_LOG_DBG("Application/installer partition offset: 0x%lx, size: 0x%x, type: %d", + fa_app_installer.fa_off, fa_app_installer.fa_size, + app_installer_is_installer_image); + BOOT_LOG_DBG("SoftDevice partition offset: 0x%lx, size: 0x%x", fa_softdevice.fa_off, + fa_softdevice.fa_size); +#ifdef CONFIG_BOOT_FIRMWARE_LOADER + BOOT_LOG_DBG("Firmware loader off: 0x%lx, size: 0x%x", fa_firmware_loader.fa_off, + fa_firmware_loader.fa_size); + BOOT_LOG_DBG("SoftDevice area valid: %d, Firmware loader area valid: %d, " + "Application/installer image valid: %d, SoftDevice image valid: %d, " + "Firmware loader image valid: %d", softdevice_area_valid, + firmware_loader_area_valid, app_installer_image_valid, softdevice_image_valid, + firmware_loader_image_valid); +#else + BOOT_LOG_DBG("SoftDevice area valid: %d, Application/installer image valid: %d, " + "SoftDevice image valid: %d", softdevice_area_valid, app_installer_image_valid, + softdevice_image_valid); +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO + if (io_detect_pin() && !io_boot_skip_serial_recovery()) { + BOOT_LOG_DBG("GPIO detected for firmware loader mode"); + boot_firmware_loader = true; + } +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET + if (io_detect_pin_reset()) { + BOOT_LOG_DBG("Pin reset detected for firmware loader mode"); + boot_firmware_loader = true; + } +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE + if (io_detect_boot_mode()) { + BOOT_LOG_DBG("Boot mode detected for firmware loader mode"); + boot_firmware_loader = true; + } +#endif + + if (app_installer_image_valid == true && app_installer_is_installer_image == true) { + /* Installer image is present, this gets priority */ + BOOT_LOG_DBG("Booting installer"); + rsp->br_image_off = flash_area_get_off(&fa_app_installer); + rsp->br_hdr = &hdr_app_installer; + } else if (boot_firmware_loader == true && softdevice_image_valid == true && + firmware_loader_image_valid == true) { + /* Boot firmware loader */ + BOOT_LOG_INF("Booting firmware loader"); + rsp->br_image_off = flash_area_get_off(&fa_firmware_loader); + rsp->br_hdr = &hdr_firmware_loader; + } else if (app_installer_image_valid == true && softdevice_image_valid == true) { + /* Boot main application */ + BOOT_LOG_INF("Booting main application"); + rsp->br_image_off = flash_area_get_off(&fa_app_installer); + rsp->br_hdr = &hdr_app_installer; + } else if (app_installer_image_valid == false && softdevice_image_valid == true && + firmware_loader_image_valid == true) { + /* Boot firmware loader due to missing main image */ + BOOT_LOG_INF("Booting firmware loader due to missing application image"); + rsp->br_image_off = flash_area_get_off(&fa_firmware_loader); + rsp->br_hdr = &hdr_firmware_loader; + } else { + /* Cannot boot in this configuration */ + BOOT_LOG_ERR("Error: no bootable configuration found"); + return -1; + } + + rsp->br_flash_dev_id = flash_area_get_device_id(&fa_app_installer); + + return 0; +} diff --git a/boot/zephyr/hooks_sample.c b/boot/zephyr/hooks_sample.c index fc7bd2fa4..42639965e 100644 --- a/boot/zephyr/hooks_sample.c +++ b/boot/zephyr/hooks_sample.c @@ -94,7 +94,8 @@ int boot_img_install_stat_hook(int image_index, int slot, int *img_install_stat) return BOOT_HOOK_REGULAR; } -int boot_find_next_slot_hook(struct boot_loader_state *state, uint8_t image, uint32_t *active_slot) +int boot_find_next_slot_hook(struct boot_loader_state *state, uint8_t image, + enum boot_slot *active_slot) { return BOOT_HOOK_REGULAR; } diff --git a/boot/zephyr/include/compression/decompression.h b/boot/zephyr/include/compression/decompression.h new file mode 100644 index 000000000..2104c4eb6 --- /dev/null +++ b/boot/zephyr/include/compression/decompression.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_DECOMPRESSION_ +#define H_DECOMPRESSION_ + +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "../src/bootutil_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Checks if a compressed image header is valid. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param state Bootloader state object. + * + * @return true if valid; false if invalid. + */ +bool boot_is_compressed_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state); + +/** + * Reads in compressed image data from a slot, decompresses it and writes it out to a destination + * slot, including corresponding image headers and TLVs. + * + * @param state Bootloader state object. + * @param fap_src Flash area of the source slot. + * @param fap_dst Flash area of the destination slot. + * @param off_src Offset of the source slot to read from (should be 0). + * @param off_dst Offset of the destination slot to write to (should be 0). + * @param sz Size of the source slot data. + * @param buf Temporary buffer for reading data from. + * @param buf_size Size of temporary buffer. + * + * @return 0 on success; nonzero on failure. + */ +int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_src, + uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size); + +/** + * Gets the total data size (excluding headers and TLVs) of a compressed image when it is + * decompressed. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param img_decomp_size Pointer to variable that will be updated with the decompressed image + * size. + * + * @return 0 on success; nonzero on failure. + */ +int bootutil_get_img_decomp_size(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *img_decomp_size); + +/** + * Calculate MCUboot-compatible image hash of compressed image slot. + * + * @param state MCUboot state. + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param tmp_buf Temporary buffer for reading data from. + * @param tmp_buf_sz Size of temporary buffer. + * @param hash_result Pointer to a variable that will be updated with the image hash. + * @param seed Not currently used, set to NULL. + * @param seed_len Not currently used, set to 0. + * + * @return 0 on success; nonzero on failure. + */ +int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, uint8_t *tmp_buf, + uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len); + +/** + * Calculates the size that the compressed image protected TLV section will occupy once the image + * has been decompressed. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param sz Pointer to variable that will be updated with the protected TLV size. + * + * @return 0 on success; nonzero on failure. + */ +int boot_size_protected_tlvs(const struct image_header *hdr, const struct flash_area *fap_src, + uint32_t *sz); + +#ifdef __cplusplus +} +#endif + +#endif /* H_DECOMPRESSION_ */ diff --git a/boot/zephyr/include/io/io.h b/boot/zephyr/include/io/io.h index 563281fa2..f0a9ba6eb 100644 --- a/boot/zephyr/include/io/io.h +++ b/boot/zephyr/include/io/io.h @@ -67,6 +67,45 @@ static inline bool io_boot_skip_serial_recovery() { uint32_t rr = nrfx_reset_reason_get(); +#ifdef NRF_RESETINFO + /* The following reset reasons should allow to enter firmware recovery or + * loader through an IO state. + */ + const uint32_t allowed_reset_reasons = ( + /* Reset from power on reset (reset reason POR or BOR). */ + NRFX_RESET_REASON_POR_MASK +#ifdef RESETINFO_RESETREAS_GLOBAL_RESETPOR_Msk + /* Reset from power on reset (reset reason other than POR or BOR). */ + | RESETINFO_RESETREAS_GLOBAL_RESETPOR_Msk +#endif + /* Reset from pin reset is not masked: it always enables the io-based recovery. */ + /* Reset from the watchdog timer. */ + | NRFX_RESET_REASON_DOG_MASK + /* Reset from CTRL-AP. */ + | NRFX_RESET_REASON_CTRLAP_MASK + /* Reset due to secure domain system reset request. */ + | NRFX_RESET_REASON_SREQ_MASK + /* Reset due to secure domain watchdog 0 timer. */ + | NRFX_RESET_REASON_SECWDT0_MASK + /* Reset due to secure domain watchdog 1 timer. */ + | NRFX_RESET_REASON_SECWDT1_MASK + /* Reset due to secure domain lockup. */ + | NRFX_RESET_REASON_LOCKUP_MASK +#if NRF_RESETINFO_HAS_LOCAL_WDT + /* Reset from the local watchdog timer 0. */ + | NRFX_RESET_REASON_LOCAL_DOG0_MASK + /* Reset from the local watchdog timer 1. */ + | NRFX_RESET_REASON_LOCAL_DOG1_MASK +#endif + /* Reset from the local soft reset request. */ + | NRFX_RESET_REASON_LOCAL_SREQ_MASK + /* Reset from local CPU lockup. */ + | NRFX_RESET_REASON_LOCAL_LOCKUP_MASK + ); + + rr &= ~allowed_reset_reasons; +#endif /* NRF_RESETINFO */ + return !(rr == 0 || (rr & NRFX_RESET_REASON_RESETPIN_MASK)); } #else diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 8df98ede6..4fd821f85 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -49,6 +49,8 @@ #endif #elif defined(CONFIG_BOOT_USE_PSA_CRYPTO) #define MCUBOOT_USE_PSA_CRYPTO +#elif defined(CONFIG_BOOT_USE_NRF_EXTERNAL_CRYPTO) +#define MCUBOOT_USE_NRF_EXTERNAL_CRYPTO #endif #ifdef CONFIG_BOOT_IMG_HASH_ALG_SHA512 @@ -66,6 +68,10 @@ #define MCUBOOT_HW_KEY #endif +#ifdef CONFIG_NCS_BOOT_SIGNATURE_USING_ITS +#define MCUBOOT_BUILTIN_KEY +#endif + #ifdef CONFIG_BOOT_VALIDATE_SLOT0 #define MCUBOOT_VALIDATE_PRIMARY_SLOT #endif diff --git a/boot/zephyr/include/nrf_cleanup.h b/boot/zephyr/include/nrf_cleanup.h new file mode 100644 index 000000000..8cd8fe377 --- /dev/null +++ b/boot/zephyr/include/nrf_cleanup.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_NRF_CLEANUP_ +#define H_NRF_CLEANUP_ + +/** + * Perform cleanup on some peripheral resources used by MCUBoot prior chainload + * the application. + * + * This function disables all RTC instances and UARTE instances. + * It Disables their interrupts signals as well. + */ +void nrf_cleanup_peripheral(void); + +/** + * Perform cleanup of non-secure RAM that may have been used by MCUBoot. + */ +void nrf_cleanup_ns_ram(void); + +/** + * Crypto key storage housekeeping. Intended to clean up key objects from + * crypto backend and apply key policies that should take effect after + * MCUboot no longer needs access to keys. + */ +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +extern void nrf_crypto_keys_housekeeping(void); +#else +#define nrf_crypto_keys_housekeeping() do {} while (0) +#endif + +#endif diff --git a/boot/zephyr/include/sysflash/pm_sysflash.h b/boot/zephyr/include/sysflash/pm_sysflash.h new file mode 100644 index 000000000..0cb16292f --- /dev/null +++ b/boot/zephyr/include/sysflash/pm_sysflash.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef __PM_SYSFLASH_H__ +#define __PM_SYSFLASH_H__ +/* Blocking the __SYSFLASH_H__ */ +#define __SYSFLASH_H__ + +#include +#include +#include + +#ifndef CONFIG_SINGLE_APPLICATION_SLOT + +/* Each pair of slots is separated by , and there is no terminating character */ +#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID, +#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID, +#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID, +#define FLASH_AREA_IMAGE_3_SLOTS PM_MCUBOOT_PRIMARY_3_ID, PM_MCUBOOT_SECONDARY_3_ID, + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#ifdef CONFIG_NCS_IS_VARIANT_IMAGE +#define MCUBOOT_S0_S1_SLOTS PM_S0_ID, PM_MCUBOOT_SECONDARY_ID, +#else +#define MCUBOOT_S0_S1_SLOTS PM_S1_ID, PM_MCUBOOT_SECONDARY_ID, +#endif +#else +#define MCUBOOT_S0_S1_SLOTS +#endif + +#if (MCUBOOT_IMAGE_NUMBER == 1) || (MCUBOOT_IMAGE_NUMBER == 2 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) || (MCUBOOT_IMAGE_NUMBER == 3 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) || (MCUBOOT_IMAGE_NUMBER == 4 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 4) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS \ + FLASH_AREA_IMAGE_3_SLOTS +#else +#error Unsupported number of images +#endif + +static inline uint32_t __flash_area_ids_for_slot(int img, int slot) +{ + static const int all_slots[] = { + ALL_AVAILABLE_SLOTS + MCUBOOT_S0_S1_SLOTS + }; + return all_slots[img * 2 + slot]; +}; + +#undef FLASH_AREA_IMAGE_0_SLOTS +#undef FLASH_AREA_IMAGE_1_SLOTS +#undef FLASH_AREA_IMAGE_2_SLOTS +#undef FLASH_AREA_IMAGE_3_SLOTS +#undef MCUBOOT_S0_S1_SLOTS +#undef ALL_AVAILABLE_SLOTS + +#define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) +#define FLASH_AREA_IMAGE_SECONDARY(x) __flash_area_ids_for_slot(x, 1) + +#if !defined(CONFIG_BOOT_SWAP_USING_MOVE) +#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID +#endif + +#else /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID +/* NOTE: Scratch parition is not used by single image DFU but some of + * functions in common files reference it, so the definitions has been + * provided to allow compilation of common units. + */ +#define FLASH_AREA_IMAGE_SCRATCH 0 + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#ifndef SOC_FLASH_0_ID +#define SOC_FLASH_0_ID 0 +#endif + +#ifndef SPI_FLASH_0_ID +#define SPI_FLASH_0_ID 1 +#endif + +#endif /* __PM_SYSFLASH_H__ */ diff --git a/boot/zephyr/include/sysflash/sysflash.h b/boot/zephyr/include/sysflash/sysflash.h index 16d222280..3c3638d7f 100644 --- a/boot/zephyr/include/sysflash/sysflash.h +++ b/boot/zephyr/include/sysflash/sysflash.h @@ -4,6 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +#if USE_PARTITION_MANAGER +/* Blocking the rest of the file */ +#define __SYSFLASH_H__ +#include +#endif + #ifndef __SYSFLASH_H__ #define __SYSFLASH_H__ diff --git a/boot/zephyr/include/target.h b/boot/zephyr/include/target.h index ea160752e..856686785 100644 --- a/boot/zephyr/include/target.h +++ b/boot/zephyr/include/target.h @@ -8,6 +8,8 @@ #ifndef H_TARGETS_TARGET_ #define H_TARGETS_TARGET_ +#ifndef USE_PARTITION_MANAGER + #if defined(MCUBOOT_TARGET_CONFIG) /* * Target-specific definitions are permitted in legacy cases that @@ -47,4 +49,6 @@ #error "Target support is incomplete; cannot build mcuboot." #endif +#endif /* ifndef USE_PARTITION_MANAGER */ + #endif /* H_TARGETS_TARGET_ */ diff --git a/boot/zephyr/io_bm.c b/boot/zephyr/io_bm.c new file mode 100644 index 000000000..788bfab9e --- /dev/null +++ b/boot/zephyr/io_bm.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012-2014 Wind River Systems, Inc. + * Copyright (c) 2020 Arm Limited + * Copyright (c) 2021-2025 Nordic Semiconductor ASA + * + * Licensed 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 +#include +#include +#include +#include +#include +#include +#include + +#include "target.h" +#include "bootutil/bootutil_log.h" + +#include +#include + +#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +#include +#endif + +#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) +#include +#endif + +/* Validate serial recovery configuration */ +#ifdef CONFIG_MCUBOOT_SERIAL +#if !defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) && \ + !defined(CONFIG_BOOT_SERIAL_WAIT_FOR_DFU) && \ + !defined(CONFIG_BOOT_SERIAL_BOOT_MODE) && \ + !defined(CONFIG_BOOT_SERIAL_NO_APPLICATION) && \ + !defined(CONFIG_BOOT_SERIAL_PIN_RESET) +#error "Serial recovery selected without an entrance mode set" +#endif +#endif + +/* Validate firmware loader configuration */ +#ifdef CONFIG_BOOT_FIRMWARE_LOADER +#if !defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +#error "Firmware loader selected without an entrance mode set" +#endif +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + +void io_led_init(void) +{ + nrf_gpio_cfg_output(BOARD_PIN_LED_0); + nrf_gpio_pin_write(BOARD_PIN_LED_0, BOARD_LED_ACTIVE_STATE); +} + +void io_led_set(int value) +{ + nrf_gpio_pin_write(BOARD_PIN_LED_0, (value == 0 ? !BOARD_LED_ACTIVE_STATE : BOARD_LED_ACTIVE_STATE)); +} +#endif /* CONFIG_MCUBOOT_INDICATION_LED */ + +#if defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) || defined(CONFIG_BOOT_USB_DFU_GPIO) || \ + defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) + +#if defined(CONFIG_MCUBOOT_SERIAL) +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_SERIAL_DETECT_DELAY +#elif defined(CONFIG_BOOT_FIRMWARE_LOADER) +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_FIRMWARE_LOADER_DETECT_DELAY +#else +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_USB_DFU_DETECT_DELAY +#endif + +bool io_detect_pin(void) +{ + bool pin_active; + + nrf_gpio_cfg_input(BOARD_PIN_BTN_0, BM_BUTTONS_PIN_PULLUP); + + /* Delay 5 us for pull-up to be applied */ + k_busy_wait(5); + + pin_active = (bool)nrf_gpio_pin_read(BOARD_PIN_BTN_0); + + if (!pin_active) { + if (BUTTON_0_DETECT_DELAY > 0) { +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(50)); +#else + k_busy_wait(50000); +#endif + + /* Get the uptime for debounce purposes. */ + int64_t timestamp = k_uptime_get(); + + for(;;) { + uint32_t delta; + + pin_active = (bool)nrf_gpio_pin_read(BOARD_PIN_BTN_0); + + /* Get delta from when this started */ + delta = k_uptime_get() - timestamp; + + /* If not pressed OR if pressed > debounce period, stop. */ + if (delta >= BUTTON_0_DETECT_DELAY || pin_active) { + break; + } + + /* Delay 1 ms */ +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(1)); +#else + k_busy_wait(1000); +#endif + } + } + } + + return (bool)!pin_active; +} +#endif + +#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +bool io_detect_pin_reset(void) +{ + uint32_t reset_cause; + int rc; + + rc = hwinfo_get_reset_cause(&reset_cause); + + if (rc == 0 && (reset_cause & RESET_PIN)) { + (void)hwinfo_clear_reset_cause(); + return true; + } + + return false; +} +#endif + +#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) +bool io_detect_boot_mode(void) +{ + int32_t boot_mode; + + boot_mode = bootmode_check(BOOT_MODE_TYPE_BOOTLOADER); + + if (boot_mode == 1) { + /* Boot mode to stay in bootloader, clear status and enter serial + * recovery mode + */ + bootmode_clear(); + + return true; + } + + return false; +} +#endif diff --git a/boot/zephyr/main.c b/boot/zephyr/main.c index 1494002e7..d7e0121aa 100644 --- a/boot/zephyr/main.c +++ b/boot/zephyr/main.c @@ -47,6 +47,12 @@ #include "bootutil/fault_injection_hardening.h" #include "bootutil/mcuboot_status.h" #include "flash_map_backend/flash_map_backend.h" +#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST +#include + +/** Number of image slots. */ +#define BOOT_REQUEST_NUM_SLOTS 2 +#endif /* CONFIG_NRF_MCUBOOT_BOOT_REQUEST */ #if defined(CONFIG_MCUBOOT_UUID_VID) || defined(CONFIG_MCUBOOT_UUID_CID) #include "bootutil/mcuboot_uuid.h" @@ -76,6 +82,10 @@ #endif /* CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */ +#ifdef CONFIG_FW_INFO +#include +#endif + #ifdef CONFIG_MCUBOOT_SERIAL #include "boot_serial/boot_serial.h" #include "serial_adapter/serial_adapter.h" @@ -94,8 +104,26 @@ const struct boot_uart_funcs boot_funcs = { #include #endif -#if defined(CONFIG_LOG) && !defined(CONFIG_LOG_MODE_IMMEDIATE) && \ - !defined(CONFIG_LOG_MODE_MINIMAL) +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) && defined(CONFIG_PCD_APP) +#include +#endif + +/* CONFIG_LOG_MINIMAL is the legacy Kconfig property, + * replaced by CONFIG_LOG_MODE_MINIMAL. + */ +#if (defined(CONFIG_LOG_MODE_MINIMAL) || defined(CONFIG_LOG_MINIMAL)) +#define ZEPHYR_LOG_MODE_MINIMAL 1 +#endif + +/* CONFIG_LOG_IMMEDIATE is the legacy Kconfig property, + * replaced by CONFIG_LOG_MODE_IMMEDIATE. + */ +#if (defined(CONFIG_LOG_MODE_IMMEDIATE) || defined(CONFIG_LOG_IMMEDIATE)) +#define ZEPHYR_LOG_MODE_IMMEDIATE 1 +#endif + +#if defined(CONFIG_LOG) && !defined(ZEPHYR_LOG_MODE_IMMEDIATE) && \ + !defined(ZEPHYR_LOG_MODE_MINIMAL) #ifdef CONFIG_LOG_PROCESS_THREAD #warning "The log internal thread for log processing can't transfer the log"\ "well for MCUBoot." @@ -122,6 +150,31 @@ K_SEM_DEFINE(boot_log_sem, 1, 1); * !defined(CONFIG_LOG_MODE_MINIMAL) */ +#include "nrf_protect.h" + +#if CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL +#include +#endif + +#include +#define CLEANUP_RAM_GAP_START ((int)__ramfunc_region_start) +#define CLEANUP_RAM_GAP_SIZE ((int) (__ramfunc_end - __ramfunc_region_start)) + +#if defined(CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX) +/* Disabling R_X has to be done while running from RAM for obvious reasons. + * Moreover as a last step before jumping to application it must work even after + * RAM has been cleared, therefore these operations are performed while executing from RAM. + * RAM cleanup ommits portion of the memory where code lives. + */ +#include + +#define RRAMC_REGION_RWX_LSB 0 +#define RRAMC_REGION_RWX_WIDTH 3 +#define RRAMC_REGION_TO_LOCK_ADDR NRF_RRAMC->REGION[4].CONFIG +#define RRAMC_REGION_TO_LOCK_ADDR_H (((uint32_t)(&(RRAMC_REGION_TO_LOCK_ADDR))) >> 16) +#define RRAMC_REGION_TO_LOCK_ADDR_L (((uint32_t)(&(RRAMC_REGION_TO_LOCK_ADDR))) & 0x0000fffful) +#endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */ + BOOT_LOG_MODULE_REGISTER(mcuboot); void os_heap_init(void); @@ -148,6 +201,84 @@ struct arm_vector_table { #endif }; +static void __ramfunc jump_in(uint32_t reset) +{ + __asm__ volatile ( + /* reset -> r0 */ + " mov r0, %0\n" +#ifdef CONFIG_MCUBOOT_CLEANUP_RAM + /* Base to write -> r1 */ + " mov r1, %1\n" + /* Size to write -> r2 */ + " mov r2, %2\n" + /* Value to write -> r3 */ + " movw r3, %5\n" + /* gap start */ + " mov r4, %3\n" + /* gap size */ + " mov r5, %4\n" + "clear:\n" + " subs r6, r4, r1\n" + " cbnz r6, skip_gap\n" + " add r1, r5\n" + "skip_gap:\n" + " str r3, [r1]\n" + " add r1, r1, #1\n" + " sub r2, r2, #1\n" + " cbz r2, clear_end\n" + " b clear\n" + "clear_end:\n" + " dsb\n" +#ifdef CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP + " b clear_end\n" +#endif /* CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP */ +#endif /* CONFIG_MCUBOOT_CLEANUP_RAM */ + +#ifdef CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX + ".thumb_func\n" + "region_disable_rwx:\n" + " movw r1, %6\n" + " movt r1, %7\n" + " ldr r2, [r1]\n" + /* Size of the region should be set at this point + * by NSIB's DISABLE_NEXT_W. + * If not, set it according partition size. + */ + " ands r4, r2, %12\n" + " cbnz r4, clear_rwx\n" + " movt r2, %8\n" + "clear_rwx:\n" + " bfc r2, %9, %10\n" + /* Disallow further modifications */ + " orr r2, %11\n" + " str r2, [r1]\n" + " dsb\n" + /* Next assembly line is important for current function */ + + #endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */ + + /* Jump to reset vector of an app */ + " bx r0\n" + : + : "r" (reset), + "r" (CONFIG_SRAM_BASE_ADDRESS), + "i" (CONFIG_SRAM_SIZE * 1024), + "r" (CLEANUP_RAM_GAP_START), + "r" (CLEANUP_RAM_GAP_SIZE), + "i" (0) +#ifdef CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX + , "i" (RRAMC_REGION_TO_LOCK_ADDR_L), + "i" (RRAMC_REGION_TO_LOCK_ADDR_H), + "i" (CONFIG_PM_PARTITION_SIZE_B0_IMAGE / 1024), + "i" (RRAMC_REGION_RWX_LSB), + "i" (RRAMC_REGION_RWX_WIDTH), + "i" (RRAMC_REGION_CONFIG_LOCK_Msk), + "i" (RRAMC_REGION_CONFIG_SIZE_Msk) +#endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */ + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory" + ); +} + static void do_boot(struct boot_rsp *rsp) { /* vt is static as it shall not land on the stack, @@ -184,6 +315,31 @@ static void do_boot(struct boot_rsp *rsp) /* Disable the USB to prevent it from firing interrupts */ usb_disable(); #endif + +#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED) + uintptr_t fw_start_addr; + + rc = flash_device_base(rsp->br_flash_dev_id, &fw_start_addr); + assert(rc == 0); + + fw_start_addr += rsp->br_image_off + rsp->br_hdr->ih_hdr_size; + + const struct fw_info *firmware_info = fw_info_find(fw_start_addr); + bool provided = fw_info_ext_api_provide(firmware_info, true); + +#ifdef PM_S0_ADDRESS + /* Only fail if the immutable bootloader is present. */ + if (!provided) { + if (firmware_info == NULL) { + BOOT_LOG_WRN("Unable to find firmware info structure in %p", vt); + } + BOOT_LOG_ERR("Failed to provide EXT_APIs to %p", vt); + } +#endif +#endif +#if CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL + nrf_cleanup_peripheral(); +#endif #if CONFIG_MCUBOOT_CLEANUP_ARM_CORE cleanup_arm_interrupts(); /* Disable and acknowledge all interrupts */ @@ -254,48 +410,7 @@ static void do_boot(struct boot_rsp *rsp) #endif /* CONFIG_CPU_CORTEX_M */ #endif -#if CONFIG_MCUBOOT_CLEANUP_RAM - __asm__ volatile ( - /* vt->reset -> r0 */ - " mov r0, %0\n" - /* base to write -> r1 */ - " mov r1, %1\n" - /* size to write -> r2 */ - " mov r2, %2\n" - /* value to write -> r3 */ - " mov r3, %3\n" - "clear:\n" - " str r3, [r1]\n" - " add r1, r1, #4\n" - " sub r2, r2, #4\n" - " cbz r2, out\n" - " b clear\n" - "out:\n" - " dsb\n" -#if CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP - " b out\n" -#endif /*CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP */ - /* jump to reset vector of an app */ - " bx r0\n" - : - : "r" (vt->reset), "i" (CONFIG_SRAM_BASE_ADDRESS), - "i" (CONFIG_SRAM_SIZE * 1024), "i" (0) - : "r0", "r1", "r2", "r3", "memory" - ); -#else - -#ifdef CONFIG_CPU_CORTEX_M - ((void (*)(void))vt->reset)(); -#else - /* Some ARM CPUs like the Cortex-R5 can run in thumb mode but reset into ARM - * mode (depending on a CPU signal configurations). To do the switch into ARM - * mode, if needed, an explicit branch with exchange instruction set - * instruction is needed - */ - __asm__("bx %0\n" : : "r" (&vt->reset)); -#endif - -#endif + jump_in(vt->reset); } #elif defined(CONFIG_XTENSA) || defined(CONFIG_RISCV) @@ -494,6 +609,37 @@ static void boot_serial_enter() } #endif +static int boot_prevalidate(void) +{ +#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST + uint8_t image_index; + enum boot_slot slot; + uint32_t area_id; + const struct flash_area *fap; + int rc = boot_request_init(); + + if (rc != 0) { + return rc; + } + + for (image_index = 0; image_index < BOOT_IMAGE_NUMBER; ++image_index) { + for (slot = BOOT_SLOT_PRIMARY; slot < BOOT_SLOT_COUNT; slot++) { + if (boot_request_check_confirmed_slot(image_index, slot)) { + BOOT_LOG_DBG("Confirm image: %d slot: %d due to bootloader request.", + image_index, slot); + + area_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(area_id, &fap); + if (rc == 0) { + rc = boot_set_next(fap, true, true); + } + } + } + } +#endif + return 0; +} + int main(void) { struct boot_rsp rsp; @@ -533,6 +679,11 @@ int main(void) } #endif /* CONFIG_MCUBOOT_UUID_VID || CONFIG_MCUBOOT_UUID_CID */ + rc = boot_prevalidate(); + if (rc) { + BOOT_LOG_ERR("Failed to prevalidate the state: %d", rc); + } + #ifdef CONFIG_BOOT_SERIAL_ENTRANCE_GPIO BOOT_LOG_DBG("Checking GPIO for serial recovery"); if (io_detect_pin() && @@ -548,6 +699,13 @@ int main(void) } #endif +#ifdef CONFIG_NRF_BOOT_SERIAL_BOOT_REQ + if (boot_request_detect_recovery()) { + BOOT_LOG_DBG("Staying in serial recovery"); + boot_serial_enter(); + } +#endif + #if defined(CONFIG_BOOT_USB_DFU_GPIO) BOOT_LOG_DBG("Checking GPIO for USB DFU request"); if (io_detect_pin()) { @@ -608,6 +766,10 @@ int main(void) } BOOT_LOG_DBG("Left boot_go with success == %d", FIH_EQ(fih_rc, FIH_SUCCESS) ? 1 : 0); +#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST + (void)boot_request_clear(); +#endif + #ifdef CONFIG_BOOT_SERIAL_BOOT_MODE if (io_detect_boot_mode()) { /* Boot mode to stay in bootloader, clear status and enter serial @@ -674,7 +836,34 @@ int main(void) mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND); + /* From this point MCUboot does not need access to crypto keys. + * Clean up backend key objects and apply key access policies that + * will take effect from now through entire boot session and application + * run. + */ + nrf_crypto_keys_housekeeping(); + +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT + + rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE); + + if (rc != 0) { + BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup."); + while (1) + ; + } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) && defined(CONFIG_PCD_APP) +#if defined(PM_TFM_SECURE_ADDRESS) + pcd_lock_ram(false); +#else + pcd_lock_ram(true); +#endif +#endif +#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */ + ZEPHYR_BOOT_LOG_STOP(); + do_boot(&rsp); mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED); diff --git a/boot/zephyr/nrf54h20_custom_s2ram.S b/boot/zephyr/nrf54h20_custom_s2ram.S new file mode 100644 index 000000000..1ffa17f42 --- /dev/null +++ b/boot/zephyr/nrf54h20_custom_s2ram.S @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief ARM Cortex-M suspend-to-RAM resume intermediary code (S2RAM) + */ + + + +/* + * resume or + * reboot reboot==T + *[S2RAM sleep]---------->[ boot (reset.S) ]---->[soc_early_reset_hook]---------->[regular boot] + * | + * | resume==T + * \/ + * [jump to well-known] + * [ App reset vector ] + */ + + + +#include +#include + +GTEXT(pm_s2ram_mark_check_and_mediate) + +GTEXT(soc_early_reset_hook) +SECTION_FUNC(TEXT, soc_early_reset_hook) +#if DT_NODE_EXISTS(DT_NODELABEL(pm_s2ram_stack)) &&\ + DT_NODE_HAS_COMPAT(DT_NODELABEL(pm_s2ram_stack), zephyr_memory_region) + ldr r0, =DT_REG_ADDR(DT_NODELABEL(pm_s2ram_stack)) + DT_REG_SIZE(DT_NODELABEL(pm_s2ram_stack)) +#else +#error "The support of bridge for S2RAM resume requires dedicated small stack" +#endif + msr msp, r0 + push {r0, lr} + bl pm_s2ram_mark_check_and_mediate + pop {r0, pc} diff --git a/boot/zephyr/nrf54h20_custom_s2ram.c b/boot/zephyr/nrf54h20_custom_s2ram.c new file mode 100644 index 000000000..faa8d9b46 --- /dev/null +++ b/boot/zephyr/nrf54h20_custom_s2ram.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "pm_s2ram.h" +#include "power.h" + +#include +#include + +#include "bootutil/fault_injection_hardening.h" + +#if DT_NODE_EXISTS(DT_NODELABEL(mcuboot_s2ram)) &&\ + DT_NODE_HAS_COMPAT(DT_NODELABEL(mcuboot_s2ram), zephyr_memory_region) +/* Linker section name is given by `zephyr,memory-region` property of + * `zephyr,memory-region` compatible DT node with nodelabel `mcuboot_s2ram`. + */ +__attribute__((section(DT_PROP(DT_NODELABEL(mcuboot_s2ram), zephyr_memory_region)))) +volatile struct mcuboot_resume_s mcuboot_resume; +#else +#error "mcuboot resume support section not defined in dts" +#endif + +#define FIXED_PARTITION_ADDR(node_label) \ + (DT_REG_ADDR(DT_NODELABEL(node_label)) + \ + COND_CODE_0(DT_FIXED_PARTITION_EXISTS(DT_NODELABEL(node_label)), (0), \ + (DT_REG_ADDR(DT_GPARENT(DT_NODELABEL(node_label)))))) + +#define S2RAM_SLOT_INFO_A 0x37 +#define S2RAM_SLOT_INFO_B 0xA4 + +#ifdef CONFIG_BOOT_DIRECT_XIP +/* Called by the image manager when setting the image as active for current boot. */ +void s2ram_designate_slot(uint8_t slot) +{ + if (slot == 0) { + mcuboot_resume.slot_info = S2RAM_SLOT_INFO_A; + } else { + mcuboot_resume.slot_info = S2RAM_SLOT_INFO_B; + } +} +#endif + +struct arm_vector_table { + uint32_t msp; + uint32_t reset; +}; + +/* This could be read from slot's image_header.ih_hdr_size, but immediate value + * is much faster to reach + */ +#define APP_EXE_START_OFFSET 0x800 /* nRF54H20 */ + +void pm_s2ram_mark_check_and_mediate(void) +{ + uint32_t reset_reason = nrf_resetinfo_resetreas_local_get(NRF_RESETINFO); + + if (reset_reason != NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK) { + /* Normal boot */ + return; + } + + /* S2RAM resume expected, do doublecheck */ + if (mcuboot_resume.magic == MCUBOOT_S2RAM_RESUME_MAGIC) { + /* clear magic to avoid accidental reuse */ + mcuboot_resume.magic = 0; + } else { + /* magic not valid, normal boot */ + goto resume_failed; + } + + /* s2ram boot */ + struct arm_vector_table *vt; + +#ifdef CONFIG_BOOT_DIRECT_XIP + if (mcuboot_resume.slot_info == S2RAM_SLOT_INFO_A) { + vt = (struct arm_vector_table *) + (FIXED_PARTITION_ADDR(slot0_partition) + APP_EXE_START_OFFSET); + } else if (mcuboot_resume.slot_info == S2RAM_SLOT_INFO_B) { + vt = (struct arm_vector_table *) + (FIXED_PARTITION_ADDR(slot1_partition) + APP_EXE_START_OFFSET); + } else { + /* invalid slot info */ + goto resume_failed; + } +#else + vt = (struct arm_vector_table *) + (FIXED_PARTITION_ADDR(slot0_partition) + APP_EXE_START_OFFSET); +#endif + + /* Jump to application */ + __asm__ volatile ( + /* vt->reset -> r0 */ + " mov r0, %0\n" + /* vt->msp -> r1 */ + " mov r1, %1\n" + /* set stack pointer */ + " msr msp, r1\n" + /* jump to reset vector of an app */ + " bx r0\n" + : + : "r" (vt->reset), "r" (vt->msp) + : "r0", "r1", "memory" + ); + +resume_failed: + FIH_PANIC; +} diff --git a/boot/zephyr/nrf_cleanup.c b/boot/zephyr/nrf_cleanup.c new file mode 100644 index 000000000..c1e5a178e --- /dev/null +++ b/boot/zephyr/nrf_cleanup.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#if defined(CONFIG_NRFX_CLOCK) +#include +#endif +#include +#include +#include +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) + #include +#endif +#if defined(CONFIG_NRF_GRTC_TIMER) + #include +#endif +#if defined(NRF_PPI) + #include +#endif +#if defined(NRF_DPPIC) + #include +#endif + +#include + +#if USE_PARTITION_MANAGER +#include +#endif + +#if defined(NRF_UARTE0) || defined(NRF_UARTE1) || defined(NRF_UARTE20) || \ + defined(NRF_UARTE30) +#define NRF_UARTE_CLEANUP +#endif + +#define NRF_UARTE_SUBSCRIBE_CONF_OFFS offsetof(NRF_UARTE_Type, SUBSCRIBE_STARTRX) +#define NRF_UARTE_SUBSCRIBE_CONF_SIZE (offsetof(NRF_UARTE_Type, EVENTS_CTS) -\ + NRF_UARTE_SUBSCRIBE_CONF_OFFS) + +#define NRF_UARTE_PUBLISH_CONF_OFFS offsetof(NRF_UARTE_Type, PUBLISH_CTS) +#define NRF_UARTE_PUBLISH_CONF_SIZE (offsetof(NRF_UARTE_Type, SHORTS) -\ + NRF_UARTE_PUBLISH_CONF_OFFS) + +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) +static inline void nrf_cleanup_rtc(NRF_RTC_Type * rtc_reg) +{ + nrf_rtc_task_trigger(rtc_reg, NRF_RTC_TASK_STOP); + nrf_rtc_event_disable(rtc_reg, 0xFFFFFFFF); + nrf_rtc_int_disable(rtc_reg, 0xFFFFFFFF); +} +#endif + +#if defined(CONFIG_NRF_GRTC_TIMER) + +/** + * This function is temporary and should be removed once nrfx_grtc_uninit + * no longer resets the counter - see NRFX-8487. + */ +static inline void nrfx_grtc_uninit_no_counter_reset(void) +{ + uint32_t ch_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK; + +#if NRF_GRTC_HAS_RTCOUNTER + uint32_t grtc_all_int_mask = (NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK | + GRTC_NON_SYSCOMPARE_INT_MASK); +#else + uint32_t grtc_all_int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK; +#endif + + nrfy_grtc_int_disable(NRF_GRTC, grtc_all_int_mask); + + for (uint8_t chan = 0; ch_mask; chan++, ch_mask >>= 1) + { + nrfx_grtc_syscounter_cc_disable(chan); + nrfx_grtc_channel_free(chan); + } + nrfy_grtc_int_uninit(NRF_GRTC); +} + +static inline void nrf_cleanup_grtc(void) +{ + nrfx_grtc_uninit_no_counter_reset(); +} +#endif + +#if defined(NRF_UARTE_CLEANUP) +static NRF_UARTE_Type *nrf_uarte_to_clean[] = { +#if defined(NRF_UARTE0) + NRF_UARTE0, +#endif +#if defined(NRF_UARTE1) + NRF_UARTE1, +#endif +#if defined(NRF_UARTE20) + NRF_UARTE20, +#endif +#if defined(NRF_UARTE30) + NRF_UARTE30, +#endif +#if defined(NRF_UARTE136) + NRF_UARTE136, +#endif +}; +#endif + +#if defined(CONFIG_NRFX_CLOCK) +static void nrf_cleanup_clock(void) +{ + nrf_clock_int_disable(NRF_CLOCK, 0xFFFFFFFF); +} +#endif + +void nrf_cleanup_peripheral(void) +{ +#if defined(NRF_RTC0) + nrf_cleanup_rtc(NRF_RTC0); +#endif +#if defined(NRF_RTC1) + nrf_cleanup_rtc(NRF_RTC1); +#endif +#if defined(NRF_RTC2) + nrf_cleanup_rtc(NRF_RTC2); +#endif + +#if defined(CONFIG_NRF_GRTC_TIMER) + nrf_cleanup_grtc(); +#endif + +#if defined(NRF_UARTE_CLEANUP) + for (int i = 0; i < sizeof(nrf_uarte_to_clean) / sizeof(nrf_uarte_to_clean[0]); ++i) { + NRF_UARTE_Type *current = nrf_uarte_to_clean[i]; + + nrfy_uarte_int_disable(current, 0xFFFFFFFF); + nrfy_uarte_int_uninit(current); + nrfy_uarte_task_trigger(current, NRF_UARTE_TASK_STOPRX); + + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_RXSTARTED); + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_ENDRX); + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_RXTO); + nrfy_uarte_disable(current); + +#ifndef CONFIG_SOC_SERIES_NRF54LX + /* Disconnect pins UARTE pins + * causes issues on nRF54l SoCs, + * could be enabled once fix to NCSDK-33039 will be implemented. + */ + + uint32_t pin[4]; + + pin[0] = nrfy_uarte_tx_pin_get(current); + pin[1] = nrfy_uarte_rx_pin_get(current); + pin[2] = nrfy_uarte_rts_pin_get(current); + pin[3] = nrfy_uarte_cts_pin_get(current); + + nrfy_uarte_pins_disconnect(current); + + for (int j = 0; j < 4; j++) { + if (pin[j] != NRF_UARTE_PSEL_DISCONNECTED) { + nrfy_gpio_cfg_default(pin[j]); + } + } +#endif + +#if defined(NRF_DPPIC) + /* Clear all SUBSCRIBE configurations. */ + memset((uint8_t *)current + NRF_UARTE_SUBSCRIBE_CONF_OFFS, 0, + NRF_UARTE_SUBSCRIBE_CONF_SIZE); + /* Clear all PUBLISH configurations. */ + memset((uint8_t *)current + NRF_UARTE_PUBLISH_CONF_OFFS, 0, + NRF_UARTE_PUBLISH_CONF_SIZE); +#endif + } +#endif + +#if defined(NRF_PPI) + nrf_ppi_channels_disable_all(NRF_PPI); +#endif +#if defined(NRF_DPPIC) + nrf_dppi_channels_disable_all(NRF_DPPIC); +#endif + +#if defined(CONFIG_NRFX_CLOCK) + nrf_cleanup_clock(); +#endif +} diff --git a/boot/zephyr/nrf_protect.h b/boot/zephyr/nrf_protect.h new file mode 100644 index 000000000..b22ce123d --- /dev/null +++ b/boot/zephyr/nrf_protect.h @@ -0,0 +1,42 @@ +/* Copyright (c) 5020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef NRF_PROTECT_H__ +#define NRF_PROTECT_H__ + +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT + +#include +#include + +#ifdef PM_S1_ADDRESS +/* MCUBoot is stored in either S0 or S1, protect both */ +#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS) +#define PROTECT_ADDR PM_S0_ADDRESS +#else +/* There is only one instance of MCUBoot */ +#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS) +#define PROTECT_ADDR PM_MCUBOOT_ADDRESS +#endif + +#ifdef CONFIG_SOC_SERIES_NRF54LX +#if defined(CONFIG_FPROTECT_ALLOW_COMBINED_REGIONS) +#define REGION_SIZE_MAX (62 *1024) +#if (PROTECT_ADDR != 0) +#error "FPROTECT with combined regions can only be used to protect from address 0" +#endif +#else +#define REGION_SIZE_MAX (31 *1024) +#endif + +#if (PROTECT_SIZE > REGION_SIZE_MAX) +#error "FPROTECT size too large" +#endif + +#endif /* CONFIG_SOC_SERIES_NRF54LX */ + +#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */ + +#endif /* NRF_PROTECT_H__ */ diff --git a/boot/zephyr/pm.yml b/boot/zephyr/pm.yml new file mode 100644 index 000000000..eec62473c --- /dev/null +++ b/boot/zephyr/pm.yml @@ -0,0 +1,94 @@ +#include + +mcuboot: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT + placement: + before: [mcuboot_primary] + align: {end: 0x1000} + +mcuboot_primary_app: + # All images to be placed in MCUboot's slot 0 should be placed in this + # partition + span: [app] + +mcuboot_primary: + span: [mcuboot_pad, mcuboot_primary_app] + +# Partition for secondary slot is not created if building in single application +# slot configuration. +#if !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_BOOT_DIRECT_XIP) +mcuboot_secondary: + share_size: [mcuboot_primary] +#if defined(CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY) + region: external_flash + placement: + align: {start: 4} +#else + placement: + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + align_next: CONFIG_FPROTECT_BLOCK_SIZE # Ensure that the next partition does not interfere with this image + after: mcuboot_primary +#endif /* CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY */ + +#endif /* !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_BOOT_DIRECT_XIP) */ + +#if CONFIG_BOOT_DIRECT_XIP + +# Direct XIP is enabled, reserve area for metadata (padding) and name the +# partition so that its clear that it is not the secondary slot, but the direct +# XIP alternative. + +mcuboot_secondary_pad: + share_size: mcuboot_pad + placement: + after: mcuboot_primary + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + +mcuboot_secondary_app: + share_size: mcuboot_primary_app + placement: + after: mcuboot_secondary_pad + +mcuboot_secondary: + span: [mcuboot_secondary_pad, mcuboot_secondary_app] + +#endif /* CONFIG_BOOT_DIRECT_XIP */ + +#if CONFIG_BOOT_SWAP_USING_SCRATCH +mcuboot_scratch: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT_SCRATCH + placement: + after: app + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} +#endif /* CONFIG_BOOT_SWAP_USING_SCRATCH */ + +# Padding placed before image to boot. This reserves space for the MCUboot image header +# and it ensures that the boot image gets linked with the correct address offset in flash. +mcuboot_pad: + # MCUboot pad must be placed before the primary application partition. + # The primary application partition includes the secure firmware if present. + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT_PAD + placement: + before: [mcuboot_primary_app] +#ifdef CONFIG_FPROTECT + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} +#endif + +#if (CONFIG_NRF53_MCUBOOT_PRIMARY_1_RAM_FLASH) +mcuboot_primary_1: + region: ram_flash + size: CONFIG_NRF53_RAM_FLASH_SIZE +#endif /* CONFIG_NRF53_MCUBOOT_PRIMARY_1_RAM_FLASH */ + +#if (CONFIG_NRF53_MULTI_IMAGE_UPDATE) +mcuboot_secondary_1: +#if defined(CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY) + region: external_flash +#else + placement: + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + after: mcuboot_secondary +#endif + size: CONFIG_NRF53_RAM_FLASH_SIZE + +#endif /* CONFIG_NRF53_MULTI_IMAGE_UPDATE */ diff --git a/boot/zephyr/prj.conf b/boot/zephyr/prj.conf index b824ca1b1..df37b82fb 100644 --- a/boot/zephyr/prj.conf +++ b/boot/zephyr/prj.conf @@ -29,3 +29,6 @@ CONFIG_CBPRINTF_NANO=y CONFIG_PICOLIBC=y ### Disable malloc arena because we don't need it CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=0 + +# NCS boot banner +CONFIG_NCS_APPLICATION_BOOT_BANNER_STRING="MCUboot" diff --git a/boot/zephyr/socs/nrf54h20_cpuapp.conf b/boot/zephyr/socs/nrf54h20_cpuapp.conf new file mode 100644 index 000000000..2a6df8b12 --- /dev/null +++ b/boot/zephyr/socs/nrf54h20_cpuapp.conf @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Configuration which is needed for supporting resume the application execution +# after suspend to RAM (S2RAM) requested by the application. +# MCUboot does not support S2RAM itself, but serves as an immediate actor while waking up +# from suspension. +CONFIG_SOC_EARLY_RESET_HOOK=y diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l05_cpuapp.conf b/boot/zephyr/socs/nrf54l05_cpuapp.conf similarity index 79% rename from boot/zephyr/boards/nrf54l15dk_nrf54l05_cpuapp.conf rename to boot/zephyr/socs/nrf54l05_cpuapp.conf index c8fcd32c3..645325513 100644 --- a/boot/zephyr/boards/nrf54l15dk_nrf54l05_cpuapp.conf +++ b/boot/zephyr/socs/nrf54l05_cpuapp.conf @@ -11,3 +11,7 @@ CONFIG_BOOT_WATCHDOG_FEED=n # Ensure the fastest RRAM write operations CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 + +# Link Time Optimizations +CONFIG_ISR_TABLES_LOCAL_DECLARATION=y +CONFIG_LTO=y diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l10_cpuapp.conf b/boot/zephyr/socs/nrf54l10_cpuapp.conf similarity index 79% rename from boot/zephyr/boards/nrf54l15dk_nrf54l10_cpuapp.conf rename to boot/zephyr/socs/nrf54l10_cpuapp.conf index c8fcd32c3..645325513 100644 --- a/boot/zephyr/boards/nrf54l15dk_nrf54l10_cpuapp.conf +++ b/boot/zephyr/socs/nrf54l10_cpuapp.conf @@ -11,3 +11,7 @@ CONFIG_BOOT_WATCHDOG_FEED=n # Ensure the fastest RRAM write operations CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 + +# Link Time Optimizations +CONFIG_ISR_TABLES_LOCAL_DECLARATION=y +CONFIG_LTO=y diff --git a/boot/zephyr/socs/nrf54l15_cpuapp.conf b/boot/zephyr/socs/nrf54l15_cpuapp.conf index 8db9d2d23..645325513 100644 --- a/boot/zephyr/socs/nrf54l15_cpuapp.conf +++ b/boot/zephyr/socs/nrf54l15_cpuapp.conf @@ -1,3 +1,17 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Ensure that the SPI NOR driver is disabled by default +CONFIG_SPI_NOR=n + +CONFIG_BOOT_WATCHDOG_FEED=n + +# Ensure the fastest RRAM write operations +CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 + # Link Time Optimizations CONFIG_ISR_TABLES_LOCAL_DECLARATION=y CONFIG_LTO=y diff --git a/boot/zephyr/sysbuild/CMakeLists.txt b/boot/zephyr/sysbuild/CMakeLists.txt index 5a2a881b7..555136a4d 100644 --- a/boot/zephyr/sysbuild/CMakeLists.txt +++ b/boot/zephyr/sysbuild/CMakeLists.txt @@ -25,19 +25,35 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_post_image_cmake) return() endif() - foreach(image ${IMAGES}) - set(app_type) - get_property(app_type TARGET ${image} PROPERTY APP_TYPE) - - if("${app_type}" STREQUAL "MAIN") - sysbuild_get(mcuboot_image_footer_size IMAGE mcuboot CACHE) - sysbuild_get(mcuboot_image_upgrade_footer_size IMAGE mcuboot CACHE) - math(EXPR mcuboot_image_footer_size "${mcuboot_image_footer_size}" OUTPUT_FORMAT HEXADECIMAL) - math(EXPR mcuboot_image_upgrade_footer_size "${mcuboot_image_upgrade_footer_size}" OUTPUT_FORMAT HEXADECIMAL) - - set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "CONFIG_ROM_END_OFFSET=${mcuboot_image_footer_size}\n") - set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE=${mcuboot_image_upgrade_footer_size}\n") - return() + set(auto_images) + if("${SB_CONFIG_MCUBOOT_IMAGES_ROM_END_OFFSET_AUTO}" STREQUAL "") + UpdateableImage_Get(auto_images ALL) + + # If the list of updateable images is empty, look for the "MAIN" image type. + list(LENGTH auto_images num_auto_images) + if(num_auto_images EQUAL 0) + foreach(image ${IMAGES}) + set(app_type) + get_property(app_type TARGET ${image} PROPERTY APP_TYPE) + + if("${app_type}" STREQUAL "MAIN") + list(APPEND auto_images ${image}) + endif() + endforeach() endif() + else() + set(auto_images "${SB_CONFIG_MCUBOOT_IMAGES_ROM_END_OFFSET_AUTO}") + endif() + + foreach(image ${auto_images}) + set(mcuboot_image_footer_size) + set(mcuboot_image_upgrade_footer_size) + sysbuild_get(mcuboot_image_footer_size IMAGE mcuboot CACHE) + sysbuild_get(mcuboot_image_upgrade_footer_size IMAGE mcuboot CACHE) + math(EXPR mcuboot_image_footer_size "${mcuboot_image_footer_size}" OUTPUT_FORMAT HEXADECIMAL) + math(EXPR mcuboot_image_upgrade_footer_size "${mcuboot_image_upgrade_footer_size}" OUTPUT_FORMAT HEXADECIMAL) + + set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "CONFIG_ROM_END_OFFSET=${mcuboot_image_footer_size}\n") + set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE=${mcuboot_image_upgrade_footer_size}\n") endforeach() endfunction(${SYSBUILD_CURRENT_MODULE_NAME}_post_image_cmake) diff --git a/boot/zephyr/uuid/CMakeLists.txt b/boot/zephyr/uuid/CMakeLists.txt new file mode 100644 index 000000000..2f5dde2fc --- /dev/null +++ b/boot/zephyr/uuid/CMakeLists.txt @@ -0,0 +1,74 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +if(CONFIG_NCS_MCUBOOT_UUID_SINGLE_VID) + if(CONFIG_MCUBOOT_UUID_VID OR CONFIG_MCUBOOT_UUID_CID) + zephyr_library_sources( + uuid.c + ) + endif() + + # Generate VID value and raw value definition + if(CONFIG_MCUBOOT_UUID_VID OR CONFIG_MCUBOOT_UUID_CID) + if("${CONFIG_NCS_MCUBOOT_UUID_VID_VALUE}" STREQUAL "" AND CONFIG_MCUBOOT_UUID_VID) + message(WARNING "VID value not set") + return() + endif() + + string(REGEX MATCHALL "^([0-9A-F][0-9A-F]|\-)+$" match_parts "${CONFIG_NCS_MCUBOOT_UUID_VID_VALUE}") + if("${match_parts}" STREQUAL "${CONFIG_NCS_MCUBOOT_UUID_VID_VALUE}") + set(UUID_VID ${match_parts}) + else() + set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8) + string( + UUID UUID_VID + NAMESPACE ${UUID_DNS_NAMESPACE} + NAME ${CONFIG_NCS_MCUBOOT_UUID_VID_VALUE} + TYPE SHA1 UPPER + ) + endif() + + if(CONFIG_MCUBOOT_UUID_VID) + # Convert UUID into C array. + string(REGEX REPLACE "([0-9A-F][0-9A-F])\-?" "0x\\1, " UUID_VID_RAW "${UUID_VID}") + zephyr_compile_definitions(NCS_MCUBOOT_UUID_VID_VALUE=${UUID_VID_RAW}) + endif() + endif() + + # Generate VID value(s) and raw value definition(s) + if(CONFIG_MCUBOOT_UUID_CID) + set(MCUBOOT_IMAGES_COUNT ${CONFIG_UPDATEABLE_IMAGE_NUMBER}) + foreach(image_id RANGE ${MCUBOOT_IMAGES_COUNT}) + if(CONFIG_NCS_MCUBOOT_UUID_CID_IMAGE_${image_id}) + if("${CONFIG_NCS_MCUBOOT_UUID_CID_IMAGE_${image_id}_VALUE}" STREQUAL "") + message(WARNING "CID value not set for image ${image_id}") + return() + endif() + + # Check if RAW UUID format is used + string(REGEX MATCHALL "^([0-9A-F][0-9A-F]|\-)+$" match_parts "${CONFIG_NCS_MCUBOOT_UUID_CID_IMAGE_${image_id}_VALUE}") + if("${match_parts}" STREQUAL "${CONFIG_NCS_MCUBOOT_UUID_CID_IMAGE_${image_id}_VALUE}") + set(UUID_CID_IMAGE_${image_id} ${match_parts}) + elseif(NOT "${UUID_VID}" STREQUAL "") + # If not - generate UUID based on VID and CID values + string( + UUID UUID_CID_IMAGE_${image_id} + NAMESPACE ${UUID_VID} + NAME ${CONFIG_NCS_MCUBOOT_UUID_CID_IMAGE_${image_id}_VALUE} + TYPE SHA1 UPPER + ) + else() + message(WARNING "VID value not set, cannot generate CID for image ${image_id}") + return() + endif() + + # Convert UUID into C array. + string(REGEX REPLACE "([0-9A-F][0-9A-F])\-?" "0x\\1, " UUID_CID_IMAGE_${image_id}_RAW "${UUID_CID_IMAGE_${image_id}}") + zephyr_compile_definitions(NCS_MCUBOOT_UUID_CID_IMAGE_${image_id}_VALUE=${UUID_CID_IMAGE_${image_id}_RAW}) + endif() + endforeach() + endif() +endif() diff --git a/boot/zephyr/uuid/Kconfig b/boot/zephyr/uuid/Kconfig new file mode 100644 index 000000000..b6e4c66c2 --- /dev/null +++ b/boot/zephyr/uuid/Kconfig @@ -0,0 +1,55 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +if MCUBOOT_UUID_VID || MCUBOOT_UUID_CID + +menu "Vendor and image Class UUIDs" + +choice NCS_MCUBOOT_UUID_IMPLEMENTATION + prompt "UUID checks implementation" + default NCS_MCUBOOT_UUID_SINGLE_VID + +config NCS_MCUBOOT_UUID_SINGLE_VID + bool "Single VID and one CID per image" + help + This implementation allows to specify a single, common Vendor UUID + (VID) for all images and a unique Class UUID (CID) for each image. + +endchoice # NCS_MCUBOOT_UUID_IMPLEMENTATION + +if NCS_MCUBOOT_UUID_SINGLE_VID + +config NCS_MCUBOOT_UUID_VID_VALUE + string "Vendor name" + default "" + help + The vendor unique identifier. + The following formats are supported: + - Domain name (i.e. amce.corp) used to generate RFC 9562 UUID5 + identifier. + - Raw UUID (i.e. 12345678-1234-5678-1234-567812345678) + - Raw HEX UUID (i.e. 12345678123456781234567812345678) + +if MCUBOOT_UUID_CID + +image=0 +rsource "Kconfig.uuid.template" +image=1 +rsource "Kconfig.uuid.template" +image=2 +rsource "Kconfig.uuid.template" +image=3 +rsource "Kconfig.uuid.template" +image=4 +rsource "Kconfig.uuid.template" + +endif # MCUBOOT_UUID_CID + +endif # NCS_MCUBOOT_UUID_SINGLE_VID + +endmenu + +endif # MCUBOOT_UUID_VID || MCUBOOT_UUID_CID diff --git a/boot/zephyr/uuid/Kconfig.uuid.template b/boot/zephyr/uuid/Kconfig.uuid.template new file mode 100644 index 000000000..c916935c7 --- /dev/null +++ b/boot/zephyr/uuid/Kconfig.uuid.template @@ -0,0 +1,29 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +if UPDATEABLE_IMAGE_NUMBER > $(image) + +config NCS_MCUBOOT_UUID_CID_IMAGE_$(image)_VALUE + string "Image class name (image $(image))" + default "" + help + The image class unique identifier. + The following formats are supported: + - Image class name (i.e. nRF5340_door_lock_btperipheral). + This format requires NCS_MCUBOOT_UUID_VID_VALUE to be defined + as the VID UUID is used as the namespace for generating RFC 9562 + UUID5 identifier. + - Raw UUID (i.e. 12345678-1234-5678-1234-567812345678) + - Raw HEX UUID (i.e. 12345678123456781234567812345678) + +config NCS_MCUBOOT_UUID_CID_IMAGE_$(image) + bool + default y + depends on NCS_MCUBOOT_UUID_CID_IMAGE_$(image)_VALUE != "" + help + Helper symbol to simplify the active CId list generation. + +endif # UPDATEABLE_IMAGE_NUMBER > $(image) diff --git a/boot/zephyr/uuid/uuid.c b/boot/zephyr/uuid/uuid.c new file mode 100644 index 000000000..28b73bc1f --- /dev/null +++ b/boot/zephyr/uuid/uuid.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +#define IMAGE_ID_COUNT CONFIG_UPDATEABLE_IMAGE_NUMBER +#define CID_INIT(index, label) \ + static const struct image_uuid label = {{ \ + NCS_MCUBOOT_UUID_CID_IMAGE_## index ##_VALUE \ + }} +#define CID_CONFIG(index) UTIL_CAT(CONFIG_NCS_MCUBOOT_UUID_CID_IMAGE_, index) +#define CID_DEFINE(index, prefix) \ + IF_ENABLED(CID_CONFIG(index), (CID_INIT(index, prefix##index))) + +#define CID_CONDITION(index, label) \ + if (image_id == index) { \ + *uuid_cid = &label; \ + FIH_RET(FIH_SUCCESS); \ + } +#define CID_CHECK(index, prefix) \ + IF_ENABLED(CID_CONFIG(index), (CID_CONDITION(index, prefix##index))) + +static fih_ret boot_uuid_compare(const struct image_uuid *uuid1, const struct image_uuid *uuid2) +{ + return fih_ret_encode_zero_equality(memcmp(uuid1->raw, uuid2->raw, + ARRAY_SIZE(uuid1->raw))); +} + +#ifdef CONFIG_MCUBOOT_UUID_CID +LISTIFY(IMAGE_ID_COUNT, CID_DEFINE, (;), uuid_cid_image_); + +static fih_ret boot_uuid_cid_get(uint32_t image_id, const struct image_uuid **uuid_cid) +{ + if (uuid_cid != NULL) { + LISTIFY(IMAGE_ID_COUNT, CID_CHECK, (), uuid_cid_image_) + } + + FIH_RET(FIH_FAILURE); +} +#endif /* CONFIG_MCUBOOT_UUID_CID */ + +fih_ret boot_uuid_init(void) +{ + FIH_RET(FIH_SUCCESS); +} + +#ifdef CONFIG_MCUBOOT_UUID_VID +fih_ret boot_uuid_vid_match(uint32_t image_id, const struct image_uuid *uuid_vid) +{ + const struct image_uuid uuid_vid_c = {{ + NCS_MCUBOOT_UUID_VID_VALUE + }}; + + return boot_uuid_compare(uuid_vid, &uuid_vid_c); +} +#endif /* CONFIG_MCUBOOT_UUID_VID */ + +#ifdef CONFIG_MCUBOOT_UUID_CID +fih_ret boot_uuid_cid_match(uint32_t image_id, const struct image_uuid *uuid_cid) +{ + FIH_DECLARE(ret_code, FIH_FAILURE); + const struct image_uuid *exp_uuid_cid = NULL; + + FIH_CALL(boot_uuid_cid_get, ret_code, image_id, &exp_uuid_cid); + if (FIH_NOT_EQ(ret_code, FIH_SUCCESS) && FIH_NOT_EQ(ret_code, FIH_FAILURE)) { + FIH_RET(FIH_FAILURE); + } + + return boot_uuid_compare(uuid_cid, exp_uuid_cid); +} +#endif /* CONFIG_MCUBOOT_UUID_CID */ diff --git a/ext/nrf/cc310_glue.h b/ext/nrf/cc310_glue.h index ed3ed5c00..22eb94911 100644 --- a/ext/nrf/cc310_glue.h +++ b/ext/nrf/cc310_glue.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include /* diff --git a/zephyr/module.yml b/zephyr/module.yml index 7a80d166f..65af58bb5 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -1,7 +1,8 @@ samples: - boot/zephyr build: - cmake: ./boot/bootutil/zephyr + cmake-ext: True + kconfig-ext: True sysbuild-cmake: boot/zephyr/sysbuild package-managers: pip: