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

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