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