/* SPDX-License-Identifier: GPL-2.0-or-later */ #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; }