/* * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2016-2020 Linaro LTD * Copyright (c) 2016-2019 JUUL Labs * Copyright (c) 2019-2023 Arm Limited * Copyright (c) 2024 Nordic Semiconductor ASA * * Original license: * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 "bootutil/bootutil.h" #include "bootutil/bootutil_log.h" #include "bootutil/bootutil_public.h" #include "bootutil/image.h" #include "bootutil_priv.h" #include "bootutil/fault_injection_hardening.h" #include "bootutil/ramload.h" #include "bootutil/mcuboot_status.h" #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif #include "mcuboot_config/mcuboot_config.h" BOOT_LOG_MODULE_DECLARE(mcuboot); #ifndef MULTIPLE_EXECUTABLE_RAM_REGIONS #if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE) #error "Platform MUST define executable RAM bounds in case of RAM_LOAD" #endif #endif /** * Verifies that the active slot of the current image can be loaded within the * predefined bounds that are allowed to be used by executable images. * * @param state Boot loader status information. * * @return 0 on success; nonzero on failure. */ static int boot_verify_ram_load_address(struct boot_loader_state *state) { uint32_t img_dst; uint32_t img_sz; uint32_t img_end_addr; uint32_t exec_ram_start; uint32_t exec_ram_size; (void)state; #ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS int rc; rc = boot_get_image_exec_ram_info(BOOT_CURR_IMG(state), &exec_ram_start, &exec_ram_size); if (rc != 0) { return BOOT_EBADSTATUS; } #else exec_ram_start = IMAGE_EXECUTABLE_RAM_START; exec_ram_size = IMAGE_EXECUTABLE_RAM_SIZE; #endif img_dst = state->slot_usage[BOOT_CURR_IMG(state)].img_dst; img_sz = state->slot_usage[BOOT_CURR_IMG(state)].img_sz; if (img_dst < exec_ram_start) { return BOOT_EBADIMAGE; } if (!boot_u32_safe_add(&img_end_addr, img_dst, img_sz)) { return BOOT_EBADIMAGE; } if (img_end_addr > (exec_ram_start + exec_ram_size)) { return BOOT_EBADIMAGE; } return 0; } #ifdef MCUBOOT_ENC_IMAGES /** * Copies and decrypts an image from a slot in the flash to an SRAM address. * * @param state Boot loader status information. * @param slot The flash slot of the image to be copied to SRAM. * @param hdr The image header. * @param src_sz Size of the image. * @param img_dst Pointer to the address at which the image needs to be * copied to SRAM. * * @return 0 on success; nonzero on failure. */ static int boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, uint32_t slot, struct image_header *hdr, uint32_t src_sz, uint32_t img_dst) { /* The flow for the decryption and copy of the image is as follows : * 1. The whole image is copied to the RAM (header + payload + TLV). * 2. The encryption key is loaded from the TLV in flash. * 3. The image is then decrypted chunk by chunk in RAM (1 chunk * is 1024 bytes). Only the payload section is decrypted. * 4. The image is authenticated in RAM. */ const struct flash_area *fap_src = NULL; struct boot_status bs; uint32_t blk_off; uint32_t tlv_off; uint32_t blk_sz; uint32_t bytes_copied = hdr->ih_hdr_size; uint32_t chunk_sz; uint32_t max_sz = 1024; uint16_t idx; uint8_t * cur_dst; int area_id; int rc; uint8_t * ram_dst = (void *)(IMAGE_RAM_BASE + img_dst); area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); rc = flash_area_open(area_id, &fap_src); if (rc != 0){ return BOOT_EFLASH; } tlv_off = BOOT_TLV_OFF(hdr); /* Copying the whole image in RAM */ rc = flash_area_read(fap_src, 0, ram_dst, src_sz); if (rc != 0) { goto done; } rc = boot_enc_load(state, slot, hdr, fap_src, &bs); if (rc < 0) { goto done; } /* if rc > 0 then the key has already been loaded */ if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { goto done; } /* Starting at the end of the header as the header section is not encrypted */ while (bytes_copied < tlv_off) { /* TLV section copied previously */ if (src_sz - bytes_copied > max_sz) { chunk_sz = max_sz; } else { chunk_sz = src_sz - bytes_copied; } cur_dst = ram_dst + bytes_copied; blk_sz = chunk_sz; idx = 0; blk_off = ((bytes_copied) - hdr->ih_hdr_size) & 0xf; if (bytes_copied + chunk_sz > tlv_off) { /* Going over TLV section * Part of the chunk is encrypted payload */ blk_sz = tlv_off - (bytes_copied); } boot_enc_decrypt(BOOT_CURR_ENC(state), slot, (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, blk_off, cur_dst); bytes_copied += chunk_sz; } rc = 0; done: flash_area_close(fap_src); return rc; } #endif /* MCUBOOT_ENC_IMAGES */ /** * Copies a slot of the current image into SRAM. * * @param state Boot loader status information. * @param slot The flash slot of the image to be copied to SRAM. * @param img_dst The address at which the image needs to be copied to * SRAM. * @param img_sz The size of the image that needs to be copied to SRAM. * * @return 0 on success; nonzero on failure. */ static int boot_copy_image_to_sram(struct boot_loader_state *state, int slot, uint32_t img_dst, uint32_t img_sz) { int rc; const struct flash_area *fap_src = NULL; int area_id; #if (BOOT_IMAGE_NUMBER == 1) (void)state; #endif area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); rc = flash_area_open(area_id, &fap_src); if (rc != 0) { return BOOT_EFLASH; } /* Direct copy from flash to its new location in SRAM. */ rc = flash_area_read(fap_src, 0, (void *)(IMAGE_RAM_BASE + img_dst), img_sz); if (rc != 0) { BOOT_LOG_INF("Error whilst copying image %d from Flash to SRAM: %d", BOOT_CURR_IMG(state), rc); } flash_area_close(fap_src); return rc; } #if (BOOT_IMAGE_NUMBER > 1) /** * Checks if two memory regions (A and B) are overlap or not. * * @param start_a Start of the A region. * @param end_a End of the A region. * @param start_b Start of the B region. * @param end_b End of the B region. * * @return true if there is overlap; false otherwise. */ static bool do_regions_overlap(uint32_t start_a, uint32_t end_a, uint32_t start_b, uint32_t end_b) { if (start_b > end_a) { return false; } else if (start_b >= start_a) { return true; } else if (end_b > start_a) { return true; } return false; } /** * Checks if the image we want to load to memory overlap with an already * ramloaded image. * * @param state Boot loader status information. * * @return 0 if there is no overlap; nonzero otherwise. */ static int boot_check_ram_load_overlapping(struct boot_loader_state *state) { uint32_t i; uint32_t start_a; uint32_t end_a; uint32_t start_b; uint32_t end_b; uint32_t image_id_to_check = BOOT_CURR_IMG(state); start_a = state->slot_usage[image_id_to_check].img_dst; /* Safe to add here, values are already verified in * boot_verify_ram_load_address() */ end_a = start_a + state->slot_usage[image_id_to_check].img_sz; for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { if (state->slot_usage[i].active_slot == NO_ACTIVE_SLOT || i == image_id_to_check) { continue; } start_b = state->slot_usage[i].img_dst; /* Safe to add here, values are already verified in * boot_verify_ram_load_address() */ end_b = start_b + state->slot_usage[i].img_sz; if (do_regions_overlap(start_a, end_a, start_b, end_b)) { return -1; } } return 0; } #endif /** * Loads the active slot of the current image into SRAM. The load address and * image size is extracted from the image header. * * @param state Boot loader status information. * * @return 0 on success; nonzero on failure. */ int boot_load_image_to_sram(struct boot_loader_state *state) { uint32_t active_slot; struct image_header *hdr = NULL; uint32_t img_dst; uint32_t img_sz; int rc; active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; hdr = boot_img_hdr(state, active_slot); if (hdr->ih_flags & IMAGE_F_RAM_LOAD) { img_dst = hdr->ih_load_addr; rc = boot_read_image_size(state, active_slot, &img_sz); if (rc != 0) { return rc; } state->slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst; state->slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz; rc = boot_verify_ram_load_address(state); if (rc != 0) { BOOT_LOG_INF("Image %d RAM load address 0x%x is invalid.", BOOT_CURR_IMG(state), img_dst); return rc; } #if (BOOT_IMAGE_NUMBER > 1) rc = boot_check_ram_load_overlapping(state); if (rc != 0) { BOOT_LOG_INF("Image %d RAM loading to address 0x%x would overlap with\ another image.", BOOT_CURR_IMG(state), img_dst); return rc; } #endif #ifdef MCUBOOT_ENC_IMAGES /* decrypt image if encrypted and copy it to RAM */ if (IS_ENCRYPTED(hdr)) { rc = boot_decrypt_and_copy_image_to_sram(state, active_slot, hdr, img_sz, img_dst); } else { rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); } #else /* Copy image to the load address from where it currently resides in * flash. */ rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); #endif if (rc != 0) { BOOT_LOG_INF("Image %d RAM loading to 0x%x is failed.", BOOT_CURR_IMG(state), img_dst); } else { BOOT_LOG_INF("Image %d RAM loading to 0x%x is succeeded.", BOOT_CURR_IMG(state), img_dst); } } else { /* Only images that support IMAGE_F_RAM_LOAD are allowed if * MCUBOOT_RAM_LOAD is set. */ rc = BOOT_EBADIMAGE; } if (rc != 0) { state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; } return rc; } /** * Removes an image from SRAM, by overwriting it with zeros. * * @param state Boot loader status information. * * @return 0 on success; nonzero on failure. */ int boot_remove_image_from_sram(struct boot_loader_state *state) { (void)state; BOOT_LOG_INF("Removing image %d from SRAM at address 0x%x", BOOT_CURR_IMG(state), state->slot_usage[BOOT_CURR_IMG(state)].img_dst); memset((void*)(IMAGE_RAM_BASE + state->slot_usage[BOOT_CURR_IMG(state)].img_dst), 0, state->slot_usage[BOOT_CURR_IMG(state)].img_sz); state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; return 0; } /** * Removes an image from flash by erasing the corresponding flash area * * @param state Boot loader status information. * @param slot The flash slot of the image to be erased. * * @return 0 on success; nonzero on failure. */ int boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot) { int area_id; int rc; const struct flash_area *fap; (void)state; BOOT_LOG_INF("Removing image %d slot %d from flash", BOOT_CURR_IMG(state), slot); area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); rc = flash_area_open(area_id, &fap); if (rc == 0) { flash_area_erase(fap, 0, flash_area_get_size(fap)); flash_area_close(fap); } return rc; } int boot_load_image_from_flash_to_sram(struct boot_loader_state *state, struct image_header *hdr) { int active_slot; /* boot_load_image_to_sram will load the image from state active_slot, * so force it before loading the image. */ active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; BOOT_IMG(state, active_slot).hdr = *hdr; return boot_load_image_to_sram(state); }