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

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