All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexander Graf <alex@csgraf.de>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH 8/9] Multiboot -kernel support
Date: Tue, 08 Jan 2008 16:22:59 +0100	[thread overview]
Message-ID: <47839553.1020108@csgraf.de> (raw)

[-- 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 */

                 reply	other threads:[~2008-01-08 15:11 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=47839553.1020108@csgraf.de \
    --to=alex@csgraf.de \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.