diff options
-rw-r--r-- | include/x86/x86-linux.h | 10 | ||||
-rw-r--r-- | kexec/arch/i386/kexec-bzImage.c | 48 | ||||
-rw-r--r-- | kexec/arch/i386/x86-linux-setup.c | 3 |
3 files changed, 53 insertions, 8 deletions
diff --git a/include/x86/x86-linux.h b/include/x86/x86-linux.h index daab50a..afe66bd 100644 --- a/include/x86/x86-linux.h +++ b/include/x86/x86-linux.h @@ -141,7 +141,10 @@ struct x86_linux_param_header { uint32_t high_filesz; /* 0x254 */ uint8_t reserved15[0x2d0 - 0x258]; /* 0x258 */ #else - uint8_t reserved15[0x2d0 - 0x230]; /* 0x230 */ + /* 2.04+ */ + uint32_t kernel_alignment; /* 0x230 */ + uint8_t relocatable_kernel; /* 0x234 */ + uint8_t reserved15[0x2d0 - 0x235]; /* 0x230 */ #endif struct e820entry e820_map[E820MAX]; /* 0x2d0 */ /* 0x550 */ @@ -201,12 +204,15 @@ struct x86_linux_header { uint32_t high_filesz; /* 0x254 */ uint32_t tail[32*1024 - 0x258]; /* 0x258 */ #else - uint8_t tail[32*1024 - 0x230]; /* 0x230 */ + uint32_t kernel_alignment; /* 0x230 */ + uint8_t relocatable_kernel; /* 0x234 */ + uint8_t tail[32*1024 - 0x235]; /* 0x230 */ #endif } PACKED; #endif /* ASSEMBLY */ #define DEFAULT_INITRD_ADDR_MAX 0x37FFFFFF +#define DEFAULT_BZIMAGE_ADDR_MAX 0x37FFFFFF #endif /* X86_LINUX_H */ diff --git a/kexec/arch/i386/kexec-bzImage.c b/kexec/arch/i386/kexec-bzImage.c index e70e238..77ccfa3 100644 --- a/kexec/arch/i386/kexec-bzImage.c +++ b/kexec/arch/i386/kexec-bzImage.c @@ -109,6 +109,8 @@ int do_bzImage_load(struct kexec_info *info, unsigned long setup_base, setup_size; struct entry32_regs regs32; struct entry16_regs regs16; + unsigned int relocatable_kernel = 0; + unsigned long kernel32_load_addr; /* * Find out about the file I am about to load. @@ -121,6 +123,7 @@ int do_bzImage_load(struct kexec_info *info, if (setup_sects == 0) { setup_sects = 4; } + kern16_size = (setup_sects +1) *512; kernel_version = ((unsigned char *)&setup_header) + 512 + setup_header.kver_addr; if (kernel_len < kern16_size) { @@ -128,13 +131,23 @@ int do_bzImage_load(struct kexec_info *info, return -1; } + if (setup_header.protocol_version >= 0x0205) { + relocatable_kernel = setup_header.relocatable_kernel; + dfprintf(stdout, "bzImage is relocatable\n"); + } + /* Load the trampoline. This must load at a higher address * the the argument/parameter segment or the kernel will stomp * it's gdt. */ - elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, - 0x3000, 640*1024, -1, 0); - + if (!real_mode_entry && relocatable_kernel) + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x3000, -1, -1, 0); + else + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x3000, 640*1024, -1, 0); + dfprintf(stdout, "Loaded purgatory at addr 0x%lx\n", + info->rhdr.rel_addr); /* The argument/parameter segment */ setup_size = kern16_size + command_line_len; real_mode = xmalloc(setup_size); @@ -142,11 +155,13 @@ int do_bzImage_load(struct kexec_info *info, if (real_mode->protocol_version >= 0x0200) { /* Careful setup_base must be greater than 8K */ setup_base = add_buffer(info, real_mode, setup_size, setup_size, - 16, 0x3000, 640*1024, -1); + 16, 0x3000, 640*1024, 1); } else { add_segment(info, real_mode, setup_size, SETUP_BASE, setup_size); setup_base = SETUP_BASE; } + dfprintf(stdout, "Loaded real-mode code and command line at 0x%lx\n", + setup_base); /* Verify purgatory loads higher than the parameters */ if (info->rhdr.rel_addr < setup_base) { die("Could not put setup code above the kernel parameters\n"); @@ -154,9 +169,30 @@ int do_bzImage_load(struct kexec_info *info, /* The main kernel segment */ size = kernel_len - kern16_size; - add_segment(info, kernel + kern16_size, size, KERN32_BASE, size); + 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; + + if (real_mode->protocol_version >= 0x0203) { + if (kernel32_max_addr > real_mode->initrd_addr_max) + kernel32_max_addr = real_mode->initrd_addr_max; + } + + kernel32_load_addr = add_buffer(info, kernel + kern16_size, + size, size, kern_align, + 0x100000, kernel32_max_addr, + 1); + } + else { + kernel32_load_addr = KERN32_BASE; + add_segment(info, kernel + kern16_size, size, + kernel32_load_addr, size); + } + dfprintf(stdout, "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, kern16_size, command_line, command_line_len, @@ -177,7 +213,7 @@ int do_bzImage_load(struct kexec_info *info, regs32.edi = 0; /* unused */ regs32.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* stack, unused */ regs32.ebp = 0; /* unused */ - regs32.eip = KERN32_BASE; /* kernel entry point */ + regs32.eip = kernel32_load_addr; /* kernel entry point */ /* * Initialize the 16bit start information. diff --git a/kexec/arch/i386/x86-linux-setup.c b/kexec/arch/i386/x86-linux-setup.c index 5701b27..e1cff1d 100644 --- a/kexec/arch/i386/x86-linux-setup.c +++ b/kexec/arch/i386/x86-linux-setup.c @@ -61,6 +61,7 @@ void setup_linux_bootloader_parameters( initrd_addr_max = DEFAULT_INITRD_ADDR_MAX; if (real_mode->protocol_version >= 0x0203) { initrd_addr_max = real_mode->initrd_addr_max; + dfprintf(stdout, "initrd_addr_max is 0x%lx\n", initrd_addr_max); } /* Load the initrd if we have one */ @@ -68,6 +69,8 @@ void setup_linux_bootloader_parameters( initrd_base = add_buffer(info, initrd_buf, initrd_size, initrd_size, 4096, INITRD_BASE, initrd_addr_max, -1); + dfprintf(stdout, "Loaded initrd at 0x%lx size 0x%lx\n", + initrd_base, initrd_size); } else { initrd_base = 0; initrd_size = 0; |