c2f7a5e
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
c2f7a5e
From: Peter Jones <pjones@redhat.com>
c2f7a5e
Date: Tue, 14 Aug 2018 14:07:44 -0400
c2f7a5e
Subject: [PATCH] arm/arm64 loader: Better memory allocation and error
c2f7a5e
 messages.
c2f7a5e
c2f7a5e
On mustang, our memory map looks like:
c2f7a5e
c2f7a5e
Type      Physical start  - end             #Pages        Size Attributes
c2f7a5e
reserved  0000004000000000-00000040001fffff 00000200      2MiB UC WC WT WB
c2f7a5e
conv-mem  0000004000200000-0000004393ffffff 00393e00  14654MiB UC WC WT WB
c2f7a5e
ldr-code  0000004394000000-00000043f7ffffff 00064000   1600MiB UC WC WT WB
c2f7a5e
BS-data   00000043f8000000-00000043f801ffff 00000020    128KiB UC WC WT WB
c2f7a5e
conv-mem  00000043f8020000-00000043fa15bfff 0000213c  34032KiB UC WC WT WB
c2f7a5e
ldr-code  00000043fa15c000-00000043fa2a1fff 00000146   1304KiB UC WC WT WB
c2f7a5e
ldr-data  00000043fa2a2000-00000043fa3e8fff 00000147   1308KiB UC WC WT WB
c2f7a5e
conv-mem  00000043fa3e9000-00000043fa3e9fff 00000001      4KiB UC WC WT WB
c2f7a5e
ldr-data  00000043fa3ea000-00000043fa3eafff 00000001      4KiB UC WC WT WB
c2f7a5e
ldr-code  00000043fa3eb000-00000043fa4affff 000000c5    788KiB UC WC WT WB
c2f7a5e
BS-code   00000043fa4b0000-00000043fa59ffff 000000f0    960KiB UC WC WT WB
c2f7a5e
RT-code   00000043fa5a0000-00000043fa5affff 00000010     64KiB RT UC WC WT WB
c2f7a5e
RT-data   00000043fa5b0000-00000043fa5bffff 00000010     64KiB RT UC WC WT WB
c2f7a5e
RT-code   00000043fa5c0000-00000043fa5cffff 00000010     64KiB RT UC WC WT WB
c2f7a5e
ldr-data  00000043fa5d0000-00000043fa5d0fff 00000001      4KiB UC WC WT WB
c2f7a5e
BS-code   00000043fa5d1000-00000043fa5ddfff 0000000d     52KiB UC WC WT WB
c2f7a5e
reserved  00000043fa5de000-00000043fa60ffff 00000032    200KiB UC WC WT WB
c2f7a5e
ACPI-rec  00000043fa610000-00000043fa6affff 000000a0    640KiB UC WC WT WB
c2f7a5e
ACPI-nvs  00000043fa6b0000-00000043fa6bffff 00000010     64KiB UC WC WT WB
c2f7a5e
ACPI-rec  00000043fa6c0000-00000043fa70ffff 00000050    320KiB UC WC WT WB
c2f7a5e
RT-code   00000043fa710000-00000043fa72ffff 00000020    128KiB RT UC WC WT WB
c2f7a5e
RT-data   00000043fa730000-00000043fa78ffff 00000060    384KiB RT UC WC WT WB
c2f7a5e
RT-code   00000043fa790000-00000043fa79ffff 00000010     64KiB RT UC WC WT WB
c2f7a5e
RT-data   00000043fa7a0000-00000043fa99ffff 00000200      2MiB RT UC WC WT WB
c2f7a5e
RT-code   00000043fa9a0000-00000043fa9affff 00000010     64KiB RT UC WC WT WB
c2f7a5e
RT-data   00000043fa9b0000-00000043fa9cffff 00000020    128KiB RT UC WC WT WB
c2f7a5e
BS-code   00000043fa9d0000-00000043fa9d9fff 0000000a     40KiB UC WC WT WB
c2f7a5e
reserved  00000043fa9da000-00000043fa9dbfff 00000002      8KiB UC WC WT WB
c2f7a5e
conv-mem  00000043fa9dc000-00000043fc29dfff 000018c2  25352KiB UC WC WT WB
c2f7a5e
BS-data   00000043fc29e000-00000043fc78afff 000004ed   5044KiB UC WC WT WB
c2f7a5e
conv-mem  00000043fc78b000-00000043fca01fff 00000277   2524KiB UC WC WT WB
c2f7a5e
BS-data   00000043fca02000-00000043fcea3fff 000004a2   4744KiB UC WC WT WB
c2f7a5e
conv-mem  00000043fcea4000-00000043fcea4fff 00000001      4KiB UC WC WT WB
c2f7a5e
BS-data   00000043fcea5000-00000043fd192fff 000002ee   3000KiB UC WC WT WB
c2f7a5e
conv-mem  00000043fd193000-00000043fd2b0fff 0000011e   1144KiB UC WC WT WB
c2f7a5e
BS-data   00000043fd2b1000-00000043ff80ffff 0000255f  38268KiB UC WC WT WB
c2f7a5e
BS-code   00000043ff810000-00000043ff99ffff 00000190   1600KiB UC WC WT WB
c2f7a5e
RT-code   00000043ff9a0000-00000043ff9affff 00000010     64KiB RT UC WC WT WB
c2f7a5e
conv-mem  00000043ff9b0000-00000043ff9bffff 00000010     64KiB UC WC WT WB
c2f7a5e
RT-data   00000043ff9c0000-00000043ff9effff 00000030    192KiB RT UC WC WT WB
c2f7a5e
conv-mem  00000043ff9f0000-00000043ffa05fff 00000016     88KiB UC WC WT WB
c2f7a5e
BS-data   00000043ffa06000-00000043ffffffff 000005fa   6120KiB UC WC WT WB
c2f7a5e
MMIO      0000000010510000-0000000010510fff 00000001      4KiB RT
c2f7a5e
MMIO      0000000010548000-0000000010549fff 00000002      8KiB RT
c2f7a5e
MMIO      0000000017000000-0000000017001fff 00000002      8KiB RT
c2f7a5e
MMIO      000000001c025000-000000001c025fff 00000001      4KiB RT
c2f7a5e
c2f7a5e
When we're trying to find the base of ram, if we start with GRUB_UINT_MAX
c2f7a5e
(0xffffffff on all platforms) and always use min(), that means we eventually
c2f7a5e
decide that the base of ram is GRUB_UINT_MAX, which is lower than our first
c2f7a5e
memory address, and thus our allocation of the initramfs, which specifies this
c2f7a5e
value as the maximum possible address it can be at, fails.
c2f7a5e
c2f7a5e
This patch changes it to start at GRUB_EFI_MAX_USABLE_ADDRESS, which is always
c2f7a5e
at least 0xffffffff on 32-bit platforms and at least 0x7ffffffffffffff on
c2f7a5e
64-bit platforms.  Additionally, this adds a requirement that the memory we
c2f7a5e
choose is actually /allocatable/ conventional memory, not merely
c2f7a5e
write-combining.  On this machine that means we wind up with an allocation
c2f7a5e
around 0x4392XXXXXX, which is a reasonable address.
c2f7a5e
c2f7a5e
This also changes grub_efi_allocate_pages_real() so that if 0 is allocated, it
c2f7a5e
tries to allocate again starting with the same max address it did the first
c2f7a5e
time, rather than interposing GRUB_EFI_MAX_USABLE_ADDRESS there, so that any
c2f7a5e
per-platform constraints on its given address are maintained.
c2f7a5e
c2f7a5e
Signed-off-by: Peter Jones <pjones@redhat.com>
c2f7a5e
---
7531222
 grub-core/kern/efi/mm.c        | 35 ++++++++++++++-----
