/* * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2020 Nordic Semiconductor ASA * Copyright (c) 2020 Arm Limited */ #include #include "bootutil/image.h" #include "bootutil_priv.h" #include "bootutil/boot_record.h" #include "bootutil/bootutil.h" #include "bootutil/bootutil_log.h" #include "bootutil/bootutil_public.h" #include "bootutil/fault_injection_hardening.h" #include "mcuboot_config/mcuboot_config.h" BOOT_LOG_MODULE_DECLARE(mcuboot); /* Variables passed outside of unit via pointers. */ static struct image_header _hdr = { 0 }; static struct boot_loader_state state; struct boot_loader_state *boot_get_loader_state(void) { return &state; } #if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; struct image_max_size *boot_get_image_max_sizes(void) { return image_max_sizes; } #endif #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) /** * 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 */ fih_ret boot_image_validate(const struct flash_area *fa_p, struct image_header *hdr) { static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; FIH_DECLARE(fih_rc, FIH_FAILURE); BOOT_LOG_DBG("boot_image_validate: encrypted == %d", (int)IS_ENCRYPTED(hdr)); /* NOTE: The first argument to boot_image_validate, for enc_state pointer, * is allowed to be NULL only because the single image loader compiles * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses * the pointer from compilation. */ /* Validate hash */ if (IS_ENCRYPTED(hdr)) { /* Clear the encrypted flag we didn't supply a key * This flag could be set if there was a decryption in place * was performed. We will try to validate the image, and if still * encrypted the validation will fail, and go in panic mode */ hdr->ih_flags &= ~(ENCRYPTIONFLAGS); } FIH_CALL(bootutil_img_validate, fih_rc, NULL, hdr, fa_p, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL); FIH_RET(fih_rc); } #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/ #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) inline static fih_ret boot_image_validate_once(const struct flash_area *fa_p, struct image_header *hdr) { static struct boot_swap_state state; int rc; FIH_DECLARE(fih_rc, FIH_FAILURE); BOOT_LOG_DBG("boot_image_validate_once: flash area %p", fa_p); memset(&state, 0, sizeof(struct boot_swap_state)); rc = boot_read_swap_state(fa_p, &state); if (rc != 0) FIH_RET(FIH_FAILURE); if (state.magic != BOOT_MAGIC_GOOD || state.image_ok != BOOT_FLAG_SET) { /* At least validate the image once */ FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr); if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { FIH_RET(FIH_FAILURE); } if (state.magic != BOOT_MAGIC_GOOD) { rc = boot_write_magic(fa_p); if (rc != 0) FIH_RET(FIH_FAILURE); } rc = boot_write_image_ok(fa_p); if (rc != 0) FIH_RET(FIH_FAILURE); } FIH_RET(FIH_SUCCESS); } #endif int boot_open_all_flash_areas(struct boot_loader_state *state) { int rc; rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)); if (rc != 0) { BOOT_LOG_DBG("Error opening image primary area. rc = %d", rc); } return rc; } void boot_close_all_flash_areas(struct boot_loader_state *state) { flash_area_close(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)); } #if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) static int app_max_sectors(struct boot_loader_state *state) { uint32_t sz = 0; uint32_t sector_sz; uint32_t trailer_sz; uint32_t trailer_sectors = 0; sector_sz = boot_img_sector_size(state, BOOT_SLOT_PRIMARY, 0); trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); while (1) { sz += sector_sz; ++trailer_sectors; if (sz >= trailer_sz) { break; } } return boot_img_num_sectors(state, BOOT_SLOT_PRIMARY) - trailer_sectors; } int app_max_size(struct boot_loader_state *state) { uint32_t sector_sz; sector_sz = boot_img_sector_size(state, BOOT_SLOT_PRIMARY, 0); return app_max_sectors(state) * sector_sz; } /** * Fetches the maximum allowed size of the image */ const struct image_max_size *boot_get_max_app_size(void) { if (image_max_sizes[0].calculated == false) { /* Information not available, need to fetch it */ boot_fetch_slot_state_sizes(); } return image_max_sizes; } #endif /** * Gather information on image and prepare for booting. * * @parami[out] rsp Parameters for booting image, on success * * @return FIH_SUCCESS on success; nonzero on failure. */ fih_ret boot_go(struct boot_rsp *rsp) { int rc = -1; FIH_DECLARE(fih_rc, FIH_FAILURE); BOOT_LOG_DBG("boot_go: Single loader"); rc = boot_open_all_flash_areas(&state); if (rc != 0) { goto out; } rc = boot_image_load_header(BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY), &_hdr); if (rc != 0) goto out; #ifdef MCUBOOT_RAM_LOAD state.imgs[0][0].hdr = _hdr; rc = boot_load_image_to_sram(&state); if (rc != 0) goto out; #endif #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT FIH_CALL(boot_image_validate, fih_rc, BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY), &_hdr); if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { #ifdef MCUBOOT_RAM_LOAD boot_remove_image_from_sram(&state); #endif goto out; } #elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) FIH_CALL(boot_image_validate_once, fih_rc, BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY), &_hdr); if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { #ifdef MCUBOOT_RAM_LOAD boot_remove_image_from_sram(&state); #endif goto out; } #else fih_rc = FIH_SUCCESS; #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ #ifdef MCUBOOT_MEASURED_BOOT rc = boot_save_boot_status(0, &_hdr, BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY)); if (rc != 0) { BOOT_LOG_ERR("Failed to add image data to shared area"); return rc; } #endif /* MCUBOOT_MEASURED_BOOT */ #ifdef MCUBOOT_DATA_SHARING rc = boot_save_shared_data(&_hdr, BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY), 0, NULL); if (rc != 0) { BOOT_LOG_ERR("Failed to add data to shared memory area."); return rc; } #endif /* MCUBOOT_DATA_SHARING */ rsp->br_flash_dev_id = flash_area_get_device_id(BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY)); rsp->br_image_off = flash_area_get_off(BOOT_IMG_AREA(&state, BOOT_SLOT_PRIMARY)); rsp->br_hdr = &_hdr; out: boot_close_all_flash_areas(&state); FIH_RET(fih_rc); }