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