c2f7a5e
 grub-core/loader/arm64/linux.c | 78 ++++++++++++++++++++++++++++++++----------
7531222
 2 files changed, 85 insertions(+), 28 deletions(-)
c2f7a5e
c2f7a5e
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
7531222
index 7692e63ba24..2c41a528f27 100644
c2f7a5e
--- a/grub-core/kern/efi/mm.c
c2f7a5e
+++ b/grub-core/kern/efi/mm.c
c2f7a5e
@@ -154,6 +154,7 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
c2f7a5e
 {
c2f7a5e
   grub_efi_status_t status;
c2f7a5e
   grub_efi_boot_services_t *b;
c2f7a5e
+  grub_efi_physical_address_t ret = address;
c2f7a5e
 
c2f7a5e
   /* Limit the memory access to less than 4GB for 32-bit platforms.  */
c2f7a5e
   if (address > GRUB_EFI_MAX_USABLE_ADDRESS)
7531222
@@ -165,19 +166,22 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
c2f7a5e
     }
c2f7a5e
 
c2f7a5e
   b = grub_efi_system_table->boot_services;
c2f7a5e
-  status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address);
c2f7a5e
+  status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &ret;;
c2f7a5e
   if (status != GRUB_EFI_SUCCESS)
c2f7a5e
     {
7531222
+      grub_dprintf ("efi",
7531222
+		    "allocate_pages(%d, %d, 0x%0lx, 0x%016lx) = 0x%016lx\n",
7531222
+		    alloctype, memtype, pages, address, status);
c2f7a5e
       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
c2f7a5e
       return NULL;
c2f7a5e
     }
