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..17c6a862f 100644 --- a/boot/bootutil/include/bootutil/crypto/ecdsa.h +++ b/boot/bootutil/include/bootutil/crypto/ecdsa.h @@ -34,7 +34,9 @@ #if (defined(MCUBOOT_USE_TINYCRYPT) + \ defined(MCUBOOT_USE_CC310) + \ - defined(MCUBOOT_USE_PSA_OR_MBED_TLS)) != 1 + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ + defined(MCUBOOT_USE_PSA_OR_MBED_TLS) + \ + defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT)) != 1 #error "One crypto backend must be defined: either CC310/TINYCRYPT/MBED_TLS/PSA_CRYPTO" #endif @@ -57,8 +59,12 @@ #define MCUBOOT_ECDSA_NEED_ASN1_SIG #endif /* MCUBOOT_USE_MBED_TLS */ +#if defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT) + #include +#endif /* MCUBOOT_USE_NRF_OBERON_EXPERIMENT */ + /*TODO: remove this after cypress port mbedtls to abstract crypto api */ -#if defined(MCUBOOT_USE_CC310) || defined(MCUBOOT_USE_MBED_TLS) +#if defined(MCUBOOT_USE_CC310) || defined(MCUBOOT_USE_MBED_TLS) || defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT) #define NUM_ECC_BYTES (256 / 8) #endif @@ -72,12 +78,19 @@ #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_NRF_OBERON_EXPERIMENT)) \ + && !defined(MCUBOOT_USE_PSA_CRYPTO) /* * Declaring these like this adds NULL termination. */ @@ -129,8 +142,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 +#if !defined(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 +192,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 /* !defined(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 +263,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 +276,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 +481,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 +500,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 +682,97 @@ 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 */ + +#if defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT) +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) +{ + if (pk == NULL || hash == NULL || sig == NULL) { + return -1; + } + + uint8_t signature[2 * NUM_ECC_BYTES]; + int rc = bootutil_decode_sig(signature, sig, sig + sig_len); + if (rc) { + return -1; + } + + /* Only support uncompressed keys */ + if (pk[0] != 0x04) { + return -1; + } + /* Skip the first byte holding key format */ + pk++; + + rc = ocrypto_ecdsa_p256_verify_hash(signature, hash, pk); + return rc; +} + +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); + return 0; +} +#endif /* MCUBOOT_USE_NRF_OBERON_EXPERIMENT */ + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/crypto/sha.h b/boot/bootutil/include/bootutil/crypto/sha.h index 6a009ff95..6191ac906 100644 --- a/boot/bootutil/include/bootutil/crypto/sha.h +++ b/boot/bootutil/include/bootutil/crypto/sha.h @@ -30,7 +30,9 @@ #if (defined(MCUBOOT_USE_PSA_OR_MBED_TLS) + \ defined(MCUBOOT_USE_TINYCRYPT) + \ - defined(MCUBOOT_USE_CC310)) != 1 + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ + defined(MCUBOOT_USE_CC310) + \ + defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT)) != 1 #error "One crypto backend must be defined: either CC310/MBED_TLS/TINYCRYPT/PSA_CRYPTO" #endif @@ -81,6 +83,10 @@ #include #endif /* MCUBOOT_USE_CC310 */ +#if defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT) + #include +#endif /* MCUBOOT_USE_NRF_OBERON_EXPERIMENT */ + #include #ifdef __cplusplus @@ -270,6 +276,83 @@ 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 */ + +#if defined(MCUBOOT_USE_NRF_OBERON_EXPERIMENT) +typedef ocrypto_sha256_ctx bootutil_sha_context; + +static inline int bootutil_sha_init(bootutil_sha_context *ctx) +{ + if (ctx == NULL) { + return -1; + } + + ocrypto_sha256_init(ctx); + return 0; +} + +static inline int bootutil_sha_drop(bootutil_sha_context *ctx) +{ + /* NOTE: No corresponding function for ocrypto_sha256 */ + (void)ctx; + return 0; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + if (ctx == NULL || data == NULL) { + return -1; + } + + ocrypto_sha256_update(ctx, (const uint8_t *)data, (size_t)data_len); + + return 0; +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + if (ctx == NULL || output == NULL) { + return -1; + } + + ocrypto_sha256_final(ctx, output); + return 0; +} + +#endif /* MCUBOOT_USE_NRF_OBERON_EXPERIMENT */ + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h index fdd473132..3d103f8da 100644 --- a/boot/bootutil/include/bootutil/image.h +++ b/boot/bootutil/include/bootutil/image.h @@ -146,10 +146,6 @@ extern "C" { */ #define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */ -#define VERSION_DEP_SLOT_ACTIVE 0x00 /* Check dependency against active slot. */ -#define VERSION_DEP_SLOT_PRIMARY 0x01 /* Check dependency against primary slot. */ -#define VERSION_DEP_SLOT_SECONDARY 0x02 /* Check dependency against secondary slot. */ - STRUCT_PACKED image_version { uint8_t iv_major; uint8_t iv_minor; @@ -159,11 +155,7 @@ STRUCT_PACKED image_version { struct image_dependency { uint8_t image_id; /* Image index (from 0) */ -#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER - uint8_t slot; /* Image slot */ -#else uint8_t _pad1; -#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */ uint16_t _pad2; struct image_version image_min_version; /* Indicates at minimum which * version of firmware must be 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..7365eac5b 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 @@ -393,9 +398,59 @@ boot_write_image_ok(const struct flash_area *fap) return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET); } +#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; + + for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { + 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; + } + } + + /* 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 /* SEND_BOOT_REQUEST || !MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP */ + int boot_read_image_ok(const struct flash_area *fap, uint8_t *image_ok) { +#ifdef SEND_BOOT_REQUEST + enum boot_slot slot_id = BOOT_SLOT_NONE; + int image_id = flash_area_to_image_slot(fap, &slot_id); + bool confirm_pending = boot_request_check_confirmed_slot(image_id, slot_id); + + if (confirm_pending) { + BOOT_LOG_DBG("Image confirmation pending for image %d slot %d", image_id, slot_id); + *image_ok = BOOT_FLAG_SET; + return 0; + } +#endif /* SEND_BOOT_REQUEST */ return boot_read_flag(fap, image_ok, boot_image_ok_off(fap)); } @@ -503,32 +558,50 @@ 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 - -static int flash_area_to_image(const struct flash_area *fa) +#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) { -#if BOOT_IMAGE_NUMBER > 1 - uint8_t i = 0; - int id = flash_area_get_id(fa); + int rc = BOOT_EBADIMAGE; - while (i < BOOT_IMAGE_NUMBER) { - if (FLASH_AREA_IMAGE_PRIMARY(i) == id || (FLASH_AREA_IMAGE_SECONDARY(i) == id)) { - return i; + /* 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; } - - ++i; } -#else - (void)fa; -#endif - return 0; + + return rc; } +#endif /* SEND_BOOT_REQUEST */ +#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 +612,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 +652,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 +680,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 +702,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..87097f36c 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,48 @@ 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 +#define VALIDATED_WITH_UNINITIALIZED INT32_MAX +static int32_t validated_with = VALIDATED_WITH_UNINITIALIZED; +#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 +117,105 @@ 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) + if(i < validated_with) { + validated_with = 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 == VALIDATED_WITH_UNINITIALIZED) { + 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 ( 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_DBG("Key invalidation failed with: %d", status); + } + } +out: + return ret; +} +#endif /* CONFIG_BOOT_KMU_KEYS_REVOCATION */ + +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +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 + +#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..faffab03a 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 { @@ -317,24 +391,6 @@ boot_verify_slot_dependency(struct boot_loader_state *state, uint8_t swap_type = state->swap_type[dep->image_id]; dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SLOT_SECONDARY : BOOT_SLOT_PRIMARY; -#elif defined(MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER) - switch(dep->slot) { - case VERSION_DEP_SLOT_ACTIVE: - dep_slot = state->slot_usage[dep->image_id].active_slot; - break; - case VERSION_DEP_SLOT_PRIMARY: - dep_slot = BOOT_SLOT_PRIMARY; - break; - case VERSION_DEP_SLOT_SECONDARY: - dep_slot = BOOT_SLOT_SECONDARY; - break; - default: - return -1; - } - - if (!state->slot_usage[dep->image_id].slot_available[dep_slot]) { - return -1; - } #else dep_slot = state->slot_usage[dep->image_id].active_slot; #endif @@ -372,27 +428,7 @@ boot_verify_slot_dependency(struct boot_loader_state *state, } #endif -#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER - if (rc == 0) { - switch(dep->slot) { - case VERSION_DEP_SLOT_PRIMARY: - state->slot_usage[dep->image_id].slot_available[BOOT_SLOT_PRIMARY] = true; - state->slot_usage[dep->image_id].slot_available[BOOT_SLOT_SECONDARY] = false; - state->slot_usage[dep->image_id].active_slot = BOOT_SLOT_PRIMARY; - break; - case VERSION_DEP_SLOT_SECONDARY: - state->slot_usage[dep->image_id].slot_available[BOOT_SLOT_PRIMARY] = false; - state->slot_usage[dep->image_id].slot_available[BOOT_SLOT_SECONDARY] = true; - state->slot_usage[dep->image_id].active_slot = BOOT_SLOT_SECONDARY; - break; - case VERSION_DEP_SLOT_ACTIVE: - default: - break; - } - } -#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */ - -return rc; + return rc; } #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) @@ -423,7 +459,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 +468,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; } @@ -537,19 +576,6 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) goto done; } -#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER - /* Validate against possible dependency slot values. */ - switch(dep->slot) { - case VERSION_DEP_SLOT_ACTIVE: - case VERSION_DEP_SLOT_PRIMARY: - case VERSION_DEP_SLOT_SECONDARY: - break; - default: - rc = BOOT_EBADARGS; - goto done; - } -#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */ - /* Verify dependency and modify the swap type if not satisfied. */ rc = boot_verify_slot_dependency(state, &dep); if (rc != 0) { @@ -695,9 +721,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 +843,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 +1015,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 +1094,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 +1109,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 +1193,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 +1246,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 +1285,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 +1402,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 +1496,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 +1751,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 +1779,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 +2153,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 +2614,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 +2740,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 +2842,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 +2885,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 +2925,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 +2955,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 @@ -2725,124 +3230,6 @@ boot_select_or_erase(struct boot_loader_state *state) } #endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */ -#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER -/** - * Tries to load a slot for all the images with validation. - * - * @param state Boot loader status information. - * - * @return 0 on success; nonzero on failure. - */ -fih_ret -boot_load_and_validate_images(struct boot_loader_state *state) -{ - uint32_t active_slot; - int rc; - fih_ret fih_rc; - uint32_t slot; - - /* Go over all the images and all slots and validate them */ - IMAGES_ITER(BOOT_CURR_IMG(state)) { - for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { -#if BOOT_IMAGE_NUMBER > 1 - if (state->img_mask[BOOT_CURR_IMG(state)]) { - continue; - } -#endif - - /* Save the number of the active slot. */ - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = slot; - -#ifdef MCUBOOT_DIRECT_XIP - rc = boot_rom_address_check(state); - if (rc != 0) { - /* The image is placed in an unsuitable slot. */ - state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false; - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; - continue; - } - -#ifdef MCUBOOT_DIRECT_XIP_REVERT - rc = boot_select_or_erase(state); - if (rc != 0) { - /* The selected image slot has been erased. */ - state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false; - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; - continue; - } -#endif /* MCUBOOT_DIRECT_XIP_REVERT */ -#endif /* MCUBOOT_DIRECT_XIP */ - -#ifdef MCUBOOT_RAM_LOAD - /* Image is first loaded to RAM and authenticated there in order to - * prevent TOCTOU attack during image copy. This could be applied - * when loading images from external (untrusted) flash to internal - * (trusted) RAM and image is authenticated before copying. - */ - rc = boot_load_image_to_sram(state); - if (rc != 0 ) { - /* Image cannot be ramloaded. */ - boot_remove_image_from_flash(state, slot); - state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false; - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; - continue; - } -#endif /* MCUBOOT_RAM_LOAD */ - - FIH_CALL(boot_validate_slot, fih_rc, state, slot, NULL, 0); - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { - /* Image is invalid. */ -#ifdef MCUBOOT_RAM_LOAD - boot_remove_image_from_sram(state); -#endif /* MCUBOOT_RAM_LOAD */ - state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false; - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; - continue; - } - - /* Valid image loaded from a slot, go to the next slot. */ - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; - } - } - - /* Go over all the images and all slots and validate them */ - IMAGES_ITER(BOOT_CURR_IMG(state)) { - /* All slots tried until a valid image found. Breaking from this loop - * means that a valid image found or already loaded. If no slot is - * found the function returns with error code. */ - while (true) { - /* Go over all the slots and try to load one */ - active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; - if (active_slot != BOOT_SLOT_NONE){ - /* A slot is already active, go to next image. */ - break; - } - - rc = BOOT_HOOK_FIND_SLOT_CALL(boot_find_next_slot_hook, BOOT_HOOK_REGULAR, - state, BOOT_CURR_IMG(state), &active_slot); - if (rc == BOOT_HOOK_REGULAR) { - active_slot = find_slot_with_highest_version(state); - } - - if (active_slot == BOOT_SLOT_NONE) { - BOOT_LOG_INF("No slot to load for image %d", - BOOT_CURR_IMG(state)); - FIH_RET(FIH_FAILURE); - } - - /* Save the number of the active slot. */ - state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot; - - /* Valid image loaded from a slot, go to the next image. */ - break; - } - } - - FIH_RET(FIH_SUCCESS); -} - -#else /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */ - /** * Tries to load a slot for all the images with validation. * @@ -2945,7 +3332,6 @@ boot_load_and_validate_images(struct boot_loader_state *state) FIH_RET(FIH_SUCCESS); } -#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */ /** * Updates the security counter for the current image. @@ -3048,6 +3434,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..968bab2d4 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,12 @@ if(CONFIG_BOOT_USE_PSA_CRYPTO) ) endif() -if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO) +if(CONFIG_BOOT_USE_NRF_OBERON_EXPERIMENT) + target_include_directories(MCUBOOT_BOOTUTIL INTERFACE ${OBERON_BASE}/include) + zephyr_link_libraries(nrfxlib_crypto) +endif() + +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..9cde6da04 --- /dev/null +++ b/boot/bootutil/zephyr/src/boot_request_retention.c @@ -0,0 +1,344 @@ +/* + * 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)); +} + +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; +} + +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..524e9c8b0 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 @@ -51,6 +54,12 @@ config BOOT_USE_CC310 config BOOT_USE_NRF_CC310_BL bool +config BOOT_USE_NRF_OBERON_EXPERIMENT + bool + default n + help + Use nrf oberon. + config NRFXLIB_CRYPTO bool @@ -85,10 +94,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 +124,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 +191,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 +249,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 +268,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,8 +289,13 @@ 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_NRF_OBERON_EXPERIMENT + bool "Use nrf oberon" + select BOOT_USE_NRF_OBERON_EXPERIMENT + config BOOT_ECDSA_TINYCRYPT bool "Use tinycrypt" select BOOT_USE_TINYCRYPT @@ -293,11 +310,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 +347,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 +368,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 +407,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 +482,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 +506,19 @@ config MCUBOOT_CLEANUP_RAM help Sets contents of memory to 0 before jumping to application. +config NCS_MCUBOOT_DISABLE_SELF_RWX_SUPPORTED + bool + default y if SOC_NRF54L15_CPUAPP || SOC_NRF54L10_CPUAPP || SOC_NRF54L05_CPUAPP + default y if SOC_NRF54LV10A_ENGA_CPUAPP + default y if SOC_NRF54LM20A_ENGA_CPUAPP + +config NCS_MCUBOOT_DISABLE_SELF_RWX + bool "Disable read and execution on self NVM" + depends on NCS_MCUBOOT_DISABLE_SELF_RWX_SUPPORTED && !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 +526,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 +811,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 +856,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 +1035,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 +1048,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. @@ -1016,15 +1119,6 @@ config BOOT_VERSION_CMP_USE_BUILD_NUMBER minor and revision. Enable this option to take into account the build number as well. -config BOOT_VERSION_CMP_USE_SLOT_NUMBER - bool "Use slot number while comparing image version" - depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \ - BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION - help - By default, the image slot comparison relies only on active slot. - Enable this option to take into account the specified slot number - instead. - choice BOOT_DOWNGRADE_PREVENTION_CHOICE prompt "Downgrade prevention" optional @@ -1061,6 +1155,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 +1178,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 +1252,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 +1281,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 +1302,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 +1412,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 +1434,21 @@ 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. + 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/nrf54h20dk_nrf54h20_cpuapp.overlay b/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp.overlay deleted file mode 100644 index 15156e5ea..000000000 --- a/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/ { - chosen { - zephyr,code-partition = &boot_partition; - }; -}; - -&gdpwr { - status = "disabled"; -}; - -&gdpwr_fast_active_0 { - status = "disabled"; -}; - -&gdpwr_fast_active_1 { - status = "disabled"; -}; - -&gdpwr_fast_main { - status = "disabled"; -}; - -&gdpwr_slow_active { - status = "disabled"; -}; - -&gdpwr_slow_main { - status = "disabled"; -}; - -&gpio_pad_group0 { - status = "disabled"; -}; - -&gpio_pad_group1 { - status = "disabled"; -}; - -&gpio_pad_group2 { - status = "disabled"; -}; - -&gpio_pad_group6 { - status = "disabled"; -}; - -&gpio_pad_group7 { - status = "disabled"; -}; - -&gpio_pad_group9 { - status = "disabled"; -}; diff --git a/boot/zephyr/boards/nrf54l15dk_ext_flash.conf b/boot/zephyr/boards/nrf54l15dk_ext_flash.conf new file mode 100644 index 000000000..15a9a228d --- /dev/null +++ b/boot/zephyr/boards/nrf54l15dk_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_ext_flash.overlay b/boot/zephyr/boards/nrf54l15dk_ext_flash.overlay new file mode 100644 index 000000000..410388bfa --- /dev/null +++ b/boot/zephyr/boards/nrf54l15dk_ext_flash.overlay @@ -0,0 +1,9 @@ +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + }; +}; + +&mx25r64 { + status = "okay"; +}; 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..e8f43a9d1 --- /dev/null +++ b/boot/zephyr/firmware_loader_bm.c @@ -0,0 +1,293 @@ +/* + * 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 "bootutil/key_revocation.h" + +#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 defined(CONFIG_BOOT_KEYS_REVOCATION) + if (softdevice_image_valid == true && firmware_loader_image_valid == true) { + allow_revoke(); + if (revoke() != BOOT_KEY_REVOKE_OK) { + return -1; + } + } +#endif /*CONFIG_BOOT_KEYS_REVOCATION*/ + + 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..1e445e24f 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -49,6 +49,10 @@ #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 +#elif defined(CONFIG_BOOT_USE_NRF_OBERON_EXPERIMENT) +#define MCUBOOT_USE_NRF_OBERON_EXPERIMENT #endif #ifdef CONFIG_BOOT_IMG_HASH_ALG_SHA512 @@ -66,6 +70,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 @@ -124,10 +132,6 @@ #define MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER #endif -#ifdef CONFIG_BOOT_VERSION_CMP_USE_SLOT_NUMBER -#define MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER -#endif - #ifdef CONFIG_BOOT_SWAP_SAVE_ENCTLV #define MCUBOOT_SWAP_SAVE_ENCTLV 1 #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..2e37c6aab --- /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..ad36a60a2 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,76 @@ K_SEM_DEFINE(boot_log_sem, 1, 1); * !defined(CONFIG_LOG_MODE_MINIMAL) */ +#if USE_PARTITION_MANAGER +#include +#endif + +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT +#include +#endif + +#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_NUMBER 4 +#define NRF_RRAM_REGION_SIZE_UNIT 0x400 +#define NRF_RRAM_REGION_ADDRESS_RESOLUTION 0x400 + +#if defined(CONFIG_SOC_NRF54L15_CPUAPP) || defined(CONFIG_SOC_NRF54L05_CPUAPP) || \ + defined(CONFIG_SOC_NRF54L10_CPUAPP) +#define MAX_PROTECTED_REGION_SIZE (31 * 1024) +#elif defined(CONFIG_SOC_NRF54LV10A_ENGA_CPUAPP) || defined(CONFIG_SOC_NRF54LM20A_ENGA_CPUAPP) +#define MAX_PROTECTED_REGION_SIZE (127 * 1024) +#elif defined(CONFIG_SOC_NRF54LS05B_ENGA_CPUAPP) +#define MAX_PROTECTED_REGION_SIZE (1023 * 1024) +#endif + +#define RRAMC_REGION_CONFIG NRF_RRAMC->REGION[RRAMC_REGION_NUMBER].CONFIG +#define RRAMC_REGION_CONFIG_H (((uint32_t)(&(RRAMC_REGION_CONFIG))) >> 16) +#define RRAMC_REGION_CONFIG_L (((uint32_t)(&(RRAMC_REGION_CONFIG))) & 0x0000fffful) + +#define RRAMC_REGION_ADDRESS NRF_RRAMC->REGION[RRAMC_REGION_NUMBER].ADDRESS +#define RRAMC_REGION_ADDRESS_H (((uint32_t)(&(RRAMC_REGION_ADDRESS))) >> 16) +#define RRAMC_REGION_ADDRESS_L (((uint32_t)(&(RRAMC_REGION_ADDRESS))) & 0x0000fffful) + +#if (CONFIG_NCS_IS_VARIANT_IMAGE) +#define PROTECTED_REGION_START PM_S1_IMAGE_ADDRESS +#define PROTECTED_REGION_SIZE PM_S1_IMAGE_SIZE +#else +#define PROTECTED_REGION_START PM_MCUBOOT_ADDRESS +#define PROTECTED_REGION_SIZE PM_MCUBOOT_SIZE +#endif + +BUILD_ASSERT((PROTECTED_REGION_START % NRF_RRAM_REGION_ADDRESS_RESOLUTION) == 0, + "Start of protected region is not aligned - not possible to protect"); + +BUILD_ASSERT((PROTECTED_REGION_SIZE % NRF_RRAM_REGION_SIZE_UNIT) == 0, + "Size of protected region is not aligned - not possible to protect"); + +BUILD_ASSERT(PROTECTED_REGION_SIZE <= MAX_PROTECTED_REGION_SIZE, + "Size of protected region is too big for protection"); + +#define PROTECTED_REGION_START_H ((PROTECTED_REGION_START) >> 16) +#define PROTECTED_REGION_START_L ((PROTECTED_REGION_START) & 0x0000fffful) + +#endif /* CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX */ + BOOT_LOG_MODULE_REGISTER(mcuboot); void os_heap_init(void); @@ -148,6 +246,97 @@ 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, the region has not been configured yet. + * Set the size and address to the partition size and address. + */ + " ands r4, r2, %12\n" + " cbnz r4, clear_rwx\n" + /* Set the size of the protected region */ + " movt r2, %8\n" + /* Set the address of the protected region */ + " movw r5, %13\n" + " movt r5, %14\n" + " movw r6, %15\n" + " movt r6, %16\n" + " str r6, [r5]\n" + " dsb\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_CONFIG_L), + "i" (RRAMC_REGION_CONFIG_H), + "i" ((PROTECTED_REGION_SIZE) / (NRF_RRAM_REGION_SIZE_UNIT)), + "i" (RRAMC_REGION_RWX_LSB), + "i" (RRAMC_REGION_RWX_WIDTH), + "i" (RRAMC_REGION_CONFIG_LOCK_Msk), + "i" (RRAMC_REGION_CONFIG_SIZE_Msk), + "i" (RRAMC_REGION_ADDRESS_L), + "i" (RRAMC_REGION_ADDRESS_H), + "i" (PROTECTED_REGION_START_L), + "i" (PROTECTED_REGION_START_H) +#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 +373,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 +468,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 +667,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 +737,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 +757,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 +824,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 +894,44 @@ 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 + +#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 + + 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/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/docs/design.md b/docs/design.md index f31c2d452..595c6a401 100755 --- a/docs/design.md +++ b/docs/design.md @@ -944,23 +944,6 @@ process is presented below. + Boot into image in the primary slot of the 0th image position\ (other image in the boot chain is started by another image). -By enabling the `MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER` configuration option, -the dependency check may be extended to match for a specified slot of a specific -image. This functionality is useful in a multi-core system when Direct XIP mode -is used. -In this case, the main image can be started from one of the two (primary or -secondary) slots. -If there is a fixed connection between the slots of two different images, -e.g. if the main image always chainloads a companion image from the same slot, -the check must take this into account and only consider a matching slot when -resolving dependencies. - -There are three values that can be passed when specifying dependencies: - -1. ``active``: the dependency should be checked against either primary or secondary slot. -2. ``primary``: the dependency should be checked only against primary slot. -3. ``secondary``: the dependency should be checked only against secondary slot. - ### [Multiple image boot for RAM loading and direct-xip](#multiple-image-boot-for-ram-loading-and-direct-xip) The operation of the bootloader is different when the ram-load or the diff --git a/docs/imgtool.md b/docs/imgtool.md index c68652dca..958e1af15 100644 --- a/docs/imgtool.md +++ b/docs/imgtool.md @@ -91,8 +91,7 @@ primary slot and adds a header and trailer that the bootloader is expecting: the `auto` keyword to automatically generate it from the image version. -d, --dependencies TEXT Add dependence on another image, format: - "(,[,] - ), ... " + "(,), ... " --pad-sig Add 0-2 bytes of padding to ECDSA signature (for mcuboot <1.5) -H, --header-size INTEGER [required] @@ -183,16 +182,6 @@ which the current image depends on. The `image_version` is the minimum version of that image to satisfy compliance. For example `-d "(1, 1.2.3+0)"` means this image depends on Image 1 which version has to be at least 1.2.3+0. -In addition, a dependency can specify the slot as follows: -`-d "(image_id, slot, image_version)"`. The `image_id` is the number of the -image on which the current image depends. -The slot specifies which slots of the image are to be taken into account -(`active`: primary or secondary, `primary`: only primary `secondary`: only -secondary slot). The `image_version` is the minimum version of that image to -fulfill the requirements. -For example `-d "(1, primary, 1.2.3+0)"` means that this image depends on the -primary slot of the Image 1, whose version must be at least 1.2.3+0. - The `--public-key-format` argument can be used to distinguish where the public key is stored for image authentication. The `hash` option is used by default, in which case only the hash of the public key is added to the TLV area (the full 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/scripts/assemble.py b/scripts/assemble.py index 05f979379..63603f9ff 100755 --- a/scripts/assemble.py +++ b/scripts/assemble.py @@ -22,27 +22,24 @@ import argparse import errno -import io -import re import os import os.path import pickle +import re import sys + def same_keys(a, b): """Determine if the dicts a and b have the same keys in them""" - for ak in a.keys(): + for ak in a: if ak not in b: return False - for bk in b.keys(): - if bk not in a: - return False - return True + return all(bk in a for bk in b) offset_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_OFFSET(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") size_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_SIZE(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") -class Assembly(): +class Assembly: def __init__(self, output, bootdir, edt): self.find_slots(edt) try: @@ -83,7 +80,7 @@ def find_slots(self, edt): def add_image(self, source, partition): with open(self.output, 'ab') as ofd: pos = ofd.tell() - print("partition {}, pos={}, offset={}".format(partition, pos, self.offsets[partition])) + print(f"partition {partition}, pos={pos}, offset={self.offsets[partition]}") if pos > self.offsets[partition]: raise Exception("Partitions not in order, unsupported") if pos < self.offsets[partition]: @@ -92,16 +89,16 @@ def add_image(self, source, partition): with open(source, 'rb') as rfd: ibuf = rfd.read() if len(ibuf) > self.sizes[partition]: - raise Exception("Image {} is too large for partition".format(source)) + raise Exception(f"Image {source} is too large for partition") ofd.write(ibuf) def find_board_name(bootdir): dot_config = os.path.join(bootdir, "zephyr", ".config") - with open(dot_config, "r") as f: + with open(dot_config) as f: for line in f: if line.startswith("CONFIG_BOARD="): return line.split("=", 1)[1].strip('"') - raise Exception("Expected CONFIG_BOARD line in {}".format(dot_config)) + raise Exception(f"Expected CONFIG_BOARD line in {dot_config}") def main(): parser = argparse.ArgumentParser() @@ -130,7 +127,7 @@ def main(): sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts", "python-devicetree", "src")) import devicetree.edtlib - board = find_board_name(args.bootdir) + find_board_name(args.bootdir) edt_pickle = os.path.join(args.bootdir, "zephyr", "edt.pickle") with open(edt_pickle, 'rb') as f: diff --git a/scripts/imgtool/boot_record.py b/scripts/imgtool/boot_record.py index fb98b2eab..f683fac73 100644 --- a/scripts/imgtool/boot_record.py +++ b/scripts/imgtool/boot_record.py @@ -48,5 +48,5 @@ def create_sw_component_data(sw_type, sw_version, sw_measurement_description, # Note: The measurement value must be the last item of the property # list because later it will be modified by the bootloader. last_key = list(properties.keys())[-1] - assert SwComponent.MEASUREMENT_VALUE == last_key, 'Measurement value is not the last item of the property list' + assert last_key == SwComponent.MEASUREMENT_VALUE, 'Measurement value is not the last item of the property list' return dumps(properties) diff --git a/scripts/imgtool/dumpinfo.py b/scripts/imgtool/dumpinfo.py index 40ece79f1..3866c9a10 100644 --- a/scripts/imgtool/dumpinfo.py +++ b/scripts/imgtool/dumpinfo.py @@ -19,7 +19,6 @@ """ import os.path import struct -import sys import click import yaml @@ -51,7 +50,7 @@ def parse_enc(key_field_len): if key_field_len is not None: - return "(len: {}, if BOOT_SWAP_SAVE_ENCTLV is unset)".format(hex(key_field_len)) + return f"(len: {hex(key_field_len)}, if BOOT_SWAP_SAVE_ENCTLV is unset)" else: return "Image not encrypted" @@ -69,7 +68,7 @@ def parse_status(status_hex): def parse_boot_magic(trailer_magic): magic = "" for i in range(BOOT_MAGIC_SIZE): - magic += "{0:#04x} ".format(trailer_magic[i]) + magic += f"{trailer_magic[i]:#04x} " if i == (BOOT_MAGIC_SIZE / 2 - 1): magic += ("\n" + " ") return magic @@ -103,16 +102,15 @@ def print_tlv_records(tlv_list): tlv_type, tlv_length, tlv_data = tlv.keys() if tlv[tlv_type] in TLV_TYPES: - print(" " * indent, "{}: {} ({})".format( - tlv_type, TLV_TYPES[tlv[tlv_type]], hex(tlv[tlv_type]))) + print(" " * indent, f"{tlv_type}: {TLV_TYPES[tlv[tlv_type]]} ({hex(tlv[tlv_type])})") else: print(" " * indent, "{}: {} ({})".format( tlv_type, "UNKNOWN", hex(tlv[tlv_type]))) - print(" " * indent, "{}: ".format(tlv_length), hex(tlv[tlv_length])) - print(" " * indent, "{}: ".format(tlv_data), end="") + print(" " * indent, f"{tlv_length}: ", hex(tlv[tlv_length])) + print(" " * indent, f"{tlv_data}: ", end="") for j, data in enumerate(tlv[tlv_data]): - print("{0:#04x}".format(data), end=" ") + print(f"{data:#04x}", end=" ") if ((j + 1) % 8 == 0) and ((j + 1) != len(tlv[tlv_data])): print("\n", end=" " * (indent + 7)) print() @@ -133,7 +131,7 @@ def dump_imginfo(imgfile, outfile=None, silent=False): with open(imgfile, "rb") as f: b = f.read() except FileNotFoundError: - raise click.UsageError("Image file not found ({})".format(imgfile)) + raise click.UsageError(f"Image file not found ({imgfile})") # Parsing the image header _header = struct.unpack('IIHHIIBBHI', b[:28]) @@ -264,12 +262,11 @@ def dump_imginfo(imgfile, outfile=None, silent=False): flag_string = hex(value) else: flag_string = "" - for flag in image.IMAGE_F.keys(): + for flag in image.IMAGE_F: if value & image.IMAGE_F[flag]: if flag_string: flag_string += ("\n" + (" " * 20)) - flag_string += "{} ({})".format( - flag, hex(image.IMAGE_F[flag])) + flag_string += f"{flag} ({hex(image.IMAGE_F[flag])})" value = flag_string if not isinstance(value, str): @@ -279,7 +276,7 @@ def dump_imginfo(imgfile, outfile=None, silent=False): # Image payload _sectionoff = header["hdr_size"] - frame_header_text = "Payload (offset: {})".format(hex(_sectionoff)) + frame_header_text = f"Payload (offset: {hex(_sectionoff)})" frame_content = "FW image (size: {} Bytes)".format(hex(header["img_size"])) print_in_frame(frame_header_text, frame_content) @@ -287,7 +284,7 @@ def dump_imginfo(imgfile, outfile=None, silent=False): _sectionoff += header["img_size"] if protected_tlv_size != 0: # Protected TLV area - section_name = "Protected TLV area (offset: {})".format(hex(_sectionoff)) + section_name = f"Protected TLV area (offset: {hex(_sectionoff)})" print_in_row(section_name) print("magic: ", hex(tlv_area["tlv_hdr_prot"]["magic"])) print("area size:", hex(tlv_area["tlv_hdr_prot"]["tlv_tot"])) @@ -295,7 +292,7 @@ def dump_imginfo(imgfile, outfile=None, silent=False): print("#" * _LINE_LENGTH) _sectionoff += protected_tlv_size - section_name = "TLV area (offset: {})".format(hex(_sectionoff)) + section_name = f"TLV area (offset: {hex(_sectionoff)})" print_in_row(section_name) print("magic: ", hex(tlv_area["tlv_hdr"]["magic"])) print("area size:", hex(tlv_area["tlv_hdr"]["tlv_tot"])) @@ -305,8 +302,8 @@ def dump_imginfo(imgfile, outfile=None, silent=False): if _img_pad_size: _sectionoff += tlv_area["tlv_hdr"]["tlv_tot"] _erased_val = b[_sectionoff] - frame_header_text = "Image padding (offset: {})".format(hex(_sectionoff)) - frame_content = "padding ({})".format(hex(_erased_val)) + frame_header_text = f"Image padding (offset: {hex(_sectionoff)})" + frame_content = f"padding ({hex(_erased_val)})" print_in_frame(frame_header_text, frame_content) # Image trailer diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 2f68f9929..017307bab 100755 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -20,19 +20,13 @@ Image signing and management. """ -from . import version as versmod -from .boot_record import create_sw_component_data -import click import copy -from enum import Enum -import array -from intelhex import IntelHex import hashlib -import array import os.path import re import struct import uuid +from collections import namedtuple from enum import Enum import click @@ -46,11 +40,10 @@ from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat from intelhex import IntelHex -from . import version as versmod, keys +from . import keys +from . import version as versmod from .boot_record import create_sw_component_data -from .keys import rsa, ecdsa, x25519 - -from collections import namedtuple +from .keys import ecdsa, rsa, x25519 IMAGE_MAGIC = 0x96f3b83d IMAGE_HEADER_SIZE = 32 @@ -125,7 +118,7 @@ def align_up(num, align): return (num + (align - 1)) & ~(align - 1) -class TLV(): +class TLV: def __init__(self, endian, magic=TLV_INFO_MAGIC): self.magic = magic self.buf = bytearray() @@ -141,9 +134,8 @@ def add(self, kind, payload): e = STRUCT_ENDIAN_DICT[self.endian] if isinstance(kind, int): if not TLV_VENDOR_RES_MIN <= kind <= TLV_VENDOR_RES_MAX: - msg = "Invalid custom TLV type value '0x{:04x}', allowed " \ - "value should be between 0x{:04x} and 0x{:04x}".format( - kind, TLV_VENDOR_RES_MIN, TLV_VENDOR_RES_MAX) + msg = f"Invalid custom TLV type value '0x{kind:04x}', allowed " \ + f"value should be between 0x{TLV_VENDOR_RES_MIN:04x} and 0x{TLV_VENDOR_RES_MAX:04x}" raise click.UsageError(msg) buf = struct.pack(e + 'HH', kind, len(payload)) else: @@ -153,7 +145,7 @@ def add(self, kind, payload): def get(self): if len(self.buf) == 0: - return bytes() + return b"" e = STRUCT_ENDIAN_DICT[self.endian] header = struct.pack(e + 'HH', self.magic, len(self)) return header + bytes(self.buf) @@ -177,7 +169,7 @@ def get(self): def is_sha_tlv(tlv): - return tlv in TLV_SHA_TO_SHA_AND_ALG.keys() + return tlv in TLV_SHA_TO_SHA_AND_ALG def tlv_sha_to_sha(tlv): @@ -224,8 +216,8 @@ def key_and_user_sha_to_alg_and_tlv(key, user_sha, is_pure = False): allowed = allowed_key_ssh[type(key)] except KeyError: - raise click.UsageError("Colud not find allowed hash algorithms for {}" - .format(type(key))) + raise click.UsageError(f"Colud not find allowed hash algorithms for {type(key)}" + ) # Pure enforces auto, and user selection is ignored if user_sha == 'auto' or is_pure: @@ -234,8 +226,8 @@ def key_and_user_sha_to_alg_and_tlv(key, user_sha, is_pure = False): if user_sha in allowed: return USER_SHA_TO_ALG_AND_TLV[user_sha] - raise click.UsageError("Key {} can not be used with --sha {}; allowed sha are one of {}" - .format(key.sig_type(), user_sha, allowed)) + raise click.UsageError(f"Key {key.sig_type()} can not be used with --sha {user_sha}; allowed sha are one of {allowed}" + ) def get_digest(tlv_type, hash_region): @@ -461,9 +453,8 @@ def check_trailer(self): self.save_enctlv, self.enctlv_len) padding = self.slot_size - (len(self.payload) + tsize) if padding < 0: - msg = "Image size (0x{:x}) + trailer (0x{:x}) exceeds " \ - "requested size 0x{:x}".format( - len(self.payload), tsize, self.slot_size) + msg = f"Image size (0x{len(self.payload):x}) + trailer (0x{tsize:x}) exceeds " \ + f"requested size 0x{self.slot_size:x}" raise click.UsageError(msg) def ecies_hkdf(self, enckey, plainkey, hmac_sha_alg): @@ -550,9 +541,8 @@ def create(self, key, public_key_format, enckey, dependencies=None, if sw_type is not None: if len(sw_type) > MAX_SW_TYPE_LENGTH: - msg = "'{}' is too long ({} characters) for sw_type. Its " \ - "maximum allowed length is 12 characters.".format( - sw_type, len(sw_type)) + msg = f"'{sw_type}' is too long ({len(sw_type)} characters) for sw_type. Its " \ + "maximum allowed length is 12 characters." raise click.UsageError(msg) image_version = (str(self.version.major) + '.' @@ -608,11 +598,10 @@ def create(self, key, public_key_format, enckey, dependencies=None, self.payload.extend(pad) compression_flags = 0x0 - if compression_tlvs is not None: - if compression_type in ["lzma2", "lzma2armthumb"]: - compression_flags = IMAGE_F['COMPRESSED_LZMA2'] - if compression_type == "lzma2armthumb": - compression_flags |= IMAGE_F['COMPRESSED_ARM_THUMB'] + if compression_tlvs is not None and compression_type in ["lzma2", "lzma2armthumb"]: + compression_flags = IMAGE_F['COMPRESSED_LZMA2'] + if compression_type == "lzma2armthumb": + compression_flags |= IMAGE_F['COMPRESSED_ARM_THUMB'] # This adds the header to the payload as well if encrypt_keylen == 256: self.add_header(enckey, protected_tlv_size, compression_flags, 256) @@ -638,9 +627,8 @@ def create(self, key, public_key_format, enckey, dependencies=None, if dependencies is not None: for i in range(dependencies_num): payload = struct.pack( - e + 'BB2x' + 'BBHI', + e + 'B3x' + 'BBHI', int(dependencies[DEP_IMAGES_KEY][i]), - dependencies[DEP_VERSIONS_KEY][i].slot, dependencies[DEP_VERSIONS_KEY][i].major, dependencies[DEP_VERSIONS_KEY][i].minor, dependencies[DEP_VERSIONS_KEY][i].revision, @@ -853,8 +841,7 @@ def _trailer_size(self, write_size, max_sectors, overwrite_only, enckey, return self.max_align * 2 + magic_align_size else: if write_size not in set([1, 2, 4, 8, 16, 32]): - raise click.BadParameter("Invalid alignment: {}".format( - write_size)) + raise click.BadParameter(f"Invalid alignment: {write_size}") m = DEFAULT_MAX_SECTORS if max_sectors is None else max_sectors trailer = m * 3 * write_size # status area if enckey is not None: diff --git a/scripts/imgtool/keys/__init__.py b/scripts/imgtool/keys/__init__.py index ed2fed57e..b37337af0 100644 --- a/scripts/imgtool/keys/__init__.py +++ b/scripts/imgtool/keys/__init__.py @@ -21,19 +21,17 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric.rsa import ( - RSAPrivateKey, RSAPublicKey) from cryptography.hazmat.primitives.asymmetric.ec import ( - EllipticCurvePrivateKey, EllipticCurvePublicKey) -from cryptography.hazmat.primitives.asymmetric.ed25519 import ( - Ed25519PrivateKey, Ed25519PublicKey) -from cryptography.hazmat.primitives.asymmetric.x25519 import ( - X25519PrivateKey, X25519PublicKey) + EllipticCurvePrivateKey, + EllipticCurvePublicKey, +) +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey -from .rsa import RSA, RSAPublic, RSAUsageError, RSA_KEY_SIZES -from .ecdsa import (ECDSA256P1, ECDSA256P1Public, - ECDSA384P1, ECDSA384P1Public, ECDSAUsageError) +from .ecdsa import ECDSA256P1, ECDSA384P1, ECDSA256P1Public, ECDSA384P1Public, ECDSAUsageError from .ed25519 import Ed25519, Ed25519Public, Ed25519UsageError +from .rsa import RSA, RSA_KEY_SIZES, RSAPublic, RSAUsageError from .x25519 import X25519, X25519Public, X25519UsageError diff --git a/scripts/imgtool/keys/ecdsa.py b/scripts/imgtool/keys/ecdsa.py index 52357b1d0..2f34dcc5e 100644 --- a/scripts/imgtool/keys/ecdsa.py +++ b/scripts/imgtool/keys/ecdsa.py @@ -4,7 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 import os.path -import hashlib from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization @@ -27,7 +26,7 @@ def __init__(self, key): self.key = key def _unsupported(self, name): - raise ECDSAUsageError("Operation {} requires private key".format(name)) + raise ECDSAUsageError(f"Operation {name} requires private key") def _get_public(self): return self.key diff --git a/scripts/imgtool/keys/ecdsa_test.py b/scripts/imgtool/keys/ecdsa_test.py index 55f6cc489..8a6bb374a 100644 --- a/scripts/imgtool/keys/ecdsa_test.py +++ b/scripts/imgtool/keys/ecdsa_test.py @@ -16,7 +16,8 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) -from imgtool.keys import load, ECDSA256P1, ECDSAUsageError +from imgtool.keys import ECDSA256P1, ECDSAUsageError, load + class EcKeyGeneration(unittest.TestCase): diff --git a/scripts/imgtool/keys/ed25519.py b/scripts/imgtool/keys/ed25519.py index a9959a665..9184a0dd6 100644 --- a/scripts/imgtool/keys/ed25519.py +++ b/scripts/imgtool/keys/ed25519.py @@ -22,7 +22,7 @@ def shortname(self): return "ed25519" def _unsupported(self, name): - raise Ed25519UsageError("Operation {} requires private key".format(name)) + raise Ed25519UsageError(f"Operation {name} requires private key") def _get_public(self): return self.key @@ -87,8 +87,7 @@ def _get_public(self): return self.key.public_key() def get_private_bytes(self, minimal, format): - raise Ed25519UsageError("Operation not supported with {} keys".format( - self.shortname())) + raise Ed25519UsageError(f"Operation not supported with {self.shortname()} keys") def export_private(self, path, passwd=None): """ diff --git a/scripts/imgtool/keys/ed25519_test.py b/scripts/imgtool/keys/ed25519_test.py index ae9ea49c0..9a2d1cb0c 100644 --- a/scripts/imgtool/keys/ed25519_test.py +++ b/scripts/imgtool/keys/ed25519_test.py @@ -12,11 +12,10 @@ import unittest from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives.asymmetric import ed25519 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) -from imgtool.keys import load, Ed25519, Ed25519UsageError +from imgtool.keys import Ed25519, Ed25519UsageError, load class Ed25519KeyGeneration(unittest.TestCase): diff --git a/scripts/imgtool/keys/general.py b/scripts/imgtool/keys/general.py index 4ff700c49..fdaab147f 100644 --- a/scripts/imgtool/keys/general.py +++ b/scripts/imgtool/keys/general.py @@ -2,23 +2,22 @@ # SPDX-License-Identifier: Apache-2.0 -import binascii -import io import os import sys -from cryptography.hazmat.primitives.hashes import Hash, SHA256 + +from cryptography.hazmat.primitives.hashes import SHA256, Hash AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */" -class FileHandler(object): +class FileHandler: def __init__(self, file, *args, **kwargs): self.file_in = file self.args = args self.kwargs = kwargs def __enter__(self): - if isinstance(self.file_in, (str, bytes, os.PathLike)): + if isinstance(self.file_in, str | bytes | os.PathLike): self.file = open(self.file_in, *self.args, **self.kwargs) else: self.file = self.file_in @@ -29,7 +28,7 @@ def __exit__(self, *args): self.file.close() -class KeyClass(object): +class KeyClass: def _emit(self, header, trailer, encoded_bytes, indent, file=sys.stdout, len_format=None): with FileHandler(file, 'w') as file: @@ -45,7 +44,7 @@ def _emit_to_output(self, header, trailer, encoded_bytes, indent, file, print("\n" + indent, end='', file=file) else: print(" ", end='', file=file) - print("0x{:02x},".format(b), end='', file=file) + print(f"0x{b:02x},", end='', file=file) print("\n" + trailer, file=file) if len_format is not None: print(len_format.format(len(encoded_bytes)), file=file) @@ -62,26 +61,26 @@ def _emit_raw(self, encoded_bytes, file): def emit_c_public(self, file=sys.stdout): self._emit( - header="const unsigned char {}_pub_key[] = {{" - .format(self.shortname()), + header=f"const unsigned char {self.shortname()}_pub_key[] = {{" + , trailer="};", encoded_bytes=self.get_public_bytes(), indent=" ", - len_format="const unsigned int {}_pub_key_len = {{}};" - .format(self.shortname()), + len_format=f"const unsigned int {self.shortname()}_pub_key_len = {{}};" + , file=file) def emit_c_public_hash(self, file=sys.stdout): digest = Hash(SHA256()) digest.update(self.get_public_bytes()) self._emit( - header="const unsigned char {}_pub_key_hash[] = {{" - .format(self.shortname()), + header=f"const unsigned char {self.shortname()}_pub_key_hash[] = {{" + , trailer="};", encoded_bytes=digest.finalize(), indent=" ", - len_format="const unsigned int {}_pub_key_hash_len = {{}};" - .format(self.shortname()), + len_format=f"const unsigned int {self.shortname()}_pub_key_hash_len = {{}};" + , file=file) def emit_raw_public(self, file=sys.stdout): @@ -94,8 +93,8 @@ def emit_raw_public_hash(self, file=sys.stdout): def emit_rust_public(self, file=sys.stdout): self._emit( - header="static {}_PUB_KEY: &[u8] = &[" - .format(self.shortname().upper()), + header=f"static {self.shortname().upper()}_PUB_KEY: &[u8] = &[" + , trailer="];", encoded_bytes=self.get_public_bytes(), indent=" ", diff --git a/scripts/imgtool/keys/privatebytes.py b/scripts/imgtool/keys/privatebytes.py index 8027ac858..abb98c18e 100644 --- a/scripts/imgtool/keys/privatebytes.py +++ b/scripts/imgtool/keys/privatebytes.py @@ -3,13 +3,12 @@ from cryptography.hazmat.primitives import serialization -class PrivateBytesMixin(): +class PrivateBytesMixin: def _get_private_bytes(self, minimal, format, exclass): if format is None: format = self._DEFAULT_FORMAT if format not in self._VALID_FORMATS: - raise exclass("{} does not support {}".format( - self.shortname(), format)) + raise exclass(f"{self.shortname()} does not support {format}") return format, self.key.private_bytes( encoding=serialization.Encoding.DER, format=self._VALID_FORMATS[format], diff --git a/scripts/imgtool/keys/rsa.py b/scripts/imgtool/keys/rsa.py index d4793c538..a61a8d746 100644 --- a/scripts/imgtool/keys/rsa.py +++ b/scripts/imgtool/keys/rsa.py @@ -7,13 +7,12 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1 +from cryptography.hazmat.primitives.asymmetric.padding import MGF1, PSS from cryptography.hazmat.primitives.hashes import SHA256 from .general import KeyClass from .privatebytes import PrivateBytesMixin - # Sizes that bootutil will recognize RSA_KEY_SIZES = [2048, 3072] @@ -34,7 +33,7 @@ def shortname(self): return "rsa" def _unsupported(self, name): - raise RSAUsageError("Operation {} requires private key".format(name)) + raise RSAUsageError(f"Operation {name} requires private key") def _get_public(self): return self.key @@ -65,10 +64,10 @@ def export_public(self, path): f.write(pem) def sig_type(self): - return "PKCS1_PSS_RSA{}_SHA256".format(self.key_size()) + return f"PKCS1_PSS_RSA{self.key_size()}_SHA256" def sig_tlv(self): - return"RSA{}".format(self.key_size()) + return f"RSA{self.key_size()}" def sig_len(self): return self.key_size() / 8 @@ -94,8 +93,8 @@ def __init__(self, key): @staticmethod def generate(key_size=2048): if key_size not in RSA_KEY_SIZES: - raise RSAUsageError("Key size {} is not supported by MCUboot" - .format(key_size)) + raise RSAUsageError(f"Key size {key_size} is not supported by MCUboot" + ) pk = rsa.generate_private_key( public_exponent=65537, key_size=key_size, diff --git a/scripts/imgtool/keys/rsa_test.py b/scripts/imgtool/keys/rsa_test.py index 7610106bc..f0b2ecbe1 100644 --- a/scripts/imgtool/keys/rsa_test.py +++ b/scripts/imgtool/keys/rsa_test.py @@ -11,14 +11,14 @@ import unittest from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1 +from cryptography.hazmat.primitives.asymmetric.padding import MGF1, PSS from cryptography.hazmat.primitives.hashes import SHA256 # Setup sys path so 'imgtool' is in it. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) -from imgtool.keys import load, RSA, RSAUsageError +from imgtool.keys import RSA, RSAUsageError, load from imgtool.keys.rsa import RSA_KEY_SIZES diff --git a/scripts/imgtool/keys/x25519.py b/scripts/imgtool/keys/x25519.py index a99cf183e..00ef0d1de 100644 --- a/scripts/imgtool/keys/x25519.py +++ b/scripts/imgtool/keys/x25519.py @@ -4,7 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 -from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import x25519 @@ -24,7 +23,7 @@ def shortname(self): return "x25519" def _unsupported(self, name): - raise X25519UsageError("Operation {} requires private key".format(name)) + raise X25519UsageError(f"Operation {name} requires private key") def _get_public(self): return self.key diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index 646c9e961..c4abf06f1 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -17,22 +17,21 @@ # See the License for the specific language governing permissions and # limitations under the License. +import base64 +import getpass +import lzma import re +import struct +import sys + import click -import getpass + import imgtool.keys as keys -import sys -import struct -import os -import lzma -import hashlib -import base64 -from collections import namedtuple from imgtool import image, imgtool_version -from imgtool.version import decode_version from imgtool.dumpinfo import dump_imginfo -from .keys import ( - RSAUsageError, ECDSAUsageError, Ed25519UsageError, X25519UsageError) +from imgtool.version import decode_version + +from .keys import ECDSAUsageError, Ed25519UsageError, RSAUsageError, X25519UsageError comp_default_dictsize=131072 comp_default_pb=2 @@ -43,17 +42,8 @@ MIN_PYTHON_VERSION = (3, 6) if sys.version_info < MIN_PYTHON_VERSION: - sys.exit("Python %s.%s or newer is required by imgtool." - % MIN_PYTHON_VERSION) + sys.exit("Python {}.{} or newer is required by imgtool.".format(*MIN_PYTHON_VERSION)) -SlottedSemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', - 'build', 'slot']) - -DEPENDENCY_SLOT_VALUES = { - 'active': 0x00, - 'primary': 0x01, - 'secondary': 0x02 -} def gen_rsa2048(keyfile, passwd): keys.RSA.generate().export_private(path=keyfile, passwd=passwd) @@ -241,9 +231,9 @@ def verify(key, imgfile): print("Image was correctly validated") print("Image version: {}.{}.{}+{}".format(*version)) if digest: - print("Image digest: {}".format(digest.hex())) + print(f"Image digest: {digest.hex()}") if signature and digest is None: - print("Image signature over image: {}".format(signature.hex())) + print(f"Image signature over image: {signature.hex()}") return elif ret == image.VerifyResult.INVALID_MAGIC: print("Invalid image magic; is this an MCUboot image?") @@ -256,7 +246,7 @@ def verify(key, imgfile): elif ret == image.VerifyResult.KEY_MISMATCH: print("Key type does not match TLV record") else: - print("Unknown return code: {}".format(ret)) + print(f"Unknown return code: {ret}") sys.exit(1) @@ -278,7 +268,7 @@ def validate_version(ctx, param, value): decode_version(value) return value except ValueError as e: - raise click.BadParameter("{}".format(e)) + raise click.BadParameter(f"{e}") def validate_security_counter(ctx, param, value): @@ -290,16 +280,16 @@ def validate_security_counter(ctx, param, value): return int(value, 0) except ValueError: raise click.BadParameter( - "{} is not a valid integer. Please use code literals " + f"{value} is not a valid integer. Please use code literals " "prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary." - .format(value)) + ) def validate_header_size(ctx, param, value): min_hdr_size = image.IMAGE_HEADER_SIZE if value < min_hdr_size: raise click.BadParameter( - "Minimum value for -H/--header-size is {}".format(min_hdr_size)) + f"Minimum value for -H/--header-size is {min_hdr_size}") return value @@ -309,34 +299,17 @@ def get_dependencies(ctx, param, value): images = re.findall(r"\((\d+)", value) if len(images) == 0: raise click.BadParameter( - "Image dependency format is invalid: {}".format(value)) - raw_versions = re.findall(r",\s*((active|primary|secondary)\s*,)?\s*([0-9.+]+)\)", value) + f"Image dependency format is invalid: {value}") + raw_versions = re.findall(r",\s*([0-9.+]+)\)", value) if len(images) != len(raw_versions): raise click.BadParameter( - '''There's a mismatch between the number of dependency images - and versions in: {}'''.format(value)) + f'''There's a mismatch between the number of dependency images + and versions in: {value}''') for raw_version in raw_versions: try: - decoded_version = decode_version(raw_version[2]) - if len(raw_version[1]) > 0: - slotted_version = SlottedSemiSemVersion( - decoded_version.major, - decoded_version.minor, - decoded_version.revision, - decoded_version.build, - DEPENDENCY_SLOT_VALUES[raw_version[1]] - ) - else: - slotted_version = SlottedSemiSemVersion( - decoded_version.major, - decoded_version.minor, - decoded_version.revision, - decoded_version.build, - 0 - ) + versions.append(decode_version(raw_version)) except ValueError as e: - raise click.BadParameter("{}".format(e)) - versions.append(slotted_version) + raise click.BadParameter(f"{e}") dependencies = dict() dependencies[image.DEP_IMAGES_KEY] = images dependencies[image.DEP_VERSIONS_KEY] = versions @@ -358,9 +331,8 @@ def convert(self, value, param, ctx): try: return int(value, 0) except ValueError: - self.fail('%s is not a valid integer. Please use code literals ' - 'prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary.' - % value, param, ctx) + self.fail(f'{value} is not a valid integer. Please use code literals ' + 'prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary.', param, ctx) @click.argument('outfile') @@ -431,7 +403,7 @@ def convert(self, value, param, ctx): '(for mcuboot <1.5)') @click.option('-d', '--dependencies', callback=get_dependencies, required=False, help='''Add dependence on another image, format: - "(,[,]), ... "''') + "(,), ... "''') @click.option('-s', '--security-counter', callback=validate_security_counter, help='Specify the value of security counter. Use the `auto` ' 'keyword to automatically generate it from the image version.') @@ -501,16 +473,15 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, img.load(infile) key = load_key(key) if key else None enckey = load_key(encrypt) if encrypt else None - if enckey and key: - if ((isinstance(key, keys.ECDSA256P1) and - not isinstance(enckey, keys.ECDSA256P1Public)) - or (isinstance(key, keys.ECDSA384P1) and - not isinstance(enckey, keys.ECDSA384P1Public)) - or (isinstance(key, keys.RSA) and - not isinstance(enckey, keys.RSAPublic))): - # FIXME - raise click.UsageError("Signing and encryption must use the same " - "type of key") + if enckey and key and ((isinstance(key, keys.ECDSA256P1) and + not isinstance(enckey, keys.ECDSA256P1Public)) + or (isinstance(key, keys.ECDSA384P1) and + not isinstance(enckey, keys.ECDSA384P1Public)) + or (isinstance(key, keys.RSA) and + not isinstance(enckey, keys.RSAPublic))): + # FIXME + raise click.UsageError("Signing and encryption must use the same " + "type of key") if pad_sig and hasattr(key, 'pad_sig'): key.pad_sig = True @@ -520,10 +491,10 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, for tlv in custom_tlv: tag = int(tlv[0], 0) if tag in custom_tlvs: - raise click.UsageError('Custom TLV %s already exists.' % hex(tag)) + raise click.UsageError(f'Custom TLV {hex(tag)} already exists.') if tag in image.TLV_VALUES.values(): raise click.UsageError( - 'Custom TLV %s conflicts with predefined TLV.' % hex(tag)) + f'Custom TLV {hex(tag)} conflicts with predefined TLV.') value = tlv[1] if value.startswith('0x'): @@ -598,7 +569,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, lc = comp_default_lc, lp = comp_default_lp) compressed_img.load_compressed(compressed_data, compression_header) compressed_img.base_addr = img.base_addr - keep_comp_size = False; + keep_comp_size = False if enckey: keep_comp_size = True compressed_img.create(key, public_key_format, enckey, diff --git a/scripts/setup.py b/scripts/setup.py index c97de95ac..02b266650 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -1,7 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 import setuptools - from imgtool import imgtool_version setuptools.setup( diff --git a/scripts/tests/test_commands.py b/scripts/tests/test_commands.py index c0c605213..4ef794e25 100644 --- a/scripts/tests/test_commands.py +++ b/scripts/tests/test_commands.py @@ -15,10 +15,9 @@ from importlib import metadata import pytest - from click.testing import CliRunner -from imgtool.main import imgtool from imgtool import imgtool_version +from imgtool.main import imgtool from packaging.version import Version # all available imgtool commands diff --git a/scripts/tests/test_compression.py b/scripts/tests/test_compression.py index 35597ec29..9483097d5 100644 --- a/scripts/tests/test_compression.py +++ b/scripts/tests/test_compression.py @@ -16,14 +16,13 @@ import pytest from click.testing import CliRunner - from imgtool.image import Image from imgtool.main import ( - comp_default_lp, comp_default_dictsize, comp_default_lc, - create_lzma2_header, + comp_default_lp, comp_default_pb, + create_lzma2_header, imgtool, ) diff --git a/scripts/tests/test_keys.py b/scripts/tests/test_keys.py index a812ba2f1..92a50a45f 100644 --- a/scripts/tests/test_keys.py +++ b/scripts/tests/test_keys.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest - import subprocess + +import pytest from click.testing import CliRunner from imgtool import main as imgtool_main from imgtool.main import imgtool 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: