In some cases the arm32 efistub could fail to allocate memory for uncompressed kernel. For example, we got the following error message when verifying EFI stub on Raspberry Pi-2 [kernel-5.2.1 + grub-2.04] : EFI stub: Booting Linux Kernel... EFI stub: ERROR: Unable to allocate memory for uncompressed kernel. EFI stub: ERROR: Failed to relocate kernel After checking the EFI memory map we found that the first page [0 - 0xfff] had been reserved by Raspberry Pi-2's firmware, and the efistub tried to set the dram base at 0, which was actually in a reserved region. grub> lsefimmap Type Physical start - end #Pages Size Attributes reserved 0000000000000000-0000000000000fff 00000001 4KiB WB conv-mem 0000000000001000-0000000007ef5fff 00007ef5 130004KiB WB RT-data 0000000007ef6000-0000000007f09fff 00000014 80KiB RT WB conv-mem 0000000007f0a000-000000002d871fff 00025968 615840KiB WB ..... To avoid a reserved address, we have to ignore the memory regions which are marked as EFI_RESERVED_TYPE, and only conventional memory regions can be chosen. If the region before the kernel base is unaligned, it will be marked as EFI_RESERVED_TYPE and let kernel ignore it so that memblock_limit will not be sticked with a very low address such as 0x1000. Signed-off-by: Chester Lin <clin@xxxxxxxx> --- arch/arm/mm/mmu.c | 3 ++ drivers/firmware/efi/libstub/arm32-stub.c | 43 ++++++++++++++++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index f3ce34113f89..909b11ba48d8 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -1184,6 +1184,9 @@ void __init adjust_lowmem_bounds(void) phys_addr_t block_start = reg->base; phys_addr_t block_end = reg->base + reg->size; + if (memblock_is_nomap(reg)) + continue; + if (reg->base < vmalloc_limit) { if (block_end > lowmem_limit) /* diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index e8f7aefb6813..10d33d36df00 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -128,7 +128,7 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg, for (l = 0; l < map_size; l += desc_size) { efi_memory_desc_t *desc; - u64 start, end; + u64 start, end, spare, kernel_base; desc = (void *)memory_map + l; start = desc->phys_addr; @@ -144,27 +144,52 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg, case EFI_BOOT_SERVICES_DATA: /* Ignore types that are released to the OS anyway */ continue; - + case EFI_RESERVED_TYPE: + /* Ignore reserved regions */ + continue; case EFI_CONVENTIONAL_MEMORY: /* * Reserve the intersection between this entry and the * region. */ start = max(start, (u64)dram_base); - end = min(end, (u64)dram_base + MAX_UNCOMP_KERNEL_SIZE); + kernel_base = round_up(start, PMD_SIZE); + spare = kernel_base - start; + end = min(end, kernel_base + MAX_UNCOMP_KERNEL_SIZE); + + status = efi_call_early(allocate_pages, + EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, + MAX_UNCOMP_KERNEL_SIZE / EFI_PAGE_SIZE, + &kernel_base); + if (status != EFI_SUCCESS) { + pr_efi_err(sys_table_arg, + "reserve_kernel_base: alloc failed.\n"); + goto out; + } + *reserve_addr = kernel_base; + if (!spare) + break; + /* + * If there's a gap between start and kernel_base, + * it needs be reserved so that the memblock_limit + * will not fall on a very low address when running + * adjust_lowmem_bounds(), wchich could eventually + * cause CMA reservation issue. + */ status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, - (end - start) / EFI_PAGE_SIZE, + EFI_RESERVED_TYPE, + spare / EFI_PAGE_SIZE, &start); if (status != EFI_SUCCESS) { pr_efi_err(sys_table_arg, - "reserve_kernel_base(): alloc failed.\n"); + "reserve spare-region failed\n"); goto out; } - break; + break; case EFI_LOADER_CODE: case EFI_LOADER_DATA: /* @@ -220,7 +245,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, *image_size = image->image_size; status = efi_relocate_kernel(sys_table, image_addr, *image_size, *image_size, - dram_base + MAX_UNCOMP_KERNEL_SIZE, 0); + *reserve_addr + MAX_UNCOMP_KERNEL_SIZE, 0); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to relocate kernel.\n"); efi_free(sys_table, *reserve_size, *reserve_addr); @@ -233,7 +258,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, * in memory. The kernel determines the base of DRAM from the * address at which the zImage is loaded. */ - if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) { + if (*image_addr + *image_size > *reserve_addr + ZIMAGE_OFFSET_LIMIT) { pr_efi_err(sys_table, "Failed to relocate kernel, no low memory available.\n"); efi_free(sys_table, *reserve_size, *reserve_addr); *reserve_size = 0; -- 2.22.0