blob: dd74052a36daac337748a224cea94419a732e6d3 [file] [log] [blame]
/*
* 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;
}