nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

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

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