c2f7a5e
 
c2f7a5e
-  if (address == 0)
c2f7a5e
+  if (ret == 0)
c2f7a5e
     {
c2f7a5e
       /* Uggh, the address 0 was allocated... This is too annoying,
c2f7a5e
 	 so reallocate another one.  */
c2f7a5e
-      address = GRUB_EFI_MAX_USABLE_ADDRESS;
c2f7a5e
-      status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address);
c2f7a5e
+      ret = address;
c2f7a5e
+      status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &ret;;
c2f7a5e
       grub_efi_free_pages (0, pages);
c2f7a5e
       if (status != GRUB_EFI_SUCCESS)
c2f7a5e
 	{
7531222
@@ -186,9 +190,9 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
c2f7a5e
 	}
c2f7a5e
     }
c2f7a5e
 
c2f7a5e
-  grub_efi_store_alloc (address, pages);
c2f7a5e
+  grub_efi_store_alloc (ret, pages);
c2f7a5e
 
c2f7a5e
-  return (void *) ((grub_addr_t) address);
c2f7a5e
+  return (void *) ((grub_addr_t) ret);
c2f7a5e
 }
c2f7a5e
 
c2f7a5e
 void *
7531222
@@ -696,11 +700,24 @@ grub_efi_get_ram_base(grub_addr_t *base_addr)
c2f7a5e
   if (ret < 1)
c2f7a5e
     return GRUB_ERR_BUG;
c2f7a5e
 
c2f7a5e
-  for (desc = memory_map, *base_addr = GRUB_UINT_MAX;
c2f7a5e
+  for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS;
c2f7a5e
        (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size);
c2f7a5e
        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
c2f7a5e
-    if (desc->attribute & GRUB_EFI_MEMORY_WB)
c2f7a5e
-      *base_addr = grub_min (*base_addr, desc->physical_start);
c2f7a5e
+    {
c2f7a5e
+      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY &&
c2f7a5e
+	  (desc->attribute & GRUB_EFI_MEMORY_WB))
c2f7a5e
+	{
c2f7a5e
+	  *base_addr = grub_min (*base_addr, desc->physical_start);
c2f7a5e
+	  grub_dprintf ("efi", "setting base_addr=0x%016lx\n", *base_addr);
c2f7a5e
+	}
c2f7a5e
+      else
c2f7a5e
+	{
c2f7a5e
+	  grub_dprintf ("efi", "ignoring address 0x%016lx\n", desc->physical_start);
c2f7a5e
+	}
c2f7a5e
+    }
c2f7a5e
+
c2f7a5e
+  if (*base_addr == GRUB_EFI_MAX_USABLE_ADDRESS)
c2f7a5e
+    grub_dprintf ("efi", "base_addr 0x%016lx is probably wrong.\n", *base_addr);
c2f7a5e
 
