[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 8/9] Multiboot -kernel support
From: |
Alexander Graf |
Subject: |
[Qemu-devel] [PATCH 8/9] Multiboot -kernel support |
Date: |
Tue, 08 Jan 2008 16:22:59 +0100 |
User-agent: |
Thunderbird 2.0.0.9 (X11/20070801) |
To boot Mac OS X a mach bootloader has to be run. David Elliot modified
the original i386 Mac OS X BIOS based bootloader to emulate the EFI
entries and support multiboot, an easy but straight-forward kernel
loading interface founded by grub. To boot multiboot-compliant kernels
directly, this adds support for multiboot loading to the -kernel option,
if a Linux kernel was not found.
Index: qemu-snapshot-2008-01-08_05/hw/pc.c
===================================================================
--- qemu-snapshot-2008-01-08_05.orig/hw/pc.c
+++ qemu-snapshot-2008-01-08_05/hw/pc.c
@@ -493,6 +493,226 @@ static long get_file_size(FILE *f)
return size;
}
+/* Generate an initial boot sector which sets state and jump to
+ a specified vector */
+static void generate_bootsect_multiboot(uint32_t mh_entry_addr, uint32_t
bootinfo)
+{
+ uint8_t bootsect[512], *p, *pgdt;
+ uint32_t ip;
+ int i;
+ int hda;
+
+ hda = drive_get_index(IF_IDE, 0, 0);
+ if (hda == -1) {
+ fprintf(stderr, "A disk image must be given for 'hda' when booting "
+ "a Multiboot kernel\n");
+ exit(1);
+ /* Copy the MSDOS partition table if possible */
+ }
+
+ memset(bootsect, 0, sizeof(bootsect));
+ bdrv_read(drives_table[hda].bdrv, 0, bootsect, 1);
+
+ /* Make sure we have a partition signature */
+ bootsect[510] = 0x55;
+ bootsect[511] = 0xaa;
+
+ /* Actual code */
+ p = bootsect;
+ *p++ = 0xfa; /* CLI */
+ *p++ = 0xfc; /* CLD */
+
+ // 660f011528000000 lgdt [0x28]
+ *p++ = 0x66; /* 32-bit operand size */
+ *p++ = 0x67; /* 32-bit addr size */
+ *p++ = 0x0f; /* LGDT [0x128] */
+ *p++ = 0x01;
+ *p++ = 0x15;
+ pgdt=p; /* we calculate the gdt position later */
+ p+=4;
+
+ /* get us to protected mode now */
+
+ *p++ = 0x66;
+ *p++ = 0xb8; /* MOV EAX,0x01 */
+ *p++ = 0x01;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ *p++ = 0x00;
+
+ *p++ = 0x0f; /* MOV CR0,EAX */
+ *p++ = 0x22;
+ *p++ = 0xc0;
+
+ /* the JMP sets CS for us and gets us to 32-bit */
+ ip = 0x00007c00 + (p - bootsect) + 8; // set i to the IP after the JMP
+ *p++ = 0x66; /* 32-bit operand size */
+ *p++ = 0xea; /* JMP */
+ *p++ = ip; /* IP */
+ *p++ = ip >> 8;
+ *p++ = ip >> 16;
+ *p++ = ip >> 24;
+ *p++ = 0x08;
+ *p++ = 0x00;
+
+ /* initialize all other segments */
+ *p++ = 0xb8; /* MOV EAX,0x10 */
+ *p++ = 0x10;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ for (i = 0; i < 6; i++) {
+ if (i == 1) /* Skip CS */
+ continue;
+
+ *p++ = 0x8e; /* MOV <seg>,EAX */
+ *p++ = 0xc0 + (i << 3);
+ }
+
+ /* EBX contains a pointer to the bootinfo struct */
+ *p++ = 0xbb; /* MOV EBX,imm32 */
+ *p++ = bootinfo;
+ *p++ = bootinfo >> 8;
+ *p++ = bootinfo >> 16;
+ *p++ = bootinfo >> 24;
+
+ /* EAX has to contain the following magic */
+ *p++ = 0xb8; /* MOV EAX,0x2badb002 */
+ *p++ = 0x02;
+ *p++ = 0xb0;
+ *p++ = 0xad;
+ *p++ = 0x2b;
+
+ /* Jump off to the kernel */
+ *p++ = 0xea; /* JMP */
+ *p++ = mh_entry_addr; /* IP */
+ *p++ = mh_entry_addr >> 8;
+ *p++ = mh_entry_addr >> 16;
+ *p++ = mh_entry_addr >> 24;
+ *p++ = 0x08;
+ *p++ = 0x00;
+
+ { /* GDT loading */
+ uint32_t gdt_base = 0x00007c00 + (p - bootsect); // 0x00007c00 is the
first IP;
+ uint32_t gdtr = gdt_base + 0x28;
+ uint8_t gdt[] = { // GDT base: 0x00000100
+ // 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x08: code segment (base=0, limit=0xfffff, type=32bit code
exec/read, DPL=0, 4k)
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00,
+ // 0x10: data segment (base=0, limit=0xfffff, type=32bit data
read/write, DPL=0, 4k)
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00,
+ // 0x18: code segment (base=0, limit=0x0ffff, type=16bit code
exec/read/conf, DPL=0, 1b)
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00,
+ // 0x20: data segment (base=0, limit=0x0ffff, type=16bit data
read/write, DPL=0, 1b)
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00,
+ // 0x28: gdtdesc
+ 0x27, 0x00, gdt_base, gdt_base >> 8, gdt_base >> 16, gdt_base >> 24
+ };
+
+ memcpy(p, gdt, sizeof(gdt));
+ p+=sizeof(gdt);
+ *pgdt++ = gdtr;
+ *pgdt++ = gdtr >> 8;
+ *pgdt++ = gdtr >> 16;
+ *pgdt++ = gdtr >> 24;
+ }
+
+ fprintf(stderr, "qemu: multiboot loader code is %d bytes long.\n",
(int)(p-bootsect));
+
+ bdrv_set_boot_sector(drives_table[hda].bdrv, bootsect, sizeof(bootsect));
+}
+
+static int load_multiboot(FILE *f,
+ const char *kernel_filename,
+ const char *initrd_filename,
+ const char *kernel_cmdline,
+ uint8_t *header)
+{
+ int i, is_multiboot = 0;
+ uint32_t flags = 0;
+ // XXX: multiboot header may be within the first 8192 bytes, but header
+ // is only the first 1024
+
+
+ // Ok, let's see if it is a multiboot image
+ for(i=0; i<(256 - 12); i+=4) { // the header is 12x32bit long
+ if(ldl_p(header+i) == 0x1BADB002) {
+ uint32_t checksum = ldl_p(header+i+8);
+ flags = ldl_p(header+i+4);
+ checksum += flags;
+ checksum += (uint32_t)0x1BADB002;
+ if(!checksum) {
+ is_multiboot = 1;
+ break;
+ }
+ }
+ }
+
+ if(!is_multiboot) return 0; // no multiboot
+ fprintf(stderr, "qemu: I believe we found a multiboot image!\n");
+
+ if(flags & 0x00000004) { // MULTIBOOT_HEADER_HAS_VBE
+ fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n");
+ }
+ if(!(flags & 0x00010000)) { // MULTIBOOT_HEADER_HAS_ADDR
+ // XXX: multiboot knows ELF. we don't.
+ fprintf(stderr, "qemu: multiboot knows ELF. we don't.\n");
+ return 0;
+ } else {
+ /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
+ uint32_t mh_header_addr = ldl_p(header+i+12);
+ uint32_t mh_load_addr = ldl_p(header+i+16);
+ uint32_t mh_load_end_addr = ldl_p(header+i+20);
+ uint32_t mh_bss_end_addr = ldl_p(header+i+24);
+ uint32_t mh_entry_addr = ldl_p(header+i+28);
+ uint8_t *mb_kernel_addr = phys_ram_base + (mh_load_addr);
+ uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
+ uint32_t mb_kernel_size = get_file_size(f) - mb_kernel_text_offset;
+ uint32_t mb_bootinfo = 0x100000;
+ uint32_t tmp_size;
+
+ /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE.
+ uint32_t mh_mode_type = ldl_p(header+i+32);
+ uint32_t mh_width = ldl_p(header+i+36);
+ uint32_t mh_height = ldl_p(header+i+40);
+ uint32_t mh_depth = ldl_p(header+i+44); */
+
+ fprintf(stderr, "multiboot: mh_header_addr = %#x\n", mh_header_addr);
+ fprintf(stderr, "multiboot: mh_load_addr = %#x\n", mh_load_addr);
+ fprintf(stderr, "multiboot: mh_load_end_addr = %#x\n",
mh_load_end_addr);
+ fprintf(stderr, "multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr);
+ fprintf(stderr, "multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
+
+ fseek(f, mb_kernel_text_offset, SEEK_SET);
+
+ fprintf(stderr, "qemu: loading multiboot kernel (%#x bytes) at %#zx\n",
+ mb_kernel_size, mb_kernel_addr - phys_ram_base);
+
+ if ((tmp_size=fread(mb_kernel_addr, 1, mb_kernel_size, f)) !=
mb_kernel_size) {
+ fprintf(stderr, "qemu: read error on multiboot kernel '%s' (%#x !=
%#x)\n", kernel_filename, tmp_size, mb_kernel_size);
+ exit(1);
+ }
+ fclose(f);
+
+ // the kernel is where we want it to be now
+ // XXX: add module support (hurd, xen)
+
+ // XXX: add support for:
+ // - mem_lower (4), mem_upper (8) flags[0]
+ // - cmdline (12) flags[2]
+ // - mods_count (20), mods_addr(24) flags[3]
+ // - syms (28 - 40) flags[4]
+ // - mmap_length (44), mmmap_addr (48) flags[6]
+ stl_p(phys_ram_base + mb_bootinfo, 2); // we only support the
boot_device info
+ stl_p(phys_ram_base + mb_bootinfo + 12, 0x8001ffff); // XXX: use the
-boot switch
+
+ generate_bootsect_multiboot(mh_entry_addr, mb_bootinfo);
+ }
+
+ return 1; // yes, we are multiboot
+}
+
static void load_linux(const char *kernel_filename,
const char *initrd_filename,
const char *kernel_cmdline)
@@ -523,10 +743,15 @@ static void load_linux(const char *kerne
#if 0
fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
#endif
- if (ldl_p(header+0x202) == 0x53726448)
- protocol = lduw_p(header+0x206);
- else
- protocol = 0;
+ if (ldl_p(header+0x202) == 0x53726448) {
+ protocol = lduw_p(header+0x206);
+ } else {
+ // This looks like a multiboot kernel. If it is, let's stop
+ // treating it like Linux.
+ if(load_multiboot(f,kernel_filename,initrd_filename, kernel_cmdline,
header))
+ return;
+ protocol = 0;
+ }
if (protocol < 0x200 || !(header[0x211] & 0x01)) {
/* Low kernel */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH 8/9] Multiboot -kernel support,
Alexander Graf <=