Blame SOURCES/0499-x86-efi-Allow-initrd-params-cmdline-allocations-abov.patch

b9d01e
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b9d01e
From: Peter Jones <pjones@redhat.com>
b9d01e
Date: Wed, 12 Sep 2018 16:12:27 -0400
b9d01e
Subject: [PATCH] x86-efi: Allow initrd+params+cmdline allocations above 4GB.
b9d01e
b9d01e
This enables everything except the kernel itself to be above 4GB.
b9d01e
Putting the kernel up there still doesn't work, because of the way
b9d01e
params->code32_start is used.
b9d01e
b9d01e
Signed-off-by: Peter Jones <pjones@redhat.com>
b9d01e
(cherry picked from commit 2b636967018431b046b625ad4753c8de51f7f6b2)
b9d01e
---
b9d01e
 grub-core/loader/i386/efi/linux.c | 67 +++++++++++++++++++++++++++++++++++----
b9d01e
 include/grub/i386/linux.h         |  6 +++-
b9d01e
 2 files changed, 65 insertions(+), 8 deletions(-)
b9d01e
b9d01e
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
b9d01e
index 5eed2014c..e9d2c85b3 100644
b9d01e
--- a/grub-core/loader/i386/efi/linux.c
b9d01e
+++ b/grub-core/loader/i386/efi/linux.c
b9d01e
@@ -54,13 +54,22 @@ struct allocation_choice {
b9d01e
     grub_efi_allocate_type_t alloc_type;
b9d01e
 };
b9d01e
 
b9d01e
-static struct allocation_choice max_addresses[] =
b9d01e
+static struct allocation_choice max_addresses[4] =
b9d01e
   {
b9d01e
+    /* the kernel overrides this one with pref_address and
b9d01e
+     * GRUB_EFI_ALLOCATE_ADDRESS */
b9d01e
     { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
b9d01e
+    /* this one is always below 4GB, which we still *prefer* even if the flag
b9d01e
+     * is set. */
b9d01e
     { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
b9d01e
+    /* If the flag in params is set, this one gets changed to be above 4GB. */
b9d01e
     { GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS },
b9d01e
     { 0, 0 }
b9d01e
   };
b9d01e
+static struct allocation_choice saved_addresses[4];
b9d01e
+
b9d01e
+#define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses))
b9d01e
+#define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses))
b9d01e
 
b9d01e
 static inline void
b9d01e
 kernel_free(void *addr, grub_efi_uintn_t size)
b9d01e
@@ -82,6 +91,11 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
b9d01e
       grub_uint64_t max = max_addresses[i].addr;
b9d01e
       grub_efi_uintn_t pages;
b9d01e
 
b9d01e
+      /*
b9d01e
+       * When we're *not* loading the kernel, or >4GB allocations aren't
b9d01e
+       * supported, these entries are basically all the same, so don't re-try
b9d01e
+       * the same parameters.
b9d01e
+       */
b9d01e
       if (max == prev_max)
b9d01e
 	continue;
b9d01e
 
b9d01e
@@ -170,6 +184,9 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
b9d01e
   return bufpos;
b9d01e
 }
b9d01e
 
b9d01e
+#define LOW_U32(val) ((grub_uint32_t)(((grub_addr_t)(val)) & 0xffffffffull))
b9d01e
+#define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull))
b9d01e
+
b9d01e
 static grub_err_t
b9d01e
 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
b9d01e
                  int argc, char *argv[])
b9d01e
@@ -214,8 +231,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
b9d01e
     goto fail;
b9d01e
   grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
b9d01e
 
b9d01e
-  params->ramdisk_size = size;
b9d01e
-  params->ramdisk_image = initrd_mem;
b9d01e
+  params->ramdisk_size = LOW_U32(size);
b9d01e
+  params->ramdisk_image = LOW_U32(initrd_mem);
b9d01e
+#if defined(__x86_64__)
b9d01e
+  params->ext_ramdisk_size = HIGH_U32(size);
b9d01e
+  params->ext_ramdisk_image = HIGH_U32(initrd_mem);
b9d01e
+#endif
b9d01e
 
