Skip to content

Commit

Permalink
elfloader: factor out load_dtb()
Browse files Browse the repository at this point in the history
Also clean up the code and improve comments.

Signed-off-by: Axel Heider <[email protected]>
  • Loading branch information
Axel Heider committed Oct 24, 2023
1 parent 6523a2b commit 0e1ffee
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 66 deletions.
4 changes: 4 additions & 0 deletions elfloader-tool/include/elfloader_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#pragma once

#include <stdbool.h>
#include <types.h>

typedef uintptr_t paddr_t;
Expand Down Expand Up @@ -41,6 +42,9 @@ typedef uintptr_t vaddr_t;
typedef struct {
paddr_t phys_base;
size_t size;
#ifdef CONFIG_ELFLOADER_INCLUDE_DTB
bool is_from_cpio;
#endif
} dtb_info_t;

/*
Expand Down
185 changes: 119 additions & 66 deletions elfloader-tool/src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,109 @@ static int load_elf(
return 0;
}

/*
* Load the DTB
*/
static int load_dtb(
void const *cpio,
size_t cpio_len,
paddr_t dtb_load_phys,
dtb_info_t *dtb_info)
{
int ret;

void const *dtb = NULL;

#ifdef CONFIG_ELFLOADER_INCLUDE_DTB

/* A DTB present in the CPIO archive takes preference over a DTB passed
* from the bootloder.
*/

printf("Looking for DTB in CPIO archive...");
/* Note the lack of newline in the above printf(). Normally, fflush(stdout)
* must be called to ensure that the message shows up on a line-buffered
* stream, which is the POSIX default on terminal devices). However, we are
* freestanding (on the "bare metal"), and use our own unbuffered printf()
* implementation.
*/

unsigned long cpio_dtb_size = 0;
dtb = cpio_get_file(cpio, cpio_len, "kernel.dtb", &cpio_dtb_size);
if (!dtb) {
printf("not found.\n");
} else {
printf("found at %p.\n", dtb);
if (dtb_info) {
dtb_info->is_from_cpio = true;
}
}

#endif /* CONFIG_ELFLOADER_INCLUDE_DTB */

/* If we don't have a DTB here, use the one a bootloader might have
* provided. Since 0 is a valid physical address, the size field is used to
* determin if the address is valid. A size of -1 indicates, that the actual
* size is not known - which is usually the case, because a bootloader often
* just passes an address.
*/
if (!dtb) {
if (0 == dtb_info->size) {
/* Not having a DTB is not an error. With dtb_info->size still set
* to zero, the caller can find out that no DTB was loaded and then
* act accordingly.
*/
printf("No DTB available\n");
dtb_info->phys_base = 0;
return 0;
}

printf("Loading DTB passed from bootloader at %p\n",
dtb_info->phys_base);

dtb = (void const *)(dtb_info->phys_base);
}

size_t dtb_size = fdt_size((void *)dtb);
if (0 == dtb_size) {
printf("ERROR: Invalid device tree blob supplied\n");
return -1;
}

#ifdef CONFIG_ELFLOADER_INCLUDE_DTB

if (dtb_info->is_from_cpio && (dtb_size > cpio_dtb_size)) {
printf("ERROR: parsed device tree is larger (%zu byte) than CPIO file (%zu byte)\n",
dtb_size, cpio_dtb_size);
return -1;
}

#endif /* CONFIG_ELFLOADER_INCLUDE_DTB */

/* Move the DTB out of the way. Check that the physical destination
* location is sane.
*/
paddr_t dtb_phys_end = dtb_load_phys + dtb_size;
ret = ensure_phys_range_valid(dtb_load_phys, dtb_phys_end);
if (0 != ret) {
printf("ERROR: Physical target address of DTB is invalid\n");
return -1;
}

memmove((void *)dtb_load_phys, dtb, dtb_size);

printf("Loaded DTB from %p.\n", dtb);
printf(" paddr=[%p..%p]\n", dtb_load_phys, dtb_phys_end - 1);

/* Set DTB values for caller. */
if (dtb_info) {
dtb_info->phys_base = dtb_load_phys;
dtb_info->size = dtb_size;
}

return 0;
}

/*
* ELF-loader for ARM systems.
*
Expand Down Expand Up @@ -388,10 +491,7 @@ int load_images(
{
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;
Expand Down Expand Up @@ -428,72 +528,23 @@ int load_images(
return -1;
}

void const *dtb = NULL;

#ifdef CONFIG_ELFLOADER_INCLUDE_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.
/* Load the DTB first, because this allows extracting further platform
* information from it, which may affect the system setup. The DTB is put
* after the kernel image, because this ensures it is in a place well
* aligned with our memory usage.
*/
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 we don't have a DTB here, use the one a bootloader might have
* provided. Since 0 is a valid physical address, the size field is used to
* determin if the address is valid. A size of -1 indicates, that the actual
* size is not known - which is usually the case, because a bootloader often
* just passes an address.
*/
if (!dtb && (dtb_info->size > 0)) {
dtb = (void const *)dtb_info->phys_base;
paddr_t next_phys_addr = ROUND_UP(kernel_phys_end, PAGE_BITS);
ret = load_dtb(cpio, cpio_len, next_phys_addr, dtb_info);
if (ret != 0) {
printf("ERROR: Could not load DTB\n");
return -1;
}

/*
* Move the DTB out of the way, if it's present.
/* It's not an error here if no DTB was loaded. Eventually, the system that
* we are loading has to decide if it can handle this.
*/
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);
dtb_info->phys_base = dtb_phys_start;
dtb_info->size = dtb_size;
} else {
dtb_info->phys_base = 0;
dtb_info->size = 0;
next_phys_addr = ROUND_UP(kernel_phys_end, PAGE_BITS);
if (dtb_info->size > 0) {
next_phys_addr = ROUND_UP(next_phys_addr + dtb_info->size, PAGE_BITS);
}

/* Load the kernel */
Expand Down Expand Up @@ -531,10 +582,12 @@ int load_images(
cpio_get_entry(cpio, cpio_len, 1, &elf_filename, NULL);
ret = strcmp(elf_filename, "kernel.dtb");
if (0 != ret) {
if (has_dtb_cpio) {
#ifdef CONFIG_ELFLOADER_INCLUDE_DTB
if (dtb_info->is_from_cpio) {
printf("ERROR: Kernel DTB not second image in archive\n");
return -1;
}
#endif
user_elf_offset = 1;
}

Expand Down

0 comments on commit 0e1ffee

Please sign in to comment.