diff options
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/arm/Makefile.inc | 1 | ||||
-rw-r--r-- | src/arch/arm/fit_payload.c | 90 |
2 files changed, 91 insertions, 0 deletions
diff --git a/src/arch/arm/Makefile.inc b/src/arch/arm/Makefile.inc index eef2650685..241bfe5051 100644 --- a/src/arch/arm/Makefile.inc +++ b/src/arch/arm/Makefile.inc @@ -119,6 +119,7 @@ ramstage-y += memset.S ramstage-y += memcpy.S ramstage-y += memmove.S ramstage-y += clock.c +ramstage-$(CONFIG_PAYLOAD_FIT_SUPPORT) += fit_payload.c rmodules_arm-y += memset.S rmodules_arm-y += memcpy.S diff --git a/src/arch/arm/fit_payload.c b/src/arch/arm/fit_payload.c new file mode 100644 index 0000000000..f5470071d4 --- /dev/null +++ b/src/arch/arm/fit_payload.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <console/console.h> +#include <bootmem.h> +#include <program_loading.h> +#include <fit.h> +#include <symbols.h> + +/** + * Place the region in free memory range. + */ +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; + + /* Linux 4.15 doesn't like 4KiB alignment. Align to 1 MiB for now. */ + start = ALIGN_UP(MAX(region->offset, range_entry_base(r)), 1 * MiB); + + 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; + + /** + * The kernel ARM documentation recommends loading the kernel above 32MiB + * in order to avoid the need to need to relocate prior to decompression. + */ + kernel->offset = (uintptr_t)_dram + 32 * MiB; + + /** + * 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. + */ + 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); + + /** + * To ensure the fdt is not overwritten by the kernel decompressor, place + * the fdt above the 128 MB from the start of RAM, as recommended by the + * kernel documentation. + */ + fdt->offset = (uintptr_t)_dram + 128 * MiB; + + 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); + + /* Place INITRD */ + if (config->ramdisk) { + initrd->offset = fdt->offset + fdt->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); + } + + /* Kernel expects FDT as argument */ + arg = (void *)fdt->offset; + + prog_set_entry(payload, (void *)kernel->offset, arg); + + bootmem_dump_ranges(); + + return true; +} |