b9d01e
   ptr = initrd_mem;
b9d01e
 
b9d01e
@@ -353,6 +374,18 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
     }
b9d01e
 #endif
b9d01e
 
b9d01e
+#if defined(__x86_64__)
b9d01e
+  if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G)
b9d01e
+    {
b9d01e
+      grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n");
b9d01e
+      max_addresses[2].addr = GRUB_EFI_MAX_USABLE_ADDRESS;
b9d01e
+    }
b9d01e
+  else
b9d01e
+    {
b9d01e
+      grub_dprintf ("linux", "Loading kernel above 4GB is not supported\n");
b9d01e
+    }
b9d01e
+#endif
b9d01e
+
b9d01e
   params = kernel_alloc (sizeof(*params), "cannot allocate kernel parameters");
b9d01e
   if (!params)
b9d01e
     goto fail;
b9d01e
@@ -387,21 +420,40 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
 
b9d01e
   grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
b9d01e
   grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n",
b9d01e
-		linux_cmdline);
b9d01e
-  lh->cmd_line_ptr = linux_cmdline;
b9d01e
+		LOW_U32(linux_cmdline));
b9d01e
+  lh->cmd_line_ptr = LOW_U32(linux_cmdline);
b9d01e
+#if defined(__x86_64__)
b9d01e
+  if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull)
b9d01e
+    {
b9d01e
+      grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n",
b9d01e
+		    HIGH_U32(linux_cmdline));
b9d01e
+      params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline);
b9d01e
+    }
b9d01e
+#endif
b9d01e
 
b9d01e
   handover_offset = lh->handover_offset;
b9d01e
   grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset);
b9d01e
 
b9d01e
   start = (lh->setup_sects + 1) * 512;
b9d01e
 
b9d01e
+  /*
b9d01e
+   * AFAICS >4GB for kernel *cannot* work because of params->code32_start being
b9d01e
+   * 32-bit and getting called unconditionally in head_64.S from either entry
b9d01e
+   * point.
b9d01e
+   *
b9d01e
+   * so nerf that out here...
b9d01e
+   */
b9d01e
+  save_addresses();
b9d01e
   grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address);
b9d01e
   if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS)
b9d01e
     {
b9d01e
       max_addresses[0].addr = lh->pref_address;
b9d01e
       max_addresses[0].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS;
b9d01e
     }
b9d01e
+  max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
b9d01e
+  max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
b9d01e
   kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel"));
b9d01e
+  restore_addresses();
b9d01e
   if (!kernel_mem)
b9d01e
     goto fail;
b9d01e
   grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
b9d01e
@@ -410,8 +462,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
 
b9d01e
   loaded = 1;
b9d01e
 
b9d01e
-  grub_dprintf ("linux", "setting lh->code32_start to %p\n", kernel_mem);
b9d01e
-  lh->code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem;
b9d01e
+  grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n",
b9d01e
+		LOW_U32(kernel_mem));
b9d01e
+  lh->code32_start = LOW_U32(kernel_mem);
b9d01e
 
b9d01e
   grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start);
b9d01e
 
b9d01e
diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h
b9d01e
index 8474a857e..a4b37dcce 100644
b9d01e
--- a/include/grub/i386/linux.h
b9d01e
+++ b/include/grub/i386/linux.h
b9d01e
@@ -230,7 +230,11 @@ struct linux_kernel_params
b9d01e
   grub_uint32_t ofw_cif_handler;	/* b8 */
b9d01e
   grub_uint32_t ofw_idt;		/* bc */
b9d01e
 
b9d01e
-  grub_uint8_t padding7[0x1b8 - 0xc0];
b9d01e
+  grub_uint32_t ext_ramdisk_image;	/* 0xc0 */
b9d01e
+  grub_uint32_t ext_ramdisk_size;	/* 0xc4 */
b9d01e
+  grub_uint32_t ext_cmd_line_ptr;	/* 0xc8 */
b9d01e
+
b9d01e
+  grub_uint8_t padding7[0x1b8 - 0xcc];
b9d01e
 
b9d01e
   union
b9d01e
     {