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 <c...@suse.com>
---
 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

Reply via email to