* [Qemu-devel] [PATCH 8/9] Multiboot -kernel support
@ 2008-01-08 15:22 Alexander Graf
0 siblings, 0 replies; only message in thread
From: Alexander Graf @ 2008-01-08 15:22 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 384 bytes --]
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.
[-- Attachment #2: qemu-multiboot.patch --]
[-- Type: text/x-patch, Size: 9298 bytes --]
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 */
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2008-01-08 15:11 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-08 15:22 [Qemu-devel] [PATCH 8/9] Multiboot -kernel support Alexander Graf
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.