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 <kevmitch@xxxxxxxxxx> --- 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 kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec