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

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