c2f7a5e
   grub_free(memory_map);
c2f7a5e
 
c2f7a5e
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
c2f7a5e
index 93b5cd306eb..e1110749eb9 100644
c2f7a5e
--- a/grub-core/loader/arm64/linux.c
c2f7a5e
+++ b/grub-core/loader/arm64/linux.c
c2f7a5e
@@ -70,13 +70,15 @@ finalize_params_linux (void)
c2f7a5e
 {
c2f7a5e
   grub_efi_loaded_image_t *loaded_image = NULL;
c2f7a5e
   int node, retval, len;
c2f7a5e
-
c2f7a5e
+  grub_err_t err = GRUB_ERR_NONE;
c2f7a5e
   void *fdt;
c2f7a5e
 
c2f7a5e
   fdt = grub_fdt_load (0x400);
c2f7a5e
-
c2f7a5e
   if (!fdt)
c2f7a5e
-    goto failure;
c2f7a5e
+    {
c2f7a5e
+      err = grub_error(GRUB_ERR_BAD_OS, "failed to load FDT");
c2f7a5e
+      goto failure;
c2f7a5e
+    }
c2f7a5e
 
c2f7a5e
   node = grub_fdt_find_subnode (fdt, 0, "chosen");
c2f7a5e
   if (node < 0)
c2f7a5e
@@ -87,17 +89,26 @@ finalize_params_linux (void)
c2f7a5e
        */
c2f7a5e
       retval = grub_fdt_set_prop32(fdt, 0, "#address-cells", 2);
c2f7a5e
       if (retval)
c2f7a5e
-	goto failure;
c2f7a5e
+	{
c2f7a5e
+	  err = grub_error(retval, "Could not find #address-cells");
c2f7a5e
+	  goto failure;
c2f7a5e
+	}
c2f7a5e
 
c2f7a5e
       retval = grub_fdt_set_prop32(fdt, 0, "#size-cells", 2);
c2f7a5e
       if (retval)
c2f7a5e
-	goto failure;
c2f7a5e
+	{
c2f7a5e
+	  err = grub_error(retval, "Could not find #size-cells");
c2f7a5e
+	  goto failure;
c2f7a5e
+	}
c2f7a5e
 
c2f7a5e
       node = grub_fdt_add_subnode (fdt, 0, "chosen");
c2f7a5e
     }
c2f7a5e
 
c2f7a5e
   if (node < 1)
c2f7a5e
-    goto failure;
c2f7a5e
+    {
c2f7a5e
+      err = grub_error(grub_errno, "failed to load chosen fdt node.");
c2f7a5e
+      goto failure;
c2f7a5e
+    }
c2f7a5e
 
c2f7a5e
   /* Set initrd info */
c2f7a5e
   if (initrd_start && initrd_end > initrd_start)
c2f7a5e
@@ -108,15 +119,26 @@ finalize_params_linux (void)
c2f7a5e
       retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start",
c2f7a5e
 				    initrd_start);
c2f7a5e
       if (retval)
c2f7a5e
-	goto failure;
c2f7a5e
+	{
c2f7a5e
+	  err = grub_error(retval, "Failed to set linux,initrd-start property");
c2f7a5e
+	  goto failure;
c2f7a5e
+	}
c2f7a5e
+
c2f7a5e
       retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end",
c2f7a5e
 				    initrd_end);
c2f7a5e
       if (retval)
c2f7a5e
-	goto failure;
c2f7a5e
+	{
c2f7a5e
+	  err = grub_error(retval, "Failed to set linux,initrd-end property");
c2f7a5e
+	  goto failure;
c2f7a5e
+	}
c2f7a5e
     }
c2f7a5e
 
