blob: c3fa882b15ba335d25f56baf41fe623e19a05d3d [file]
/*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
* Copyright 2021, HENSOLDT Cyber
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <autoconf.h>
#include <elfloader/gen_config.h>
#include <printf.h>
#include <types.h>
#include <strops.h>
#include <binaries/elf/elf.h>
#include <cpio/cpio.h>
#include <elfloader.h>
#include <fdt.h>
#ifdef CONFIG_HASH_SHA
#include "crypt_sha256.h"
#elif CONFIG_HASH_MD5
#include "crypt_md5.h"
#endif
#include "hash.h"
#ifdef CONFIG_ELFLOADER_ROOTSERVERS_LAST
#include <platform_info.h> // this provides memory_region
#endif
extern char _bss[];
extern char _bss_end[];
/*
* Clear the BSS segment
*/
void clear_bss(void)
{
char *start = _bss;
char *end = _bss_end;
while (start < end) {
*start = 0;
start++;
}
}
#define KEEP_HEADERS_SIZE BIT(PAGE_BITS)
/*
* Determine if two intervals overlap.
*/
static int regions_overlap(
uintptr_t startA,
uintptr_t endA,
uintptr_t startB,
uintptr_t endB)
{
if (endA < startB) {
return 0;
}
if (endB < startA) {
return 0;
}
return 1;
}
/*
* Ensure that we are able to use the given physical memory range.
*
* We fail if the destination physical range overlaps us, or if it goes outside
* the bounds of memory.
*/
static int ensure_phys_range_valid(
paddr_t paddr_min,
paddr_t paddr_max)
{
/*
* Ensure that the physical load address of the object we're loading (called
* `name`) doesn't overwrite us.
*/
if (regions_overlap(paddr_min,
paddr_max - 1,
(uintptr_t)_text,
(uintptr_t)_end - 1)) {
printf("ERROR: image load address overlaps with ELF-loader!\n");
return -1;
}
return 0;
}
/*
* Unpack an ELF file to the given physical address.
*/
static int unpack_elf_to_paddr(
void const *elf,
paddr_t dest_paddr)
{
int ret;
/* Get the memory bounds. Unlike most other functions, this returns 1 on
* success and anything else is an error.
*/
uint64_t u64_min_vaddr, u64_max_vaddr;
ret = elf_getMemoryBounds(elf, 0, &u64_min_vaddr, &u64_max_vaddr);
if (ret != 1) {
printf("ERROR: Could not get image size\n");
return -1;
}
/* Check that image virtual address range is sane */
if ((u64_min_vaddr > UINTPTR_MAX) || (u64_max_vaddr > UINTPTR_MAX)) {
printf("ERROR: image virtual address [%"PRIu64"..%"PRIu64"] exceeds "
"UINTPTR_MAX (%u)\n",
u64_min_vaddr, u64_max_vaddr, UINTPTR_MAX);
return -1;
}
vaddr_t max_vaddr = (vaddr_t)u64_max_vaddr;
vaddr_t min_vaddr = (vaddr_t)u64_min_vaddr;
size_t image_size = max_vaddr - min_vaddr;
if (dest_paddr + image_size < dest_paddr) {
printf("ERROR: image destination address integer overflow\n");
return -1;
}
/* Zero out all memory in the region, as the ELF file may be sparse. */
memset((void *)dest_paddr, 0, image_size);
/* Load each segment in the ELF file. */
for (unsigned int i = 0; i < elf_getNumProgramHeaders(elf); i++) {
/* Skip segments that are not marked as being loadable. */
if (elf_getProgramHeaderType(elf, i) != PT_LOAD) {
continue;
}
/* Parse size/length headers. */
vaddr_t seg_vaddr = elf_getProgramHeaderVaddr(elf, i);
size_t seg_size = elf_getProgramHeaderFileSize(elf, i);
size_t seg_elf_offset = elf_getProgramHeaderOffset(elf, i);
size_t seg_virt_offset = seg_vaddr - min_vaddr;
paddr_t seg_dest_paddr = dest_paddr + seg_virt_offset;
void const *seg_src_addr = (void const *)((uintptr_t)elf +
seg_elf_offset);
/* Check segment sanity and integer overflows. */
if ((seg_vaddr < min_vaddr) ||
(seg_size > image_size) ||
(seg_src_addr < elf) ||
((uintptr_t)seg_src_addr + seg_size < (uintptr_t)elf) ||
(seg_virt_offset > image_size) ||
(seg_virt_offset + seg_size > image_size) ||
(seg_dest_paddr < dest_paddr) ||
(seg_dest_paddr + seg_size < dest_paddr)) {
printf("ERROR: segement %d invalid\n", i);
return -1;
}
/* Load data into memory. */
memcpy((void *)seg_dest_paddr, seg_src_addr, seg_size);
}
return 0;
}
/*
* Load an ELF file into physical memory at the given physical address.
*
* Returns in 'next_phys_addr' the byte past the last byte of the physical
* address used.
*/
static int load_elf(
void const *cpio,
size_t cpio_len,
const char *name,
void const *elf_blob,
size_t elf_blob_size,
char const *elf_hash_filename,
paddr_t dest_paddr,
int keep_headers,
struct image_info *info,
paddr_t *next_phys_addr)
{
int ret;
uint64_t min_vaddr, max_vaddr;
/* Print diagnostics. */
printf("ELF-loading image '%s' to %p\n", name, dest_paddr);
/* Get the memory bounds. Unlike most other functions, this returns 1 on
* success and anything else is an error.
*/
ret = elf_getMemoryBounds(elf_blob, 0, &min_vaddr, &max_vaddr);
if (ret != 1) {
printf("ERROR: Could not get image bounds\n");
return -1;
}
/* round up size to the end of the page next page */
max_vaddr = ROUND_UP(max_vaddr, PAGE_BITS);
size_t image_size = (size_t)(max_vaddr - min_vaddr);
/* Ensure our starting physical address is aligned. */
if (!IS_ALIGNED(dest_paddr, PAGE_BITS)) {
printf("ERROR: Attempting to load ELF at unaligned physical address\n");
return -1;
}
/* Ensure that the ELF file itself is 4-byte aligned in memory, so that
* libelf can perform word accesses on it. */
if (!IS_ALIGNED(dest_paddr, 2)) {
printf("ERROR: Input ELF file not 4-byte aligned in memory\n");
return -1;
}
#ifdef CONFIG_HASH_NONE
UNUSED_VARIABLE(cpio);
UNUSED_VARIABLE(cpio_len);
UNUSED_VARIABLE(elf_blob_size);
UNUSED_VARIABLE(elf_hash_filename);
#else
/* Get the binary file that contains the Hash */
unsigned long cpio_file_size = 0;
void const *file_hash = cpio_get_file(cpio,
cpio_len,
elf_hash_filename,
&cpio_file_size);
/* If the file hash doesn't have a pointer, the file doesn't exist, so we
* cannot confirm the file is what we expect.
*/
if (file_hash == NULL) {
printf("ERROR: hash file '%s' doesn't exist\n", elf_hash_filename);
return -1;
}
/* Ensure we can safely cast the CPIO API type to our preferred type. */
_Static_assert(sizeof(cpio_file_size) <= sizeof(size_t),
"integer model mismatch");
size_t file_hash_len = (size_t)cpio_file_size;
#ifdef CONFIG_HASH_SHA
uint8_t calculated_hash[32];
hashes_t hashes = { .hash_type = SHA_256 };
#else
uint8_t calculated_hash[16];
hashes_t hashes = { .hash_type = MD5 };
#endif
if (file_hash_len < sizeof(calculated_hash)) {
printf("ERROR: hash file '%s' size %u invalid, expected at least %u\n",
elf_hash_filename, file_hash_len, sizeof(calculated_hash));
}
/* Print the Hash for the user to see */
printf("Hash from ELF File: ");
print_hash(file_hash, sizeof(calculated_hash));
get_hash(hashes, elf_blob, elf_blob_size, calculated_hash);
/* Print the hash so the user can see they're the same or different */
printf("Hash for ELF Input: ");
print_hash(calculated_hash, sizeof(calculated_hash));
/* Check the hashes are the same. There is no memcmp() in the striped down
* runtime lib of ELF Loader, so we compare here byte per byte. */
for (unsigned int i = 0; i < sizeof(calculated_hash); i++) {
if (((char const *)file_hash)[i] != ((char const *)calculated_hash)[i]) {
printf("ERROR: Hashes are different\n");
return -1;
}
}
#endif /* CONFIG_HASH_NONE */
/* Print diagnostics. */
printf(" paddr=[%p..%p]\n", dest_paddr, dest_paddr + image_size - 1);
printf(" vaddr=[%p..%p]\n", (vaddr_t)min_vaddr, (vaddr_t)max_vaddr - 1);
printf(" virt_entry=%p\n", (vaddr_t)elf_getEntryPoint(elf_blob));
/* Ensure the ELF file is valid. */
ret = elf_checkFile(elf_blob);
if (0 != ret) {
printf("ERROR: Invalid ELF file\n");
return -1;
}
/* Ensure sane alignment of the image. */
if (!IS_ALIGNED(min_vaddr, PAGE_BITS)) {
printf("ERROR: Start of image is not 4K-aligned\n");
return -1;
}
/* Ensure that we region we want to write to is sane. */
ret = ensure_phys_range_valid(dest_paddr, dest_paddr + image_size);
if (0 != ret) {
printf("ERROR: Physical address range invalid\n");
return -1;
}
/* Copy the data. */
ret = unpack_elf_to_paddr(elf_blob, dest_paddr);
if (0 != ret) {
printf("ERROR: Unpacking ELF to %p failed\n", dest_paddr);
return -1;
}
/* Record information about the placement of the image. */
info->phys_region_start = dest_paddr;
info->phys_region_end = dest_paddr + image_size;
info->virt_region_start = (vaddr_t)min_vaddr;
info->virt_region_end = (vaddr_t)max_vaddr;
info->virt_entry = (vaddr_t)elf_getEntryPoint(elf_blob);
info->phys_virt_offset = dest_paddr - (vaddr_t)min_vaddr;
/* Round up the destination address to the next page */
dest_paddr = ROUND_UP(dest_paddr + image_size, PAGE_BITS);
if (keep_headers) {
/* Put the ELF headers in this page */
uint32_t phnum = elf_getNumProgramHeaders(elf_blob);
uint32_t phsize;
paddr_t source_paddr;
if (ISELF32(elf_blob)) {
phsize = ((struct Elf32_Header const *)elf_blob)->e_phentsize;
source_paddr = (paddr_t)elf32_getProgramHeaderTable(elf_blob);
} else {
phsize = ((struct Elf64_Header const *)elf_blob)->e_phentsize;
source_paddr = (paddr_t)elf64_getProgramHeaderTable(elf_blob);
}
/* We have no way of sharing definitions with the kernel so we just
* memcpy to a bunch of magic offsets. Explicit numbers for sizes
* and offsets are used so that it is clear exactly what the layout
* is */
memcpy((void *)dest_paddr, &phnum, 4);
memcpy((void *)(dest_paddr + 4), &phsize, 4);
memcpy((void *)(dest_paddr + 8), (void *)source_paddr, phsize * phnum);
/* return the frame after our headers */
dest_paddr += KEEP_HEADERS_SIZE;
}
if (next_phys_addr) {
*next_phys_addr = dest_paddr;
}
return 0;
}
/*
* ELF-loader for ARM systems.
*
* We are currently running out of physical memory, with an ELF file for the
* kernel and one or more ELF files for the userspace image. (Typically there
* will only be one userspace ELF file, though if we are running a multi-core
* CPU, we may have multiple userspace images; one per CPU.) These ELF files
* are packed into an 'ar' archive.
*
* The kernel ELF file indicates what physical address it wants to be loaded
* at, while userspace images run out of virtual memory, so don't have any
* requirements about where they are located. We place the kernel at its
* desired location, and then load userspace images straight after it in
* physical memory.
*
* Several things could possibly go wrong:
*
* 1. The physical load address of the kernel might want to overwrite this
* ELF-loader;
*
* 2. The physical load addresses of the kernel might not actually be in
* physical memory;
*
* 3. Userspace images may not fit in physical memory, or may try to overlap
* the ELF-loader.
*
* We attempt to check for some of these, but some may go unnoticed.
*/
int load_images(
struct image_info *kernel_info,
struct image_info *user_info,
unsigned int max_user_images,
unsigned int *num_images,
void const *bootloader_dtb,
void const **chosen_dtb,
size_t *chosen_dtb_size)
{
int ret;
uint64_t kernel_phys_start, kernel_phys_end;
uintptr_t dtb_phys_start, dtb_phys_end;
paddr_t next_phys_addr;
const char *elf_filename;
int has_dtb_cpio = 0;
void const *cpio = _archive_start;
size_t cpio_len = _archive_start_end - _archive_start;
/* Load kernel. */
unsigned long cpio_file_size = 0;
void const *kernel_elf_blob = cpio_get_file(cpio,
cpio_len,
"kernel.elf",
&cpio_file_size);
if (kernel_elf_blob == NULL) {
printf("ERROR: No kernel image present in archive\n");
return -1;
}
/* Ensure we can safely cast the CPIO API type to our preferred type. */
_Static_assert(sizeof(cpio_file_size) <= sizeof(size_t),
"integer model mismatch");
size_t kernel_elf_blob_size = (size_t)cpio_file_size;
ret = elf_checkFile(kernel_elf_blob);
if (ret != 0) {
printf("ERROR: Kernel image not a valid ELF file\n");
return -1;
}
/* Get physical memory bounds. Unlike most other functions, this returns 1
* on success and anything else is an error.
*/
ret = elf_getMemoryBounds(kernel_elf_blob, 1, &kernel_phys_start,
&kernel_phys_end);
if (1 != ret) {
printf("ERROR: Could not get kernel memory bounds\n");
return -1;
}
void const *dtb = NULL;
#ifdef CONFIG_ELFLOADER_INCLUDE_DTB
if (chosen_dtb) {
printf("Looking for DTB in CPIO archive...");
/*
* Note the lack of newline in the above printf(). Normally one would
* have an fflush(stdout) here to ensure that the message shows up on a
* line-buffered stream (which is the POSIX default on terminal
* devices). But we are freestanding (on the "bare metal"), and using
* our own unbuffered printf() implementation.
*/
dtb = cpio_get_file(cpio, cpio_len, "kernel.dtb", NULL);
if (dtb == NULL) {
printf("not found.\n");
} else {
has_dtb_cpio = 1;
printf("found at %p.\n", dtb);
}
}
#endif /* CONFIG_ELFLOADER_INCLUDE_DTB */
if (chosen_dtb && !dtb && bootloader_dtb) {
/* Use the bootloader's DTB if we are not using the DTB in the CPIO
* archive.
*/
dtb = bootloader_dtb;
}
/*
* Move the DTB out of the way, if it's present.
*/
if (dtb) {
/* keep it page aligned */
next_phys_addr = dtb_phys_start = ROUND_UP(kernel_phys_end, PAGE_BITS);
size_t dtb_size = fdt_size(dtb);
if (0 == dtb_size) {
printf("ERROR: Invalid device tree blob supplied\n");
return -1;
}
/* Make sure this is a sane thing to do */
ret = ensure_phys_range_valid(next_phys_addr,
next_phys_addr + dtb_size);
if (0 != ret) {
printf("ERROR: Physical address of DTB invalid\n");
return -1;
}
memmove((void *)next_phys_addr, dtb, dtb_size);
next_phys_addr += dtb_size;
next_phys_addr = ROUND_UP(next_phys_addr, PAGE_BITS);
dtb_phys_end = next_phys_addr;
printf("Loaded DTB from %p.\n", dtb);
printf(" paddr=[%p..%p]\n", dtb_phys_start, dtb_phys_end - 1);
*chosen_dtb = (void *)dtb_phys_start;
*chosen_dtb_size = dtb_size;
} else {
next_phys_addr = ROUND_UP(kernel_phys_end, PAGE_BITS);
}
/* Load the kernel */
ret = load_elf(cpio,
cpio_len,
"kernel",
kernel_elf_blob,
kernel_elf_blob_size,
"kernel.bin", // hash file
(paddr_t)kernel_phys_start,
0, // don't keep ELF headers
kernel_info,
NULL); // we have calculated next_phys_addr already
if (0 != ret) {
printf("ERROR: Could not load kernel ELF\n");
return -1;
}
/*
* Load userspace images.
*
* We assume (and check) that the kernel is the first file in the archive,
* that the DTB is the second if present,
* and then load the (n+user_elf_offset)'th file in the archive onto the
* (n)'th CPU.
*/
unsigned int user_elf_offset = 2;
cpio_get_entry(cpio, cpio_len, 0, &elf_filename, NULL);
ret = strcmp(elf_filename, "kernel.elf");
if (0 != ret) {
printf("ERROR: Kernel image not first image in archive\n");
return -1;
}
cpio_get_entry(cpio, cpio_len, 1, &elf_filename, NULL);
ret = strcmp(elf_filename, "kernel.dtb");
if (0 != ret) {
if (has_dtb_cpio) {
printf("ERROR: Kernel DTB not second image in archive\n");
return -1;
}
user_elf_offset = 1;
}
#ifdef CONFIG_ELFLOADER_ROOTSERVERS_LAST
/* work out the size of the user images - this corresponds to how much
* memory load_elf uses */
unsigned int total_user_image_size = 0;
for (unsigned int i = 0; i < max_user_images; i++) {
void const *user_elf = cpio_get_entry(cpio,
cpio_len,
i + user_elf_offset,
NULL,
NULL);
if (user_elf == NULL) {
break;
}
/* Get the memory bounds. Unlike most other functions, this returns 1 on
* success and anything else is an error.
*/
uint64_t min_vaddr, max_vaddr;
int ret = elf_getMemoryBounds(user_elf, 0, &min_vaddr, &max_vaddr);
if (ret != 1) {
printf("ERROR: Could not get image bounds\n");
return -1;
}
/* round up size to the end of the page next page */
total_user_image_size += (ROUND_UP(max_vaddr, PAGE_BITS) - min_vaddr)
+ KEEP_HEADERS_SIZE;
}
/* work out where to place the user image */
next_phys_addr = ROUND_DOWN(memory_region[0].end, PAGE_BITS)
- ROUND_UP(total_user_image_size, PAGE_BITS);
#endif /* CONFIG_ELFLOADER_ROOTSERVERS_LAST */
*num_images = 0;
for (unsigned int i = 0; i < max_user_images; i++) {
/* Fetch info about the next ELF file in the archive. */
unsigned long cpio_file_size = 0;
void const *user_elf = cpio_get_entry(cpio,
cpio_len,
i + user_elf_offset,
&elf_filename,
&cpio_file_size);
if (user_elf == NULL) {
break;
}
/* Ensure we can safely cast the CPIO API type to our preferred type. */
_Static_assert(sizeof(cpio_file_size) <= sizeof(size_t),
"integer model mismatch");
size_t elf_filesize = (size_t)cpio_file_size;
/* Load the file into memory. */
ret = load_elf(cpio,
cpio_len,
elf_filename,
user_elf,
elf_filesize,
"app.bin", // hash file
next_phys_addr,
1, // keep ELF headers
&user_info[*num_images],
&next_phys_addr);
if (0 != ret) {
printf("ERROR: Could not load user image ELF\n");
}
*num_images = i + 1;
}
return 0;
}
/*
* Platform specific ELF Loader initialization. Can be overwritten.
*/
WEAK void platform_init(void)
{
/* nothing by default */
}