|
|
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 |
{
|