On Fri, Jul 24, 2015 at 12:38:27PM +0100, Ard Biesheuvel wrote: > When allocating memory for the kernel image, try the AllocatePages() > boot service to obtain memory at the preferred offset of > 'dram_base + TEXT_OFFSET', and only revert to efi_low_alloc() if that > fails. This is the only way to allocate at the base of DRAM if DRAM > starts at 0x0, since efi_low_alloc() refuses to allocate at 0x0. > > Tested-by: Haojian Zhuang <haojian.zhuang@xxxxxxxxxx> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx> Reviewed-by: Mark Rutland <mark.rutland@xxxxxxx> Mark. > --- > v2: > - reshuffle code flow to make it more logical, and have only a single > memcpy() invocation at the end of the function > --- > arch/arm64/kernel/efi-stub.c | 41 ++++++++++++++++++++++++++++++++--------- > 1 file changed, 32 insertions(+), 9 deletions(-) > > diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c > index f5374065ad53..816120ece6bc 100644 > --- a/arch/arm64/kernel/efi-stub.c > +++ b/arch/arm64/kernel/efi-stub.c > @@ -13,7 +13,7 @@ > #include <asm/efi.h> > #include <asm/sections.h> > > -efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table, > +efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, > unsigned long *image_addr, > unsigned long *image_size, > unsigned long *reserve_addr, > @@ -23,21 +23,44 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table, > { > efi_status_t status; > unsigned long kernel_size, kernel_memsize = 0; > + unsigned long nr_pages; > + void *old_image_addr = (void *)*image_addr; > > /* Relocate the image, if required. */ > kernel_size = _edata - _text; > if (*image_addr != (dram_base + TEXT_OFFSET)) { > kernel_memsize = kernel_size + (_end - _edata); > - status = efi_low_alloc(sys_table, kernel_memsize + TEXT_OFFSET, > - SZ_2M, reserve_addr); > + > + /* > + * First, try a straight allocation at the preferred offset. > + * This will work around the issue where, if dram_base == 0x0, > + * efi_low_alloc() refuses to allocate at 0x0 (to prevent the > + * address of the allocation to be mistaken for a FAIL return > + * value or a NULL pointer). It will also ensure that, on > + * platforms where the [dram_base, dram_base + TEXT_OFFSET) > + * interval is partially occupied by the firmware (like on APM > + * Mustang), we can still place the kernel at the address > + * 'dram_base + TEXT_OFFSET'. > + */ > + *image_addr = *reserve_addr = dram_base + TEXT_OFFSET; > + nr_pages = round_up(kernel_memsize, EFI_ALLOC_ALIGN) / > + EFI_PAGE_SIZE; > + status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, > + EFI_LOADER_DATA, nr_pages, > + (efi_physical_addr_t *)reserve_addr); > if (status != EFI_SUCCESS) { > - pr_efi_err(sys_table, "Failed to relocate kernel\n"); > - return status; > + kernel_memsize += TEXT_OFFSET; > + status = efi_low_alloc(sys_table_arg, kernel_memsize, > + SZ_2M, reserve_addr); > + > + if (status != EFI_SUCCESS) { > + pr_efi_err(sys_table_arg, "Failed to relocate kernel\n"); > + return status; > + } > + *image_addr = *reserve_addr + TEXT_OFFSET; > } > - memcpy((void *)*reserve_addr + TEXT_OFFSET, (void *)*image_addr, > - kernel_size); > - *image_addr = *reserve_addr + TEXT_OFFSET; > - *reserve_size = kernel_memsize + TEXT_OFFSET; > + memcpy((void *)*image_addr, old_image_addr, kernel_size); > + *reserve_size = kernel_memsize; > } > > > -- > 1.9.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html