debuggers.hg
changeset 16998:67ca9c37ef02
Direct Linux boot: Support booting non-relocatable Linux kernels.
This patch introduces a hack to make non-relocatable kernels
bootable too. Non-relocatable kernels absolutely want to run
at 0x100000 and are not at all happy about being at 0x200000.
Fortunately, thanks to crazy programs like LOADLIN, Linux has
a couple of hooks in its boot process which can be used to
play games. The 'code32_switch' hook is executed immediately
following the switch to protected mode.
So, this patch installs a hook at 0x200000+kernel_size. The hook
is hand crafted assembly which sets up all the segments as needed,
then essentially does memmove(0x100000,0x200000,kernel_size) and
finally does an unconditional jmp to 0x100000.
Amazingly this actually really does work. It has been successfully
tested with RHEL-2.1 and Fedora Core 6 install kernels on i386, and
Fedora Core 6 and 7 kernels on x86_64.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This patch introduces a hack to make non-relocatable kernels
bootable too. Non-relocatable kernels absolutely want to run
at 0x100000 and are not at all happy about being at 0x200000.
Fortunately, thanks to crazy programs like LOADLIN, Linux has
a couple of hooks in its boot process which can be used to
play games. The 'code32_switch' hook is executed immediately
following the switch to protected mode.
So, this patch installs a hook at 0x200000+kernel_size. The hook
is hand crafted assembly which sets up all the segments as needed,
then essentially does memmove(0x100000,0x200000,kernel_size) and
finally does an unconditional jmp to 0x100000.
Amazingly this actually really does work. It has been successfully
tested with RHEL-2.1 and Fedora Core 6 install kernels on i386, and
Fedora Core 6 and 7 kernels on x86_64.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author | Keir Fraser <keir.fraser@citrix.com> |
---|---|
date | Fri Feb 01 11:16:37 2008 +0000 (2008-02-01) |
parents | 17cce0554151 |
children | 396ab902b02d |
files | tools/ioemu/hw/pc.c |
line diff
1.1 --- a/tools/ioemu/hw/pc.c Fri Feb 01 11:14:53 2008 +0000 1.2 +++ b/tools/ioemu/hw/pc.c Fri Feb 01 11:16:37 2008 +0000 1.3 @@ -417,6 +417,90 @@ static void generate_bootsect(uint32_t g 1.4 bdrv_set_boot_sector(bs_table[0], bootsect, sizeof(bootsect)); 1.5 } 1.6 1.7 +/* 1.8 + * Evil helper for non-relocatable kernels 1.9 + * 1.10 + * So it works out like this: 1.11 + * 1.12 + * 0x100000 - Xen HVM firmware lives here. Kernel wants to boot here 1.13 + * 1.14 + * You can't both live there and HVM firmware is needed first, thus 1.15 + * our plan is 1.16 + * 1.17 + * 0x200000 - kernel is loaded here by QEMU 1.18 + * 0x200000+kernel_size - helper code is put here by QEMU 1.19 + * 1.20 + * code32_switch in kernel header is set to point at out helper 1.21 + * code at 0x200000+kernel_size 1.22 + * 1.23 + * Our helper basically does memmove(0x100000,0x200000,kernel_size) 1.24 + * and then jmps to 0x1000000. 1.25 + * 1.26 + * So we've overwritten the HVM firmware (which was no longer 1.27 + * needed) and the non-relocatable kernel can happily boot 1.28 + * at its usual address. 1.29 + * 1.30 + * Simple, eh ? 1.31 + * 1.32 + * Well the assembler needed to do this is fairly short: 1.33 + * 1.34 + * # Load segments 1.35 + * cld 1.36 + * cli 1.37 + * movl $0x18,%eax 1.38 + * mov %ax,%ds 1.39 + * mov %ax,%es 1.40 + * mov %ax,%fs 1.41 + * mov %ax,%gs 1.42 + * mov %ax,%ss 1.43 + * 1.44 + * # Move the kernel into position 1.45 + * xor %edx,%edx 1.46 + *_doloop: 1.47 + * movzbl 0x600000(%edx),%eax 1.48 + * mov %al,0x100000(%edx) 1.49 + * add $0x1,%edx 1.50 + * cmp $0x500000,%edx 1.51 + * jne _doloop 1.52 + * 1.53 + * # start kernel 1.54 + * xorl %ebx,%ebx 1.55 + * mov $0x100000,%ecx 1.56 + * jmp *%ecx 1.57 + * 1.58 + */ 1.59 +static void setup_relocator(target_phys_addr_t addr, target_phys_addr_t src, target_phys_addr_t dst, size_t len) 1.60 +{ 1.61 + /* Now this assembler corresponds to follow machine code, with our args from QEMU spliced in :-) */ 1.62 + unsigned char buf[] = { 1.63 + /* Load segments */ 1.64 + 0xfc, /* cld */ 1.65 + 0xfa, /* cli */ 1.66 + 0xb8, 0x18, 0x00, 0x00, 0x00, /* mov $0x18,%eax */ 1.67 + 0x8e, 0xd8, /* mov %eax,%ds */ 1.68 + 0x8e, 0xc0, /* mov %eax,%es */ 1.69 + 0x8e, 0xe0, /* mov %eax,%fs */ 1.70 + 0x8e, 0xe8, /* mov %eax,%gs */ 1.71 + 0x8e, 0xd0, /* mov %eax,%ss */ 1.72 + 0x31, 0xd2, /* xor %edx,%edx */ 1.73 + 1.74 + /* Move the kernel into position */ 1.75 + 0x0f, 0xb6, 0x82, (src&0xff), ((src>>8)&0xff), ((src>>16)&0xff), ((src>>24)&0xff), /* movzbl $src(%edx),%eax */ 1.76 + 0x88, 0x82, (dst&0xff), ((dst>>8)&0xff), ((dst>>16)&0xff), ((dst>>24)&0xff), /* mov %al,$dst(%edx) */ 1.77 + 0x83, 0xc2, 0x01, /* add $0x1,%edx */ 1.78 + 0x81, 0xfa, (len&0xff), ((len>>8)&0xff), ((len>>16)&0xff), ((len>>24)&0xff), /* cmp $len,%edx */ 1.79 + 0x75, 0xe8, /* jne 13 <_doloop> */ 1.80 + 1.81 + /* Start kernel */ 1.82 + 0x31, 0xdb, /* xor %ebx,%ebx */ 1.83 + 0xb9, (dst&0xff), ((dst>>8)&0xff), ((dst>>16)&0xff), ((dst>>24)&0xff), /* mov $dst,%ecx */ 1.84 + 0xff, 0xe1, /* jmp *%ecx */ 1.85 + }; 1.86 + cpu_physical_memory_rw(addr, buf, sizeof(buf), 1); 1.87 + fprintf(stderr, "qemu: helper at 0x%x of size %d bytes, to move kernel of %d bytes from 0x%x to 0x%x\n", 1.88 + (int)addr, (int)sizeof(buf), (int)len, (int)src, (int)dst); 1.89 +} 1.90 + 1.91 1.92 static long get_file_size(FILE *f) 1.93 { 1.94 @@ -597,8 +681,15 @@ static void load_linux(const char *kerne 1.95 stl_p(header+0x214, reloc_prot_addr); 1.96 fprintf(stderr, "qemu: kernel is relocatable\n"); 1.97 } else { 1.98 - fprintf(stderr, "qemu: unable to load non-relocatable kernel\n"); 1.99 - exit(1); 1.100 + /* Setup a helper which moves kernel back to 1.101 + * its expected addr after firmware has got out 1.102 + * of the way. We put a helper at reloc_prot_addr+kernel_size. 1.103 + * It moves kernel from reloc_prot_addr to prot_addr and 1.104 + * then jumps to prot_addr. Yes this is sick. 1.105 + */ 1.106 + fprintf(stderr, "qemu: kernel is NOT relocatable\n"); 1.107 + stl_p(header+0x214, reloc_prot_addr + kernel_size); 1.108 + setup_relocator(reloc_prot_addr + kernel_size, reloc_prot_addr, prot_addr, kernel_size); 1.109 } 1.110 } 1.111