/* update_flash.c * * Implementation for Flash based updater * * * Copyright (C) 2021 wolfSSL Inc. * * This file is part of wolfBoot. * * wolfBoot is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include #include "loader.h" #include "image.h" #include "hal.h" #include "spi_flash.h" #include "wolfboot/wolfboot.h" #include "delta.h" #include "printf.h" #ifdef WOLFBOOT_TPM #include "tpm.h" #endif #ifdef SECURE_PKCS11 int WP11_Library_Init(void); #endif #ifdef RAM_CODE #ifndef TARGET_rp2350 extern unsigned int _start_text; #else extern unsigned int __logical_binary_start; unsigned int _start_text = (unsigned int)&__logical_binary_start; #endif static volatile const uint32_t __attribute__((used)) wolfboot_version = WOLFBOOT_VERSION; #ifdef EXT_FLASH # ifndef BUFFER_DECLARED # define BUFFER_DECLARED static uint8_t buffer[FLASHBUFFER_SIZE]; # endif #endif #ifdef EXT_ENCRYPTED #include "encrypt.h" #endif static void RAMFUNCTION wolfBoot_erase_bootloader(void) { uint32_t len = WOLFBOOT_PARTITION_BOOT_ADDRESS - ARCH_FLASH_OFFSET; hal_flash_erase(ARCH_FLASH_OFFSET, len); } #include static void RAMFUNCTION wolfBoot_self_update(struct wolfBoot_image *src) { uintptr_t pos = 0; uintptr_t src_offset = IMAGE_HEADER_SIZE; hal_flash_unlock(); wolfBoot_erase_bootloader(); #ifdef EXT_FLASH if (PART_IS_EXT(src)) { while (pos < src->fw_size) { uint8_t buffer[FLASHBUFFER_SIZE]; if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { uintptr_t opos = pos + ((uintptr_t)&_start_text); ext_flash_check_read((uintptr_t)(src->hdr) + src_offset + pos, (void*)buffer, FLASHBUFFER_SIZE); hal_flash_write(opos, buffer, FLASHBUFFER_SIZE); } pos += FLASHBUFFER_SIZE; } goto lock_and_reset; } #endif while (pos < src->fw_size) { if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { uint8_t *orig = (uint8_t*)(src->hdr + src_offset + pos); hal_flash_write(pos + (uintptr_t)&_start_text, orig, FLASHBUFFER_SIZE); } pos += FLASHBUFFER_SIZE; } #ifdef EXT_FLASH lock_and_reset: #endif hal_flash_lock(); arch_reboot(); } void wolfBoot_check_self_update(void) { uint8_t st; struct wolfBoot_image update; /* Check for self update in the UPDATE partition */ if ((wolfBoot_get_partition_state(PART_UPDATE, &st) == 0) && (st == IMG_STATE_UPDATING) && (wolfBoot_open_image(&update, PART_UPDATE) == 0) && wolfBoot_get_image_type(PART_UPDATE) == (HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH)) { uint32_t update_version = wolfBoot_update_firmware_version(); if (update_version <= wolfboot_version) { hal_flash_unlock(); wolfBoot_erase_partition(PART_UPDATE); hal_flash_lock(); return; } if (wolfBoot_verify_integrity(&update) < 0) return; if (wolfBoot_verify_authenticity(&update) < 0) return; PART_SANITY_CHECK(&update); wolfBoot_self_update(&update); } } #endif /* RAM_CODE for self_update */ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src, struct wolfBoot_image *dst, uint32_t sector) { uint32_t pos = 0; uint32_t src_sector_offset = (sector * WOLFBOOT_SECTOR_SIZE); uint32_t dst_sector_offset = src_sector_offset; #ifdef EXT_ENCRYPTED uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE]; uint32_t iv_counter; #endif if (src == dst) return 0; wolfBoot_printf("Copy sector %d (part %d->%d)\n", sector, src->part, dst->part); if (src->part == PART_SWAP) src_sector_offset = 0; if (dst->part == PART_SWAP) dst_sector_offset = 0; #ifdef EXT_ENCRYPTED wolfBoot_get_encrypt_key(key, nonce); if(src->part == PART_SWAP) iv_counter = dst_sector_offset; else iv_counter = src_sector_offset; iv_counter /= ENCRYPT_BLOCK_SIZE; crypto_set_iv(nonce, iv_counter); #endif #ifdef EXT_FLASH if (PART_IS_EXT(src)) { #ifndef BUFFER_DECLARED #define BUFFER_DECLARED static uint8_t buffer[FLASHBUFFER_SIZE]; #endif wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE); while (pos < WOLFBOOT_SECTOR_SIZE) { if (src_sector_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { /* bypass decryption, copy encrypted data into swap */ if (dst->part == PART_SWAP) { ext_flash_read((uintptr_t)(src->hdr) + src_sector_offset + pos, (void *)buffer, FLASHBUFFER_SIZE); } else { ext_flash_check_read((uintptr_t)(src->hdr) + src_sector_offset + pos, (void *)buffer, FLASHBUFFER_SIZE); } wb_flash_write(dst, dst_sector_offset + pos, buffer, FLASHBUFFER_SIZE); } pos += FLASHBUFFER_SIZE; } return pos; } #endif wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE); while (pos < WOLFBOOT_SECTOR_SIZE) { if (src_sector_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) { uint8_t *orig = (uint8_t*)(src->hdr + src_sector_offset + pos); wb_flash_write(dst, dst_sector_offset + pos, orig, FLASHBUFFER_SIZE); } pos += FLASHBUFFER_SIZE; } return pos; } #ifndef DISABLE_BACKUP #ifdef EXT_ENCRYPTED # define TRAILER_OFFSET_WORDS \ ((ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE) / sizeof(uint32_t)) #else # define TRAILER_OFFSET_WORDS 0 #endif /** * @brief Performs the final swap and erase operations during a secure update, * ensuring that if power is lost during the update, the process can be resumed * on next boot. * * This function handles the final phase of the three-way swap update process. * It ensures that the update is atomic and power-fail safe by: * 1. Saving the sector at tmpBootPos (staging sector) to the swap area * 2. Setting a magic trailer value to mark the swap as in progress * 3. Erasing the last sector(s) of the boot partition (where partition state is stored) * 4. Restoring the saved staging sector from swap back to boot * 5. Setting the boot partition state to TESTING * 6. Erasing the last sector(s) of the update partition * * The staging sector (tmpBootPos) is positioned right before the final sectors * that will be erased. This sector is preserved and used to store a magic trailer * that indicates a swap operation is in progress. * * The function can be called in two modes: * - Normal mode (resume=0): Initiates the swap and erase process * - Resume mode (resume=1): Checks if a swap was interrupted and completes it * * @param resume If 1, checks for interrupted swap and resumes it; if 0, starts * new swap * @return 0 on success, negative value if no swap needed or on error */ static int wolfBoot_swap_and_final_erase(int resume) { struct wolfBoot_image boot[1]; struct wolfBoot_image update[1]; struct wolfBoot_image swap[1]; uint8_t updateState; int eraseLen = (WOLFBOOT_SECTOR_SIZE #ifdef NVM_FLASH_WRITEONCE /* need to erase the redundant sector too */ * 2 #endif ); int swapDone = 0; /* Calculate position of staging sector - just before the final sectors * that store partition state */ uintptr_t tmpBootPos = WOLFBOOT_PARTITION_SIZE - eraseLen - WOLFBOOT_SECTOR_SIZE; uint32_t tmpBuffer[TRAILER_OFFSET_WORDS + 1]; /* open partitions (ignore failure) */ wolfBoot_open_image(boot, PART_BOOT); wolfBoot_open_image(update, PART_UPDATE); wolfBoot_open_image(swap, PART_SWAP); wolfBoot_get_partition_state(PART_UPDATE, &updateState); /* Read the trailer from the staging sector to check if we're resuming an * interrupted operation */ #if defined(EXT_FLASH) && PARTN_IS_EXT(PART_BOOT) ext_flash_read((uintptr_t)(boot->hdr + tmpBootPos), (void*)tmpBuffer, sizeof(tmpBuffer)); #else memcpy(tmpBuffer, boot->hdr + tmpBootPos, sizeof(tmpBuffer)); #endif /* Check if the magic trailer exists - indicates an interrupted swap * operation */ /* final swap and erase flag is WOLFBOOT_MAGIC_TRAIL */ if (tmpBuffer[TRAILER_OFFSET_WORDS] == WOLFBOOT_MAGIC_TRAIL) { swapDone = 1; } /* If we're in resume mode but no swap was in progress, return */ if ((resume == 1) && (swapDone == 0) && (updateState != IMG_STATE_FINAL_FLAGS) ) { return -1; } hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); #endif /* If update state isn't set to FINAL_FLAGS, this is the first run of the function */ /* IMG_STATE_FINAL_FLAGS allows re-entry without blowing away swap */ if (updateState != IMG_STATE_FINAL_FLAGS) { /* First, backup the staging sector (sector at tmpBootPos) into swap partition */ /* This sector will be modified with the magic trailer, so we need to preserve it */ wolfBoot_copy_sector(boot, swap, tmpBootPos / WOLFBOOT_SECTOR_SIZE); /* Mark update as being in final swap phase to allow resumption if power fails */ wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_FINAL_FLAGS); } #ifdef EXT_ENCRYPTED if (swapDone == 0) { /* For encrypted images: Get the encryption key and IV */ wolfBoot_get_encrypt_key((uint8_t*)tmpBuffer, (uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]); /* Set the magic trailer in the buffer and write it to the staging sector */ tmpBuffer[TRAILER_OFFSET_WORDS] = WOLFBOOT_MAGIC_TRAIL; wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE); wb_flash_write(boot, tmpBootPos, (void*)tmpBuffer, sizeof(tmpBuffer)); } #endif /* Erase the last sector(s) of boot partition (where partition state is stored) */ wb_flash_erase(boot, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen); #ifdef EXT_ENCRYPTED /* Initialize encryption with the saved key */ wolfBoot_set_encrypt_key((uint8_t*)tmpBuffer, (uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]); /* wolfBoot_set_encrypt_key calls hal_flash_unlock, need to unlock again */ hal_flash_unlock(); #endif /* Restore the original contents of the staging sector (with the magic trailer if encrypted) */ if (tmpBootPos < boot->fw_size + IMAGE_HEADER_SIZE) { wolfBoot_copy_sector(swap, boot, tmpBootPos / WOLFBOOT_SECTOR_SIZE); } else { wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE); } /* Mark boot partition as TESTING - this tells bootloader to fallback if update fails */ wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING); /* Erase the last sector(s) of update partition */ /* This resets the update partition state to IMG_STATE_NEW */ wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen); #ifdef EXT_FLASH ext_flash_lock(); #endif hal_flash_lock(); return 0; } #endif #ifdef DELTA_UPDATES #ifndef DELTA_BLOCK_SIZE # define DELTA_BLOCK_SIZE 1024 #endif static int wolfBoot_delta_update(struct wolfBoot_image *boot, struct wolfBoot_image *update, struct wolfBoot_image *swap, int inverse, int resume) { int sector = 0; int ret; uint8_t flag, st; int hdr_size; uint8_t delta_blk[DELTA_BLOCK_SIZE]; uint32_t offset = 0; uint16_t ptr_len; uint32_t *img_offset; uint32_t *img_size; uint32_t total_size; WB_PATCH_CTX ctx; uint32_t cur_v, upd_v, delta_base_v; #ifdef EXT_ENCRYPTED uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE]; uint8_t enc_blk[DELTA_BLOCK_SIZE]; #endif uint16_t delta_base_hash_sz; uint8_t *delta_base_hash; uint16_t base_hash_sz; uint8_t *base_hash; /* Use biggest size for the swap */ total_size = boot->fw_size + IMAGE_HEADER_SIZE; if ((update->fw_size + IMAGE_HEADER_SIZE) > total_size) total_size = update->fw_size + IMAGE_HEADER_SIZE; hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); #endif /* Read encryption key/IV before starting the update */ #ifdef EXT_ENCRYPTED wolfBoot_get_encrypt_key(key, nonce); #endif if (wolfBoot_get_delta_info(PART_UPDATE, inverse, &img_offset, &img_size, &delta_base_hash, &delta_base_hash_sz) < 0) { return -1; } cur_v = wolfBoot_current_firmware_version(); upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); if (delta_base_hash_sz != WOLFBOOT_SHA_DIGEST_SIZE) { if (delta_base_hash_sz == 0) { wolfBoot_printf("Warning: delta update: Base hash not found in image\n"); delta_base_hash = NULL; } else { wolfBoot_printf("Error: delta update: Base hash size mismatch" " (size: %x expected %x)\n", delta_base_hash_sz, WOLFBOOT_SHA_DIGEST_SIZE); return -1; } } #if defined(WOLFBOOT_HASH_SHA256) base_hash_sz = wolfBoot_find_header(boot->hdr + IMAGE_HEADER_OFFSET, HDR_SHA256, &base_hash); #elif defined(WOLFBOOT_HASH_SHA384) base_hash_sz = wolfBoot_find_header(boot->hdr + IMAGE_HEADER_OFFSET, HDR_SHA384, &base_hash); #elif defined(WOLFBOOT_HASH_SHA3_384) base_hash_sz = wolfBoot_find_header(boot->hdr + IMAGE_HEADER_OFFSET, HDR_SHA3_384, &base_hash); #else #error "Delta update: Fatal error, no hash algorithm defined!" #endif if (inverse) { if (((cur_v == upd_v) && (delta_base_v < cur_v)) || resume) { ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + IMAGE_HEADER_SIZE, update->hdr + *img_offset, *img_size); } else { wolfBoot_printf("Delta version check failed! " "Cur 0x%x, Upd 0x%x, Delta 0x%x\n", cur_v, upd_v, delta_base_v); ret = -1; } } else { if (!resume && (cur_v != delta_base_v)) { /* Wrong base image version, cannot apply delta patch */ wolfBoot_printf("Delta Base 0x%x != Cur 0x%x\n", cur_v, delta_base_v); ret = -1; } else if (!resume && delta_base_hash && memcmp(base_hash, delta_base_hash, base_hash_sz) != 0) { /* Wrong base image digest, cannot apply delta patch */ wolfBoot_printf("Delta Base hash mismatch\n"); ret = -1; } else { ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + IMAGE_HEADER_SIZE, update->hdr + IMAGE_HEADER_SIZE, *img_size); } } if (ret < 0) goto out; while((sector * WOLFBOOT_SECTOR_SIZE) < (int)total_size) { if ((wolfBoot_get_update_sector_flag(sector, &flag) != 0) || (flag == SECT_FLAG_NEW)) { uint32_t len = 0; wb_flash_erase(swap, 0, WOLFBOOT_SECTOR_SIZE); while (len < WOLFBOOT_SECTOR_SIZE) { ret = wb_patch(&ctx, delta_blk, DELTA_BLOCK_SIZE); if (ret > 0) { #ifdef EXT_ENCRYPTED uint32_t iv_counter = sector * WOLFBOOT_SECTOR_SIZE + len; int wr_ret; iv_counter /= ENCRYPT_BLOCK_SIZE; /* Encrypt + send */ crypto_set_iv(nonce, iv_counter); crypto_encrypt(enc_blk, delta_blk, ret); wr_ret = ext_flash_write( (uint32_t)(WOLFBOOT_PARTITION_SWAP_ADDRESS + len), enc_blk, ret); if (wr_ret < 0) { ret = wr_ret; goto out; } #else wb_flash_write(swap, len, delta_blk, ret); #endif len += ret; } else if (ret == 0) { break; } else goto out; } flag = SECT_FLAG_SWAPPING; wolfBoot_set_update_sector_flag(sector, flag); } else { /* Consume one sector off the patched image * when resuming an interrupted patch */ uint32_t len = 0; while (len < WOLFBOOT_SECTOR_SIZE) { ret = wb_patch(&ctx, delta_blk, DELTA_BLOCK_SIZE); if (ret == 0) break; if (ret < 0) goto out; len += ret; } } if (flag == SECT_FLAG_SWAPPING) { wolfBoot_copy_sector(swap, boot, sector); flag = SECT_FLAG_UPDATED; if (((sector + 1) * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE) wolfBoot_set_update_sector_flag(sector, flag); } if (sector == 0) { /* New total image size after first sector is patched */ volatile uint32_t update_size; hal_flash_lock(); update_size = wolfBoot_image_size((uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS) + IMAGE_HEADER_SIZE; hal_flash_unlock(); if (update_size > total_size) total_size = update_size; if (total_size <= IMAGE_HEADER_SIZE) { ret = -1; goto out; } if (total_size > WOLFBOOT_PARTITION_SIZE) { ret = -1; goto out; } } sector++; } ret = 0; /* erase to the last sector, writeonce has 2 sectors */ while((sector * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE #ifdef NVM_FLASH_WRITEONCE * 2 #endif ) { wb_flash_erase(boot, sector * WOLFBOOT_SECTOR_SIZE, WOLFBOOT_SECTOR_SIZE); sector++; } out: #ifdef EXT_FLASH ext_flash_lock(); #endif hal_flash_lock(); /* start re-entrant final erase, return code is only for resumption in * wolfBoot_start */ #ifndef DISABLE_BACKUP if (ret == 0) { wolfBoot_swap_and_final_erase(0); } #endif /* encryption key was not erased, will be erased by success */ return ret; } #endif #ifdef WOLFBOOT_ARMORED # ifdef __GNUC__ # pragma GCC push_options # pragma GCC optimize("O0") # elif defined(__IAR_SYSTEMS_ICC__) # pragma optimize=none # endif #endif /* Reserve space for two sectors in case of NVM_FLASH_WRITEONCE, for redundancy */ #ifndef NVM_FLASH_WRITEONCE #define MAX_UPDATE_SIZE (size_t)((WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE)) #else #define MAX_UPDATE_SIZE (size_t)((WOLFBOOT_PARTITION_SIZE - (2 *WOLFBOOT_SECTOR_SIZE))) #endif static int wolfBoot_get_total_size(struct wolfBoot_image* boot, struct wolfBoot_image* update) { uint32_t total_size = 0; /* Use biggest size for the swap */ total_size = boot->fw_size + IMAGE_HEADER_SIZE; if ((update->fw_size + IMAGE_HEADER_SIZE) > total_size) total_size = update->fw_size + IMAGE_HEADER_SIZE; return total_size; } static int RAMFUNCTION wolfBoot_update(int fallback_allowed) { uint32_t total_size = 0; const uint32_t sector_size = WOLFBOOT_SECTOR_SIZE; uint32_t sector = 0; /* we need to pre-set flag to SECT_FLAG_NEW in case magic hasn't been set * on the update partition as part of the delta update direction check. if * magic has not been set flag will have an un-determined value when we go * to check it */ uint8_t flag = SECT_FLAG_NEW; struct wolfBoot_image boot, update, swap; uint16_t update_type; uint32_t fw_size; uint32_t size; #if defined(DISABLE_BACKUP) && defined(EXT_ENCRYPTED) uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE]; #endif #ifdef DELTA_UPDATES uint8_t st; int inverse = 0; int resume = 0; int stateRet = -1; uint32_t cur_v; uint32_t up_v; #endif uint32_t cur_ver, upd_ver; wolfBoot_printf("Staring Update (fallback allowed %d)\n", fallback_allowed); /* No Safety check on open: we might be in the middle of a broken update */ wolfBoot_open_image(&update, PART_UPDATE); wolfBoot_open_image(&boot, PART_BOOT); wolfBoot_open_image(&swap, PART_SWAP); /* get total size */ total_size = wolfBoot_get_total_size(&boot, &update); if (total_size <= IMAGE_HEADER_SIZE) { wolfBoot_printf("Image total size %u too large!\n", total_size); return -1; } /* In case this is a new update, do the required * checks on the firmware update * before starting the swap */ update_type = wolfBoot_get_image_type(PART_UPDATE); wolfBoot_get_update_sector_flag(0, &flag); /* Check the first sector to detect interrupted update */ if (flag == SECT_FLAG_NEW) { if (((update_type & HDR_IMG_TYPE_PART_MASK) != HDR_IMG_TYPE_APP) || ((update_type & HDR_IMG_TYPE_AUTH_MASK) != HDR_IMG_TYPE_AUTH)) { wolfBoot_printf("Update type invalid 0x%x!=0x%x\n", update_type, HDR_IMG_TYPE_AUTH); return -1; } if (update.fw_size > MAX_UPDATE_SIZE - 1) { wolfBoot_printf("Invalid update size %u\n", update.fw_size); return -1; } if (!update.hdr_ok || (wolfBoot_verify_integrity(&update) < 0) || (wolfBoot_verify_authenticity(&update) < 0)) { wolfBoot_printf("Update verify failed: Hdr %d, Hash %d, Sig %d\n", update.hdr_ok, update.sha_ok, update.signature_ok); return -1; } PART_SANITY_CHECK(&update); cur_ver = wolfBoot_current_firmware_version(); upd_ver = wolfBoot_update_firmware_version(); wolfBoot_printf("Versions: Current 0x%x, Update 0x%x\n", cur_ver, upd_ver); #ifndef ALLOW_DOWNGRADE if ( ((fallback_allowed==1) && (~(uint32_t)fallback_allowed == 0xFFFFFFFE)) || (cur_ver < upd_ver) ) { VERIFY_VERSION_ALLOWED(fallback_allowed); } else { wolfBoot_printf("Update version not allowed\n"); return -1; } #endif } #ifdef DELTA_UPDATES if ((update_type & 0x00F0) == HDR_IMG_TYPE_DIFF) { cur_v = wolfBoot_current_firmware_version(); up_v = wolfBoot_update_firmware_version(); inverse = cur_v >= up_v; /* if magic isn't set stateRet will be -1 but that means we're on a * fresh partition and aren't resuming */ stateRet = wolfBoot_get_partition_state(PART_UPDATE, &st); /* if we've already written a sector or we've mangled the boot partition * header we can't determine the direction by version numbers. instead * use the update partition state, updating means regular, new means * reverting */ if ((stateRet == 0) && ((flag != SECT_FLAG_NEW) || (cur_v == 0))) { resume = 1; if (st == IMG_STATE_UPDATING) { inverse = 0; } else { inverse = 1; } } /* If we're dealing with a "ping-pong" fallback that wasn't interrupted * we need to set to UPDATING, otherwise there's no way to tell the * original direction of the update once interrupted */ else if ((inverse == 0) && (fallback_allowed == 1)) { hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); #endif wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); #ifdef EXT_FLASH ext_flash_lock(); #endif hal_flash_lock(); } return wolfBoot_delta_update(&boot, &update, &swap, inverse, resume); } #endif #ifndef DISABLE_BACKUP /* Interruptible swap */ hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); #endif /* Interruptible swap * The status is saved in the sector flags of the update partition. * If something goes wrong, the operation will be resumed upon reboot. */ while ((sector * sector_size) < total_size) { flag = SECT_FLAG_NEW; wolfBoot_get_update_sector_flag(sector, &flag); switch (flag) { case SECT_FLAG_NEW: flag = SECT_FLAG_SWAPPING; wolfBoot_copy_sector(&update, &swap, sector); if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) wolfBoot_set_update_sector_flag(sector, flag); /* FALL THROUGH */ case SECT_FLAG_SWAPPING: size = total_size - (sector * sector_size); if (size > sector_size) size = sector_size; flag = SECT_FLAG_BACKUP; wolfBoot_copy_sector(&boot, &update, sector); if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) wolfBoot_set_update_sector_flag(sector, flag); /* FALL THROUGH */ case SECT_FLAG_BACKUP: size = total_size - (sector * sector_size); if (size > sector_size) size = sector_size; flag = SECT_FLAG_UPDATED; wolfBoot_copy_sector(&swap, &boot, sector); if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) wolfBoot_set_update_sector_flag(sector, flag); break; case SECT_FLAG_UPDATED: /* FALL THROUGH */ default: break; } sector++; /* headers that can be in different positions depending on when the * power fails are now in a known state, re-read and swap fw_size * because the locations are correct but the metadata is now swapped * also recalculate total_size since it could be invalid */ if (sector == 1) { wolfBoot_open_image(&boot, PART_BOOT); wolfBoot_open_image(&update, PART_UPDATE); /* swap the fw_size since they're now swapped */ fw_size = boot.fw_size; boot.fw_size = update.fw_size; update.fw_size = fw_size; /* get total size */ total_size = wolfBoot_get_total_size(&boot, &update); } } /* Erase remainder of partitions */ #if defined(WOLFBOOT_FLASH_MULTI_SECTOR_ERASE) || defined(PRINTF_ENABLED) /* calculate number of remaining bytes */ /* reserve 1 sector for status (2 sectors for NV write once) */ #ifdef NVM_FLASH_WRITEONCE size = WOLFBOOT_PARTITION_SIZE - (sector * sector_size) - (2 * sector_size); #else size = WOLFBOOT_PARTITION_SIZE - (sector * sector_size) - sector_size; #endif wolfBoot_printf("Erasing remainder of partitions (%d sectors)...\n", size/sector_size); #endif #ifdef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE /* Erase remainder of flash sectors in one HAL command. */ /* This can improve performance if the HAL supports erase of * multiple sectors */ wb_flash_erase(&boot, sector * sector_size, size); wb_flash_erase(&update, sector * sector_size, size); #else /* Iterate over every remaining sector and erase individually. */ /* This loop is smallest code size */ while ((sector * sector_size) < WOLFBOOT_PARTITION_SIZE - sector_size #ifdef NVM_FLASH_WRITEONCE * 2 #endif ) { wb_flash_erase(&boot, sector * sector_size, sector_size); wb_flash_erase(&update, sector * sector_size, sector_size); sector++; } #endif /* WOLFBOOT_FLASH_MULTI_SECTOR_ERASE */ /* encryption key was not erased, will be erased by success */ #ifdef EXT_FLASH ext_flash_lock(); #endif hal_flash_lock(); /* start re-entrant final erase, return code is only for resumption in * wolfBoot_start*/ wolfBoot_swap_and_final_erase(0); #else /* DISABLE_BACKUP */ #ifdef WOLFBOOT_ELF_FLASH_SCATTER unsigned long entry; void* base = (void*)WOLFBOOT_PARTITION_BOOT_ADDRESS; wolfBoot_printf("ELF Scattered image digest check\n"); if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) { wolfBoot_printf("ELF Scattered image digest check: failed. Restoring " "scattered image...\n"); wolfBoot_load_flash_image_elf(PART_BOOT, &entry, PART_IS_EXT(boot)); if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) { wolfBoot_printf( "Fatal: Could not verify digest after scattering. Panic().\n"); wolfBoot_panic(); } } wolfBoot_printf( "Scattered image correctly verified. Setting entry point to %lx\n", entry); boot.fw_base = (void*)entry; #endif /* Direct Swap without power fail safety */ hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); #endif #ifdef EXT_ENCRYPTED wolfBoot_get_encrypt_key(key, nonce); #endif /* Directly copy the content of the UPDATE partition into the BOOT * partition. */ while ((sector * sector_size) < total_size) { wolfBoot_copy_sector(&update, &boot, sector); sector++; } /* erase remainder of partition */ #ifdef PRINTF_ENABLED size = WOLFBOOT_PARTITION_SIZE - (sector * sector_size); wolfBoot_printf("Erasing remainder of partition (%d sectors)...\n", size/sector_size); #endif while ((sector * sector_size) < WOLFBOOT_PARTITION_SIZE) { wb_flash_erase(&boot, sector * sector_size, sector_size); sector++; } wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_SUCCESS); #ifdef EXT_FLASH ext_flash_lock(); #endif hal_flash_lock(); /* Save the encryption key after swapping */ #ifdef EXT_ENCRYPTED wolfBoot_set_encrypt_key(key, nonce); #endif #endif /* DISABLE_BACKUP */ return 0; } #if defined(ARCH_SIM) && defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_SEAL) int wolfBoot_unlock_disk(void) { int ret; struct wolfBoot_image img; uint8_t secret[WOLFBOOT_MAX_SEAL_SZ]; int secretSz; uint8_t* policy = NULL, *pubkey_hint = NULL; uint16_t policySz = 0; int nvIndex = 0; /* where the sealed blob is stored in NV */ memset(secret, 0, sizeof(secret)); wolfBoot_printf("Unlocking disk...\n"); /* check policy */ ret = wolfBoot_open_image(&img, PART_BOOT); if (ret == 0) { ret = wolfBoot_get_header(&img, HDR_PUBKEY, &pubkey_hint); ret = (ret == WOLFBOOT_SHA_DIGEST_SIZE) ? 0 : -1; } if (ret == 0) { ret = wolfBoot_get_policy(&img, &policy, &policySz); if (ret == -TPM_RC_POLICY_FAIL) { /* the image is not signed with a policy */ wolfBoot_printf("Image policy signature missing!\n"); } } if (ret == 0) { /* try to unseal the secret */ ret = wolfBoot_unseal(pubkey_hint, policy, policySz, nvIndex, secret, &secretSz); if (ret != 0) { /* if secret does not exist, expect TPM_RC_HANDLE here */ if ((ret & RC_MAX_FMT1) == TPM_RC_HANDLE) { wolfBoot_printf("Sealed secret does not exist!\n"); } /* create secret to seal */ secretSz = 32; ret = wolfBoot_get_random(secret, secretSz); if (ret == 0) { wolfBoot_printf("Creating new secret (%d bytes)\n", secretSz); wolfBoot_print_hexstr(secret, secretSz, 0); /* seal new secret */ ret = wolfBoot_seal(pubkey_hint, policy, policySz, nvIndex, secret, secretSz); } if (ret == 0) { uint8_t secretCheck[WOLFBOOT_MAX_SEAL_SZ]; int secretCheckSz = 0; /* unseal again to make sure it works */ memset(secretCheck, 0, sizeof(secretCheck)); ret = wolfBoot_unseal(pubkey_hint, policy, policySz, nvIndex, secretCheck, &secretCheckSz); if (ret == 0) { if (secretSz != secretCheckSz || memcmp(secret, secretCheck, secretSz) != 0) { wolfBoot_printf("secret check mismatch!\n"); ret = -1; } } wolfBoot_printf("Secret Check %d bytes\n", secretCheckSz); wolfBoot_print_hexstr(secretCheck, secretCheckSz, 0); TPM2_ForceZero(secretCheck, sizeof(secretCheck)); } } } if (ret == 0) { wolfBoot_printf("Secret %d bytes\n", secretSz); wolfBoot_print_hexstr(secret, secretSz, 0); /* TODO: Unlock disk */ /* Extend a PCR from the mask to prevent future unsealing */ #if !defined(ARCH_SIM) && !defined(WOLFBOOT_NO_UNSEAL_PCR_EXTEND) { uint32_t pcrMask; uint32_t pcrArraySz; uint8_t pcrArray[1]; /* get one PCR from mask */ /* random value to extend the first PCR mask */ const uint8_t digest[WOLFBOOT_TPM_PCR_DIG_SZ] = { 0xEA, 0xA7, 0x5C, 0xF6, 0x91, 0x7C, 0x77, 0x91, 0xC5, 0x33, 0x16, 0x6D, 0x74, 0xFF, 0xCE, 0xCD, 0x27, 0xE3, 0x47, 0xF6, 0x82, 0x1D, 0x4B, 0xB1, 0x32, 0x70, 0x88, 0xFC, 0x69, 0xFF, 0x6C, 0x02, }; memcpy(&pcrMask, policy, sizeof(pcrMask)); pcrArraySz = wolfBoot_tpm_pcrmask_sel(pcrMask, pcrArray, sizeof(pcrArray)); /* get first PCR from mask */ wolfBoot_tpm2_extend(pcrArray[0], (uint8_t*)digest, __LINE__); } #endif } else { wolfBoot_printf("unlock disk failed! %d (%s)\n", ret, wolfTPM2_GetRCString(ret)); } TPM2_ForceZero(secret, sizeof(secretSz)); return ret; } #endif void RAMFUNCTION wolfBoot_start(void) { int bootRet; int updateRet; #ifndef DISABLE_BACKUP int resumedFinalErase; #endif uint8_t bootState; uint8_t updateState; struct wolfBoot_image boot; #if defined(ARCH_SIM) && defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_SEAL) wolfBoot_unlock_disk(); #endif #ifdef RAM_CODE wolfBoot_check_self_update(); #endif #ifdef NVM_FLASH_WRITEONCE /* nvm_select_fresh_sector needs unlocked flash in cases where the unused * sector needs to be erased */ hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); #endif #endif bootRet = wolfBoot_get_partition_state(PART_BOOT, &bootState); updateRet = wolfBoot_get_partition_state(PART_UPDATE, &updateState); #ifdef NVM_FLASH_WRITEONCE hal_flash_lock(); #ifdef EXT_FLASH ext_flash_lock(); #endif #endif #if !defined(DISABLE_BACKUP) /* resume the final erase in case the power failed before it finished */ resumedFinalErase = wolfBoot_swap_and_final_erase(1); if (resumedFinalErase != 0) #endif { /* Check if the BOOT partition is still in TESTING, * to trigger fallback. */ if ((bootRet == 0) && (bootState == IMG_STATE_TESTING)) { wolfBoot_update(1); } /* Check for new updates in the UPDATE partition or if we were * interrupted during the flags setting */ else if ((updateRet == 0) && (updateState == IMG_STATE_UPDATING)) { /* Check for new updates in the UPDATE partition */ wolfBoot_update(0); } } bootRet = wolfBoot_open_image(&boot, PART_BOOT); wolfBoot_printf("Booting version: 0x%x\n", wolfBoot_get_blob_version(boot.hdr)); if (bootRet < 0 || (wolfBoot_verify_integrity(&boot) < 0) || (wolfBoot_verify_authenticity(&boot) < 0) ) { wolfBoot_printf("Boot failed: Hdr %d, Hash %d, Sig %d\n", boot.hdr_ok, boot.sha_ok, boot.signature_ok); wolfBoot_printf("Trying emergency update\n"); if (likely(wolfBoot_update(1) < 0)) { /* panic: no boot option available. */ wolfBoot_printf("Boot failed! No boot option available!\n"); #ifdef WOLFBOOT_TPM wolfBoot_tpm2_deinit(); #endif wolfBoot_panic(); } else { /* Emergency update successful, try to re-open boot image */ if (likely(((wolfBoot_open_image(&boot, PART_BOOT) < 0) || (wolfBoot_verify_integrity(&boot) < 0) || (wolfBoot_verify_authenticity(&boot) < 0) ))) { wolfBoot_printf("Boot (try 2) failed: Hdr %d, Hash %d, Sig %d\n", boot.hdr_ok, boot.sha_ok, boot.signature_ok); /* panic: something went wrong after the emergency update */ #ifdef WOLFBOOT_TPM wolfBoot_tpm2_deinit(); #endif wolfBoot_panic(); } } } PART_SANITY_CHECK(&boot); #ifdef WOLFBOOT_ELF_FLASH_SCATTER unsigned long entry; wolfBoot_printf("ELF Scattered image digest check\n"); if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) { wolfBoot_printf("ELF Scattered image digest check: failed. Restoring " "scattered image...\n"); if (wolfBoot_load_flash_image_elf(PART_BOOT, &entry, PART_IS_EXT(&boot)) < 0) { wolfBoot_printf( "ELF: [BOOT] ERROR: could not store scattered image\n"); wolfBoot_panic(); } if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) { wolfBoot_printf( "Fatal: Could not verify digest after scattering. Panic().\n"); wolfBoot_panic(); } } wolfBoot_printf( "Scattered image correctly verified. Setting entry point to %lx\n", entry); boot.fw_base = (void*)entry; #endif #ifdef WOLFBOOT_TPM wolfBoot_tpm2_deinit(); #endif #ifdef SECURE_PKCS11 WP11_Library_Init(); #endif #ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT (void)hal_hsm_disconnect(); #endif hal_prepare_boot(); do_boot((void *)boot.fw_base); } #ifdef WOLFBOOT_ARMORED # ifdef __GNUC__ # pragma GCC pop_options # elif defined(__IAR_SYSTEMS_ICC__) # pragma optimize=default # endif #endif