blob: 60baa5bdfbe0d7dd4b266835ec690b5666e5de8f [file] [log] [blame] [edit]
/*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <auth_mod.h>
#include <bl_common.h>
#include <debug.h>
#include <errno.h>
#include <io_storage.h>
#include <platform.h>
#include <string.h>
#include <utils.h>
#include <xlat_tables_defs.h>
#if TRUSTED_BOARD_BOOT
# ifdef DYN_DISABLE_AUTH
static int disable_auth;
/******************************************************************************
* API to dynamically disable authentication. Only meant for development
* systems. This is only invoked if DYN_DISABLE_AUTH is defined.
*****************************************************************************/
void dyn_disable_auth(void)
{
INFO("Disabling authentication of images dynamically\n");
disable_auth = 1;
}
# endif /* DYN_DISABLE_AUTH */
/******************************************************************************
* Function to determine whether the authentication is disabled dynamically.
*****************************************************************************/
static int dyn_is_auth_disabled(void)
{
# ifdef DYN_DISABLE_AUTH
return disable_auth;
# else
return 0;
# endif
}
#endif /* TRUSTED_BOARD_BOOT */
uintptr_t page_align(uintptr_t value, unsigned dir)
{
/* Round up the limit to the next page boundary */
if (value & (PAGE_SIZE - 1)) {
value &= ~(PAGE_SIZE - 1);
if (dir == UP)
value += PAGE_SIZE;
}
return value;
}
/******************************************************************************
* Determine whether the memory region delimited by 'addr' and 'size' is free,
* given the extents of free memory.
* Return 1 if it is free, 0 if it is not free or if the input values are
* invalid.
*****************************************************************************/
int is_mem_free(uintptr_t free_base, size_t free_size,
uintptr_t addr, size_t size)
{
uintptr_t free_end, requested_end;
/*
* Handle corner cases first.
*
* The order of the 2 tests is important, because if there's no space
* left (i.e. free_size == 0) but we don't ask for any memory
* (i.e. size == 0) then we should report that the memory is free.
*/
if (size == 0)
return 1; /* A zero-byte region is always free */
if (free_size == 0)
return 0;
/*
* Check that the end addresses don't overflow.
* If they do, consider that this memory region is not free, as this
* is an invalid scenario.
*/
if (check_uptr_overflow(free_base, free_size - 1))
return 0;
free_end = free_base + (free_size - 1);
if (check_uptr_overflow(addr, size - 1))
return 0;
requested_end = addr + (size - 1);
/*
* Finally, check that the requested memory region lies within the free
* region.
*/
return (addr >= free_base) && (requested_end <= free_end);
}
/* Generic function to return the size of an image */
size_t get_image_size(unsigned int image_id)
{
uintptr_t dev_handle;
uintptr_t image_handle;
uintptr_t image_spec;
size_t image_size = 0;
int io_result;
/* Obtain a reference to the image by querying the platform layer */
io_result = plat_get_image_source(image_id, &dev_handle, &image_spec);
if (io_result != 0) {
WARN("Failed to obtain reference to image id=%u (%i)\n",
image_id, io_result);
return 0;
}
/* Attempt to access the image */
io_result = io_open(dev_handle, image_spec, &image_handle);
if (io_result != 0) {
WARN("Failed to access image id=%u (%i)\n",
image_id, io_result);
return 0;
}
/* Find the size of the image */
io_result = io_size(image_handle, &image_size);
if ((io_result != 0) || (image_size == 0)) {
WARN("Failed to determine the size of the image id=%u (%i)\n",
image_id, io_result);
}
io_result = io_close(image_handle);
/* Ignore improbable/unrecoverable error in 'close' */
/* TODO: Consider maintaining open device connection from this
* bootloader stage
*/
io_result = io_dev_close(dev_handle);
/* Ignore improbable/unrecoverable error in 'dev_close' */
return image_size;
}
/*******************************************************************************
* Internal function to load an image at a specific address given
* an image ID and extents of free memory.
*
* If the load is successful then the image information is updated.
*
* Returns 0 on success, a negative error code otherwise.
******************************************************************************/
static int load_image(unsigned int image_id, image_info_t *image_data)
{
uintptr_t dev_handle;
uintptr_t image_handle;
uintptr_t image_spec;
uintptr_t image_base;
size_t image_size;
size_t bytes_read;
int io_result;
assert(image_data != NULL);
assert(image_data->h.version >= VERSION_2);
image_base = image_data->image_base;
/* Obtain a reference to the image by querying the platform layer */
io_result = plat_get_image_source(image_id, &dev_handle, &image_spec);
if (io_result != 0) {
WARN("Failed to obtain reference to image id=%u (%i)\n",
image_id, io_result);
return io_result;
}
/* Attempt to access the image */
io_result = io_open(dev_handle, image_spec, &image_handle);
if (io_result != 0) {
WARN("Failed to access image id=%u (%i)\n",
image_id, io_result);
return io_result;
}
INFO("Loading image id=%u at address %p\n", image_id,
(void *) image_base);
/* Find the size of the image */
io_result = io_size(image_handle, &image_size);
if ((io_result != 0) || (image_size == 0)) {
WARN("Failed to determine the size of the image id=%u (%i)\n",
image_id, io_result);
goto exit;
}
/* Check that the image size to load is within limit */
if (image_size > image_data->image_max_size) {
WARN("Image id=%u size out of bounds\n", image_id);
io_result = -EFBIG;
goto exit;
}
image_data->image_size = image_size;
/* We have enough space so load the image now */
/* TODO: Consider whether to try to recover/retry a partially successful read */
io_result = io_read(image_handle, image_base, image_size, &bytes_read);
if ((io_result != 0) || (bytes_read < image_size)) {
WARN("Failed to load image id=%u (%i)\n", image_id, io_result);
goto exit;
}
INFO("Image id=%u loaded: %p - %p\n", image_id, (void *) image_base,
(void *) (image_base + image_size));
exit:
io_close(image_handle);
/* Ignore improbable/unrecoverable error in 'close' */
/* TODO: Consider maintaining open device connection from this bootloader stage */
io_dev_close(dev_handle);
/* Ignore improbable/unrecoverable error in 'dev_close' */
return io_result;
}
static int load_auth_image_internal(unsigned int image_id,
image_info_t *image_data,
int is_parent_image)
{
int rc;
#if TRUSTED_BOARD_BOOT
if (dyn_is_auth_disabled() == 0) {
unsigned int parent_id;
/* Use recursion to authenticate parent images */
rc = auth_mod_get_parent_id(image_id, &parent_id);
if (rc == 0) {
rc = load_auth_image_internal(parent_id, image_data, 1);
if (rc != 0) {
return rc;
}
}
}
#endif /* TRUSTED_BOARD_BOOT */
/* Load the image */
rc = load_image(image_id, image_data);
if (rc != 0) {
return rc;
}
#if TRUSTED_BOARD_BOOT
if (dyn_is_auth_disabled() == 0) {
/* Authenticate it */
rc = auth_mod_verify_img(image_id,
(void *)image_data->image_base,
image_data->image_size);
if (rc != 0) {
/* Authentication error, zero memory and flush it right away. */
zero_normalmem((void *)image_data->image_base,
image_data->image_size);
flush_dcache_range(image_data->image_base,
image_data->image_size);
return -EAUTH;
}
}
#endif /* TRUSTED_BOARD_BOOT */
/*
* Flush the image to main memory so that it can be executed later by
* any CPU, regardless of cache and MMU state. If TBB is enabled, then
* the file has been successfully loaded and authenticated and flush
* only for child images, not for the parents (certificates).
*/
if (!is_parent_image) {
flush_dcache_range(image_data->image_base,
image_data->image_size);
}
return 0;
}
/*******************************************************************************
* Generic function to load and authenticate an image. The image is actually
* loaded by calling the 'load_image()' function. Therefore, it returns the
* same error codes if the loading operation failed, or -EAUTH if the
* authentication failed. In addition, this function uses recursion to
* authenticate the parent images up to the root of trust.
******************************************************************************/
int load_auth_image(unsigned int image_id, image_info_t *image_data)
{
int err;
do {
err = load_auth_image_internal(image_id, image_data, 0);
} while (err != 0 && plat_try_next_boot_source());
return err;
}
/*******************************************************************************
* Print the content of an entry_point_info_t structure.
******************************************************************************/
void print_entry_point_info(const entry_point_info_t *ep_info)
{
INFO("Entry point address = %p\n", (void *)ep_info->pc);
INFO("SPSR = 0x%x\n", ep_info->spsr);
#define PRINT_IMAGE_ARG(n) \
VERBOSE("Argument #" #n " = 0x%llx\n", \
(unsigned long long) ep_info->args.arg##n)
PRINT_IMAGE_ARG(0);
PRINT_IMAGE_ARG(1);
PRINT_IMAGE_ARG(2);
PRINT_IMAGE_ARG(3);
#ifndef AARCH32
PRINT_IMAGE_ARG(4);
PRINT_IMAGE_ARG(5);
PRINT_IMAGE_ARG(6);
PRINT_IMAGE_ARG(7);
#endif
#undef PRINT_IMAGE_ARG
}