In linux-5.2, the following commit allows the kdump kernel to be loaded
at a higher address than 896M

9ca5c8e632ce ("x86/kdump: Have crashkernel=X reserve under 4G by
default")

While this limit does indeed seem unnecessary for x86_64 kernels, it
still is required to boot to or from i386 kernels. Therefore,
kexec-tools continues to enforce it when using the i386 bzImage loader.

However, the i386 bzImage loader may also be used to load an x86_64
kernel from i386 user space to be kexeced by an x86_64 kernel. In this
case, the limit was incorrectly enforced.

This commit adds an additional check for an x86_64 image kexeced by an
x86_64 kernel in the i386 loader and bumps the limit to the maximum
addressable 4G in that case.

Signed-off-by: Kevin Mitchell <[email protected]>
---
 kexec/arch/i386/kexec-bzImage.c | 41 ++++++++++++++++++++++-----------
 1 file changed, 28 insertions(+), 13 deletions(-)

diff --git a/kexec/arch/i386/kexec-bzImage.c b/kexec/arch/i386/kexec-bzImage.c
index df8985d..7b8e36e 100644
--- a/kexec/arch/i386/kexec-bzImage.c
+++ b/kexec/arch/i386/kexec-bzImage.c
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -114,6 +115,7 @@ int do_bzImage_load(struct kexec_info *info,
        struct entry32_regs regs32;
        struct entry16_regs regs16;
        unsigned int relocatable_kernel = 0;
+       unsigned int kernel64 = 0;
        unsigned long kernel32_load_addr;
        char *modified_cmdline;
        unsigned long cmdline_end;
@@ -155,6 +157,13 @@ int do_bzImage_load(struct kexec_info *info,
                dbgprintf("bzImage is relocatable\n");
        }
 
+       if ((setup_header.protocol_version >= 0x020C) &&
+           (info->kexec_flags & KEXEC_ARCH_X86_64) &&
+           (setup_header.xloadflags & 1)) {
+               kernel64 = 1;
+               dbgprintf("loading x86_64 bzImage from an x86_64 kernel\n");
+       }
+
        /* Can't use bzImage for crash dump purposes with real mode entry */
        if((info->kexec_flags & KEXEC_ON_CRASH) && real_mode_entry) {
                fprintf(stderr, "Can't use bzImage for crash dump purposes"
@@ -197,17 +206,17 @@ int do_bzImage_load(struct kexec_info *info,
        /* Load the trampoline.  This must load at a higher address
         * than the argument/parameter segment or the kernel will stomp
         * it's gdt.
-        *
-        * x86_64 purgatory code has got relocations type R_X86_64_32S
-        * that means purgatory got to be loaded within first 2G otherwise
-        * overflow takes place while applying relocations.
         */
-       if (!real_mode_entry && relocatable_kernel)
+       if (!real_mode_entry && relocatable_kernel) {
+               /* x86_64 purgatory could be anywhere */
+               unsigned long purg_max_addr = kernel64 ? ULONG_MAX : 0x7fffffff;
+
                elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
-                                       0x3000, 0x7fffffff, -1, 0);
-       else
+                                       0x3000, purg_max_addr, -1, 0);
+       } else {
                elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
                                        0x3000, 640*1024, -1, 0);
+       }
        dbgprintf("Loaded purgatory at addr 0x%lx\n", info->rhdr.rel_addr);
 
        /* The argument/parameter segment */
@@ -277,14 +286,20 @@ int do_bzImage_load(struct kexec_info *info,
        if (real_mode->protocol_version >=0x0205 && relocatable_kernel) {
                /* Relocatable bzImage */
                unsigned long kern_align = real_mode->kernel_alignment;
-               unsigned long kernel32_max_addr = DEFAULT_BZIMAGE_ADDR_MAX;
+               unsigned long kernel_max_addr = DEFAULT_BZIMAGE_ADDR_MAX;
 
-               if (kernel32_max_addr > real_mode->initrd_addr_max)
-                       kernel32_max_addr = real_mode->initrd_addr_max;
+               /*
+                * x86_64 kernels can be kexeced by an x86_64 kernel
+                * from any addressable location
+                */
+               if (kernel64)
+                       kernel_max_addr = ULONG_MAX;
+               else if (kernel_max_addr > real_mode->initrd_addr_max)
+                       kernel_max_addr = real_mode->initrd_addr_max;
 
                kernel32_load_addr = add_buffer(info, kernel + kern16_size,
                                                size, size, kern_align,
-                                               0x100000, kernel32_max_addr,
+                                               0x100000, kernel_max_addr,
                                                1);
        }
        else {
@@ -296,9 +311,9 @@ int do_bzImage_load(struct kexec_info *info,
        dbgprintf("Loaded 32bit kernel at 0x%lx\n", kernel32_load_addr);
 
        /* Tell the kernel what is going on */
-       setup_linux_bootloader_parameters(info, real_mode, setup_base,
+       setup_linux_bootloader_parameters_high(info, real_mode, setup_base,
                kern16_size_needed, command_line, command_line_len,
-               initrd, initrd_len);
+               initrd, initrd_len, kernel64); /* put x86_64 initrd high too */
 
        if (real_mode_entry && real_mode->protocol_version >= 0x0201) {
                real_mode->loader_flags |= 0x80; /* CAN_USE_HEAP */
-- 
2.32.0


_______________________________________________
kexec mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to