Re: [PATCH] efi/libstub: Move efi_relocate_kernel() into its only remaining user
WANG Rui <[email protected]>
| Newsgroups | org.kernel.vger.linux-efi |
|---|---|
| Message-ID | <CAHirt9gOEJhbHjgmsRezzx7AuyKGDUky-2mAOv015uCKyOZ7jw@mail.gmail.com> |
Hi Ard, On Wed, Apr 29, 2026 at 1:06 AM Ard Biesheuvel <[email protected]> wrote: > > From: Ard Biesheuvel <[email protected]> > > LoongArch is the only arch that still uses efi_relocate_kernel(), so > before making changes to it that LoongArch needs, turn it into a private > function. Move efi_low_alloc_above() into mem.c while at it, and drop > the relocate.c source file altogether. > > Cc: WANG Rui <[email protected]> > Cc: Huacai Chen <[email protected]> > Signed-off-by: Ard Biesheuvel <[email protected]> Tested-by: WANG Rui <[email protected]> Thanks, Rui > --- > Unless there are any objections, I will queue this up and rebase the > LoongArch changes adding efi_cache_sync_image() on top of it. > > drivers/firmware/efi/libstub/Makefile | 2 +- > drivers/firmware/efi/libstub/efistub.h | 7 - > drivers/firmware/efi/libstub/loongarch-stub.c | 79 ++++++++++ > drivers/firmware/efi/libstub/mem.c | 82 ++++++++++ > drivers/firmware/efi/libstub/relocate.c | 166 -------------------- > 5 files changed, 162 insertions(+), 174 deletions(-) > > diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile > index 983a438e35f3..cfedb3025c26 100644 > --- a/drivers/firmware/efi/libstub/Makefile > +++ b/drivers/firmware/efi/libstub/Makefile > @@ -66,7 +66,7 @@ KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ > lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ > file.o mem.o random.o randomalloc.o pci.o \ > skip_spaces.o lib-cmdline.o lib-ctype.o \ > - alignedmem.o relocate.o printk.o vsprintf.o > + alignedmem.o printk.o vsprintf.o > > # include the stub's libfdt dependencies from lib/ when needed > libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \ > diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h > index 979a21818cc1..fd91fc15ec81 100644 > --- a/drivers/firmware/efi/libstub/efistub.h > +++ b/drivers/firmware/efi/libstub/efistub.h > @@ -1104,13 +1104,6 @@ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, > efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, > unsigned long *addr, unsigned long min); > > -efi_status_t efi_relocate_kernel(unsigned long *image_addr, > - unsigned long image_size, > - unsigned long alloc_size, > - unsigned long preferred_addr, > - unsigned long alignment, > - unsigned long min_addr); > - > efi_status_t efi_parse_options(char const *cmdline); > > void efi_parse_option_graphics(char *option); > diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c > index 736b6aae323d..8c538a5243d9 100644 > --- a/drivers/firmware/efi/libstub/loongarch-stub.c > +++ b/drivers/firmware/efi/libstub/loongarch-stub.c > @@ -14,6 +14,85 @@ extern int kernel_asize; > extern int kernel_fsize; > extern int kernel_entry; > > +/** > + * efi_relocate_kernel() - copy memory area > + * @image_addr: pointer to address of memory area to copy > + * @image_size: size of memory area to copy > + * @alloc_size: minimum size of memory to allocate, must be greater or > + * equal to image_size > + * @preferred_addr: preferred target address > + * @alignment: minimum alignment of the allocated memory area. It > + * should be a power of two. > + * @min_addr: minimum target address > + * > + * Copy a memory area to a newly allocated memory area aligned according > + * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address > + * is not available, the allocated address will not be below @min_addr. > + * On exit, @image_addr is updated to the target copy address that was used. > + * > + * This function is used to copy the Linux kernel verbatim. It does not apply > + * any relocation changes. > + * > + * Return: status code > + */ > +static > +efi_status_t efi_relocate_kernel(unsigned long *image_addr, > + unsigned long image_size, > + unsigned long alloc_size, > + unsigned long preferred_addr, > + unsigned long alignment, > + unsigned long min_addr) > +{ > + unsigned long cur_image_addr; > + unsigned long new_addr = 0; > + efi_status_t status; > + unsigned long nr_pages; > + efi_physical_addr_t efi_addr = preferred_addr; > + > + if (!image_addr || !image_size || !alloc_size) > + return EFI_INVALID_PARAMETER; > + if (alloc_size < image_size) > + return EFI_INVALID_PARAMETER; > + > + cur_image_addr = *image_addr; > + > + /* > + * The EFI firmware loader could have placed the kernel image > + * anywhere in memory, but the kernel has restrictions on the > + * max physical address it can run at. Some architectures > + * also have a preferred address, so first try to relocate > + * to the preferred address. If that fails, allocate as low > + * as possible while respecting the required alignment. > + */ > + nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; > + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, > + EFI_LOADER_DATA, nr_pages, &efi_addr); > + new_addr = efi_addr; > + /* > + * If preferred address allocation failed allocate as low as > + * possible. > + */ > + if (status != EFI_SUCCESS) { > + status = efi_low_alloc_above(alloc_size, alignment, &new_addr, > + min_addr); > + } > + if (status != EFI_SUCCESS) { > + efi_err("Failed to allocate usable memory for kernel.\n"); > + return status; > + } > + > + /* > + * We know source/dest won't overlap since both memory ranges > + * have been allocated by UEFI, so we can safely use memcpy. > + */ > + memcpy((void *)new_addr, (void *)cur_image_addr, image_size); > + > + /* Return the new address of the relocated image. */ > + *image_addr = new_addr; > + > + return status; > +} > + > efi_status_t handle_kernel_image(unsigned long *image_addr, > unsigned long *image_size, > unsigned long *reserve_addr, > diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c > index 9c82259eea81..59f3f83de50c 100644 > --- a/drivers/firmware/efi/libstub/mem.c > +++ b/drivers/firmware/efi/libstub/mem.c > @@ -124,3 +124,85 @@ void efi_free(unsigned long size, unsigned long addr) > nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; > efi_bs_call(free_pages, addr, nr_pages); > } > + > +/** > + * efi_low_alloc_above() - allocate pages at or above given address > + * @size: size of the memory area to allocate > + * @align: minimum alignment of the allocated memory area. It should > + * a power of two. > + * @addr: on exit the address of the allocated memory > + * @min: minimum address to used for the memory allocation > + * > + * Allocate at the lowest possible address that is not below @min as > + * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at > + * least EFI_ALLOC_ALIGN. The first allocated page will not below the address > + * given by @min. > + * > + * Return: status code > + */ > +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, > + unsigned long *addr, unsigned long min) > +{ > + struct efi_boot_memmap *map __free(efi_pool) = NULL; > + efi_status_t status; > + unsigned long nr_pages; > + int i; > + > + status = efi_get_memory_map(&map, false); > + if (status != EFI_SUCCESS) > + return status; > + > + /* > + * Enforce minimum alignment that EFI or Linux requires when > + * requesting a specific address. We are doing page-based (or > + * larger) allocations, and both the address and size must meet > + * alignment constraints. > + */ > + if (align < EFI_ALLOC_ALIGN) > + align = EFI_ALLOC_ALIGN; > + > + size = round_up(size, EFI_ALLOC_ALIGN); > + nr_pages = size / EFI_PAGE_SIZE; > + for (i = 0; i < map->map_size / map->desc_size; i++) { > + efi_memory_desc_t *desc; > + unsigned long m = (unsigned long)map->map; > + u64 start, end; > + > + desc = efi_memdesc_ptr(m, map->desc_size, i); > + > + if (desc->type != EFI_CONVENTIONAL_MEMORY) > + continue; > + > + if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE) > + continue; > + > + if (efi_soft_reserve_enabled() && > + (desc->attribute & EFI_MEMORY_SP)) > + continue; > + > + if (desc->num_pages < nr_pages) > + continue; > + > + start = desc->phys_addr; > + end = start + desc->num_pages * EFI_PAGE_SIZE; > + > + if (start < min) > + start = min; > + > + start = round_up(start, align); > + if ((start + size) > end) > + continue; > + > + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, > + EFI_LOADER_DATA, nr_pages, &start); > + if (status == EFI_SUCCESS) { > + *addr = start; > + break; > + } > + } > + > + if (i == map->map_size / map->desc_size) > + return EFI_NOT_FOUND; > + > + return EFI_SUCCESS; > +} > diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c > deleted file mode 100644 > index d4264bfb6dc1..000000000000 > --- a/drivers/firmware/efi/libstub/relocate.c > +++ /dev/null > @@ -1,166 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0 > - > -#include <linux/efi.h> > -#include <asm/efi.h> > - > -#include "efistub.h" > - > -/** > - * efi_low_alloc_above() - allocate pages at or above given address > - * @size: size of the memory area to allocate > - * @align: minimum alignment of the allocated memory area. It should > - * a power of two. > - * @addr: on exit the address of the allocated memory > - * @min: minimum address to used for the memory allocation > - * > - * Allocate at the lowest possible address that is not below @min as > - * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at > - * least EFI_ALLOC_ALIGN. The first allocated page will not below the address > - * given by @min. > - * > - * Return: status code > - */ > -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, > - unsigned long *addr, unsigned long min) > -{ > - struct efi_boot_memmap *map __free(efi_pool) = NULL; > - efi_status_t status; > - unsigned long nr_pages; > - int i; > - > - status = efi_get_memory_map(&map, false); > - if (status != EFI_SUCCESS) > - return status; > - > - /* > - * Enforce minimum alignment that EFI or Linux requires when > - * requesting a specific address. We are doing page-based (or > - * larger) allocations, and both the address and size must meet > - * alignment constraints. > - */ > - if (align < EFI_ALLOC_ALIGN) > - align = EFI_ALLOC_ALIGN; > - > - size = round_up(size, EFI_ALLOC_ALIGN); > - nr_pages = size / EFI_PAGE_SIZE; > - for (i = 0; i < map->map_size / map->desc_size; i++) { > - efi_memory_desc_t *desc; > - unsigned long m = (unsigned long)map->map; > - u64 start, end; > - > - desc = efi_memdesc_ptr(m, map->desc_size, i); > - > - if (desc->type != EFI_CONVENTIONAL_MEMORY) > - continue; > - > - if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE) > - continue; > - > - if (efi_soft_reserve_enabled() && > - (desc->attribute & EFI_MEMORY_SP)) > - continue; > - > - if (desc->num_pages < nr_pages) > - continue; > - > - start = desc->phys_addr; > - end = start + desc->num_pages * EFI_PAGE_SIZE; > - > - if (start < min) > - start = min; > - > - start = round_up(start, align); > - if ((start + size) > end) > - continue; > - > - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, > - EFI_LOADER_DATA, nr_pages, &start); > - if (status == EFI_SUCCESS) { > - *addr = start; > - break; > - } > - } > - > - if (i == map->map_size / map->desc_size) > - return EFI_NOT_FOUND; > - > - return EFI_SUCCESS; > -} > - > -/** > - * efi_relocate_kernel() - copy memory area > - * @image_addr: pointer to address of memory area to copy > - * @image_size: size of memory area to copy > - * @alloc_size: minimum size of memory to allocate, must be greater or > - * equal to image_size > - * @preferred_addr: preferred target address > - * @alignment: minimum alignment of the allocated memory area. It > - * should be a power of two. > - * @min_addr: minimum target address > - * > - * Copy a memory area to a newly allocated memory area aligned according > - * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address > - * is not available, the allocated address will not be below @min_addr. > - * On exit, @image_addr is updated to the target copy address that was used. > - * > - * This function is used to copy the Linux kernel verbatim. It does not apply > - * any relocation changes. > - * > - * Return: status code > - */ > -efi_status_t efi_relocate_kernel(unsigned long *image_addr, > - unsigned long image_size, > - unsigned long alloc_size, > - unsigned long preferred_addr, > - unsigned long alignment, > - unsigned long min_addr) > -{ > - unsigned long cur_image_addr; > - unsigned long new_addr = 0; > - efi_status_t status; > - unsigned long nr_pages; > - efi_physical_addr_t efi_addr = preferred_addr; > - > - if (!image_addr || !image_size || !alloc_size) > - return EFI_INVALID_PARAMETER; > - if (alloc_size < image_size) > - return EFI_INVALID_PARAMETER; > - > - cur_image_addr = *image_addr; > - > - /* > - * The EFI firmware loader could have placed the kernel image > - * anywhere in memory, but the kernel has restrictions on the > - * max physical address it can run at. Some architectures > - * also have a preferred address, so first try to relocate > - * to the preferred address. If that fails, allocate as low > - * as possible while respecting the required alignment. > - */ > - nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; > - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, > - EFI_LOADER_DATA, nr_pages, &efi_addr); > - new_addr = efi_addr; > - /* > - * If preferred address allocation failed allocate as low as > - * possible. > - */ > - if (status != EFI_SUCCESS) { > - status = efi_low_alloc_above(alloc_size, alignment, &new_addr, > - min_addr); > - } > - if (status != EFI_SUCCESS) { > - efi_err("Failed to allocate usable memory for kernel.\n"); > - return status; > - } > - > - /* > - * We know source/dest won't overlap since both memory ranges > - * have been allocated by UEFI, so we can safely use memcpy. > - */ > - memcpy((void *)new_addr, (void *)cur_image_addr, image_size); > - > - /* Return the new address of the relocated image. */ > - *image_addr = new_addr; > - > - return status; > -} > -- > 2.54.0.545.g6539524ca2-goog >