also need to make sure pass right 64bit start address to go there directly later. Signed-off-by: Yinghai Lu <yinghai at kernel.org> --- kexec/arch/i386/kexec-bzImage.c | 53 ++++++++++++++++++++++++++++++++++++-- 1 files changed, 50 insertions(+), 3 deletions(-) diff --git a/kexec/arch/i386/kexec-bzImage.c b/kexec/arch/i386/kexec-bzImage.c index 6998587..3e705ca 100644 --- a/kexec/arch/i386/kexec-bzImage.c +++ b/kexec/arch/i386/kexec-bzImage.c @@ -18,8 +18,10 @@ */ #define _GNU_SOURCE +#include <stddef.h> #include <stdio.h> #include <string.h> +#include <limits.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> @@ -35,6 +37,7 @@ #include "../../kexec-elf.h" #include "../../kexec-syscall.h" #include "kexec-x86.h" +#include "../x86_64/kexec-x86_64.h" #include "x86-linux-setup.h" #include "crashdump-x86.h" #include <arch/options.h> @@ -111,12 +114,15 @@ int do_bzImage_load(struct kexec_info *info, size_t size; int kern16_size; unsigned long setup_base, setup_size; + struct entry64_regs regs64; struct entry32_regs regs32; struct entry16_regs regs16; unsigned int relocatable_kernel = 0; unsigned long kernel32_load_addr; char *modified_cmdline; unsigned long cmdline_end; + unsigned long code64_start_offset = 0; + unsigned long kernel64_load_addr = 0; /* * Find out about the file I am about to load. @@ -154,6 +160,13 @@ int do_bzImage_load(struct kexec_info *info, dbgprintf("bzImage is relocatable\n"); } + if (setup_header.protocol_version >= 0x020C) { + code64_start_offset = setup_header.code64_start_offset; + if (code64_start_offset) + dbgprintf("code64_start_offset: 0x%lx\n", + code64_start_offset); + } + /* 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" @@ -250,7 +263,26 @@ int do_bzImage_load(struct kexec_info *info, kernel32_max_addr = real_mode->initrd_addr_max; } - kernel32_load_addr = add_buffer(info, kernel + kern16_size, + if (!real_mode_entry && code64_start_offset) { + /* align to 1G to avoid cross the PUD_SIZE boundary */ + kernel64_load_addr = add_buffer( + info, kernel + kern16_size, + size, size, 1UL<<30, + 1UL<<32, ULONG_MAX, + -1); + if (!kernel64_load_addr) + kernel64_load_addr = add_buffer( + info, kernel + kern16_size, + size, size, 1UL<<30, + 1UL<<30, 1UL<<32, + -1); + if (kernel64_load_addr) + kernel64_load_addr += code64_start_offset; + } + + if (!kernel64_load_addr) + kernel32_load_addr = add_buffer( + info, kernel + kern16_size, size, size, kern_align, 0x100000, kernel32_max_addr, 1); @@ -260,8 +292,11 @@ int do_bzImage_load(struct kexec_info *info, add_segment(info, kernel + kern16_size, size, kernel32_load_addr, size); } - - dbgprintf("Loaded 32bit kernel at 0x%lx\n", kernel32_load_addr); + + if (kernel64_load_addr) + dbgprintf("Loaded 64bit kernel at 0x%lx\n", kernel64_load_addr); + else + 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, @@ -271,6 +306,16 @@ int do_bzImage_load(struct kexec_info *info, /* Get the initial register values */ elf_rel_get_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16)); elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32)); + if (kernel64_load_addr) { + elf_rel_get_symbol(&info->rhdr, "entry64_regs", ®s64, sizeof(regs64)); + regs64.rbx = 0; /* Bootstrap processor */ + regs64.rsi = setup_base; /* Pointer to the parameters */ + regs64.rip = kernel64_load_addr; /* the entry point */ + regs64.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */ + elf_rel_set_symbol(&info->rhdr, "entry64_regs", ®s64, sizeof(regs64)); + + goto cmd_line; + } /* * Initialize the 32bit start information. @@ -320,6 +365,8 @@ int do_bzImage_load(struct kexec_info *info, elf_rel_set_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16)); elf_rel_set_symbol(&info->rhdr, "entry16_debug_regs", ®s16, sizeof(regs16)); elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32)); + +cmd_line: cmdline_end = setup_base + kern16_size + command_line_len - 1; elf_rel_set_symbol(&info->rhdr, "cmdline_end", &cmdline_end, sizeof(unsigned long)); -- 1.7.7