124 lines
3.2 KiB
C
124 lines
3.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include <commonlib/bsd/compression.h>
|
|
#include <console/console.h>
|
|
#include <bootmem.h>
|
|
#include <program_loading.h>
|
|
#include <fit.h>
|
|
#include <endian.h>
|
|
|
|
/* Implements a Berkeley Boot Loader (BBL) compatible payload loading */
|
|
|
|
#define MAX_KERNEL_SIZE (64*MiB)
|
|
|
|
#if CONFIG(ARCH_RISCV_RV32)
|
|
#define SECTION_ALIGN (4 * MiB)
|
|
#endif
|
|
#if CONFIG(ARCH_RISCV_RV64)
|
|
#define SECTION_ALIGN (2 * MiB)
|
|
#endif
|
|
|
|
static size_t get_kernel_size(const struct fit_image_node *node)
|
|
{
|
|
/*
|
|
* Since we don't have a way to determine the uncompressed size of the
|
|
* kernel, we have to keep as much memory as possible free for use by
|
|
* the kernel immediately after the end of the kernel image. The amount
|
|
* of space required will vary depending on selected features, and is
|
|
* effectively unbound.
|
|
*/
|
|
|
|
printk(BIOS_INFO,
|
|
"FIT: Leaving additional %u MiB of free space after kernel.\n",
|
|
MAX_KERNEL_SIZE >> 20);
|
|
|
|
return node->size + MAX_KERNEL_SIZE;
|
|
}
|
|
|
|
/**
|
|
* Place the region in free memory range.
|
|
*
|
|
* The caller has to set region->offset to the minimum allowed address.
|
|
*/
|
|
static bool fit_place_mem(const struct range_entry *r, void *arg)
|
|
{
|
|
struct region *region = arg;
|
|
resource_t start;
|
|
|
|
if (range_entry_tag(r) != BM_MEM_RAM)
|
|
return true;
|
|
|
|
/* Section must be aligned at page boundary */
|
|
start = ALIGN_UP(MAX(region->offset, range_entry_base(r)), SECTION_ALIGN);
|
|
|
|
if (start + region->size < range_entry_end(r)) {
|
|
region->offset = (size_t)start;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool fit_payload_arch(struct prog *payload, struct fit_config_node *config,
|
|
struct region *kernel,
|
|
struct region *fdt,
|
|
struct region *initrd)
|
|
{
|
|
void *arg = NULL;
|
|
|
|
if (!config->fdt || !fdt) {
|
|
printk(BIOS_CRIT, "CRIT: Providing a valid FDT is mandatory to "
|
|
"boot a RISC-V kernel!\n");
|
|
return false;
|
|
/* TODO: Fall back to the ROM FDT? */
|
|
}
|
|
|
|
/* Update kernel size from image header, if possible */
|
|
kernel->size = get_kernel_size(config->kernel);
|
|
printk(BIOS_DEBUG, "FIT: Using kernel size of 0x%zx bytes\n",
|
|
kernel->size);
|
|
|
|
/*
|
|
* The code assumes that bootmem_walk provides a sorted list of memory
|
|
* regions, starting from the lowest address.
|
|
* The order of the calls here doesn't matter, as the placement is
|
|
* enforced in the called functions.
|
|
* For details check code on top.
|
|
*/
|
|
kernel->offset = 0;
|
|
if (!bootmem_walk(fit_place_mem, kernel))
|
|
return false;
|
|
|
|
/* Mark as reserved for future allocations. */
|
|
bootmem_add_range(kernel->offset, kernel->size, BM_MEM_PAYLOAD);
|
|
|
|
/* Place FDT and INITRD after kernel. */
|
|
|
|
/* Place INITRD */
|
|
if (config->ramdisk) {
|
|
initrd->offset = kernel->offset + kernel->size;
|
|
|
|
if (!bootmem_walk(fit_place_mem, initrd))
|
|
return false;
|
|
/* Mark as reserved for future allocations. */
|
|
bootmem_add_range(initrd->offset, initrd->size, BM_MEM_PAYLOAD);
|
|
}
|
|
|
|
/* Place FDT */
|
|
fdt->offset = kernel->offset + kernel->size;
|
|
|
|
if (!bootmem_walk(fit_place_mem, fdt))
|
|
return false;
|
|
/* Mark as reserved for future allocations. */
|
|
bootmem_add_range(fdt->offset, fdt->size, BM_MEM_PAYLOAD);
|
|
|
|
/* Kernel expects FDT as argument */
|
|
arg = (void *)fdt->offset;
|
|
|
|
prog_set_entry(payload, (void *)kernel->offset, arg);
|
|
|
|
bootmem_dump_ranges();
|
|
|
|
return true;
|
|
}
|