c2f7a5e
-  if (grub_fdt_install() != GRUB_ERR_NONE)
c2f7a5e
-    goto failure;
c2f7a5e
+  retval = grub_fdt_install();
c2f7a5e
+  if (retval != GRUB_ERR_NONE)
c2f7a5e
+    {
c2f7a5e
+      err = grub_error(retval, "Failed to install fdt");
c2f7a5e
+      goto failure;
c2f7a5e
+    }
c2f7a5e
 
c2f7a5e
   grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n",
c2f7a5e
 		fdt);
c2f7a5e
@@ -124,14 +146,20 @@ finalize_params_linux (void)
c2f7a5e
   /* Convert command line to UCS-2 */
c2f7a5e
   loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
c2f7a5e
   if (!loaded_image)
c2f7a5e
-    goto failure;
c2f7a5e
+    {
c2f7a5e
+      err = grub_error(grub_errno, "Failed to install fdt");
c2f7a5e
+      goto failure;
c2f7a5e
+    }
c2f7a5e
 
c2f7a5e
   loaded_image->load_options_size = len =
c2f7a5e
     (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
c2f7a5e
   loaded_image->load_options =
c2f7a5e
     grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
c2f7a5e
   if (!loaded_image->load_options)
c2f7a5e
-    return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
c2f7a5e
+    {
c2f7a5e
+      err = grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
c2f7a5e
+      goto failure;
c2f7a5e
+    }
c2f7a5e
 
c2f7a5e
   loaded_image->load_options_size =
c2f7a5e
     2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
c2f7a5e
@@ -141,7 +169,7 @@ finalize_params_linux (void)
c2f7a5e
 
c2f7a5e
 failure:
c2f7a5e
   grub_fdt_unload();
c2f7a5e
-  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
c2f7a5e
+  return err;
c2f7a5e
 }
c2f7a5e
 
c2f7a5e
 static void
c2f7a5e
@@ -225,16 +253,28 @@ grub_linux_unload (void)
c2f7a5e
 static void *
c2f7a5e
 allocate_initrd_mem (int initrd_pages)
c2f7a5e
 {
c2f7a5e
-  grub_addr_t max_addr;
c2f7a5e
+  grub_addr_t max_addr = 0;
c2f7a5e
+  grub_err_t err;
c2f7a5e
+  void *ret;
c2f7a5e
 
c2f7a5e
-  if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
c2f7a5e
-    return NULL;
c2f7a5e
+  err = grub_efi_get_ram_base (&max_addr);
c2f7a5e
+  if (err != GRUB_ERR_NONE)
c2f7a5e
+    {
c2f7a5e
+      grub_error (err, "grub_efi_get_ram_base() failed");
c2f7a5e
+      return NULL;
c2f7a5e
+    }
c2f7a5e
+
c2f7a5e
+  grub_dprintf ("linux", "max_addr: 0x%016lx, INITRD_MAX_ADDRESS_OFFSET: 0x%016llx\n",
c2f7a5e
+		max_addr, INITRD_MAX_ADDRESS_OFFSET);
c2f7a5e
 
c2f7a5e
   max_addr += INITRD_MAX_ADDRESS_OFFSET - 1;
c2f7a5e
+  grub_dprintf ("linux", "calling grub_efi_allocate_pages_real (0x%016lx, 0x%08x, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA)", max_addr, initrd_pages);
c2f7a5e
 
c2f7a5e
-  return grub_efi_allocate_pages_real (max_addr, initrd_pages,
c2f7a5e
-				       GRUB_EFI_ALLOCATE_MAX_ADDRESS,
c2f7a5e
-				       GRUB_EFI_LOADER_DATA);
c2f7a5e
+  ret = grub_efi_allocate_pages_real (max_addr, initrd_pages,
c2f7a5e
+				      GRUB_EFI_ALLOCATE_MAX_ADDRESS,
c2f7a5e
+				      GRUB_EFI_LOADER_DATA);
c2f7a5e
+  grub_dprintf ("linux", "got 0x%016llx\n", (unsigned long long)ret);
c2f7a5e
+  return ret;
c2f7a5e
 }
c2f7a5e
 
c2f7a5e
 static grub_err_t