qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] x86 Multiboot support (extended)
@ 2008-01-30 18:43 Alexander Graf
  2008-01-31  9:58 ` Kevin Wolf
  0 siblings, 1 reply; 10+ messages in thread
From: Alexander Graf @ 2008-01-30 18:43 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 989 bytes --]

Hi,

this patch implements x86 Multiboot support for the -kernel way of  
booting the virtual machine. Multiboot is a new approach to get rid of  
different bootloaders, providing a unified interface for the kernel.  
It supports command line options and kernel modules. The two probably  
best known projects using multiboot to boot are Xen and GNU Hurd. I  
have only tested Xen and a modified mach bootloader that makes use of  
multiboot so far.

This implementation should be mostly feature-complete. I have not  
implemented VBE extensions, but as no system uses them currently it  
does not really hurt. To use multiboot, specify the kernel as -kernel  
option. Modules should be given as -initrd options, seperated by a  
comma (,). The -append option is also supported.

Please bear in mind that grub also does gzip decompression, which does  
not occur with this multiboot implementation. To run existing images,  
please ungzip them first.

Comments are welcome.

Regards,

Alex

[-- Attachment #2: qemu-multiboot.patch --]
[-- Type: application/octet-stream, Size: 16064 bytes --]

diff --git a/elf_ops.h b/elf_ops.h
index 6126565..ab5fd7b 100644
--- a/elf_ops.h
+++ b/elf_ops.h
@@ -156,6 +156,10 @@ static int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend,
     }
 
     if (ELF_MACHINE != ehdr.e_machine)
+#if (ELF_MACHINE == EM_X86_64) && !CONFIG_USER_ONLY
+      /* x86_64 systems can run i386 code as well */
+      if(ehdr.e_machine != EM_386)
+#endif
         goto fail;
 
     if (pentry)
diff --git a/hw/pc.c b/hw/pc.c
index b4f0db7..f7b65fb 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -480,6 +480,411 @@ 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, *pmmaploop;
+    uint32_t ip;
+    int i;
+    int hda;
+    int mmaploop;
+
+    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);
+    }
+
+    memset(bootsect, 0, sizeof(bootsect));
+
+    /* Copy the MSDOS partition table if possible */
+    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;
+
+    /* Initialize multiboot mmap structs using the 0x15(e820) */
+    *p++ = 0x31;                /* XOR BX,BX */
+    *p++ = 0xdb;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xbf;                /* MOV EDI,0x9004 */
+    *p++ = 0x04;
+    *p++ = 0x90;
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    pmmaploop = p;
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xb8;                /* MOV EAX,0x20 */
+    *p++ = 0x20;
+    *p++ = 0x00;
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0x89;                /* MOV -4(EDI),EAX */
+    *p++ = 0x47;
+    *p++ = 0xfc;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xb8;                /* MOV EAX,0x0000e820 */
+    *p++ = 0x20;
+    *p++ = 0xe8;
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xba;                /* MOV EDX,0x534d4150 */
+    *p++ = 0x50;
+    *p++ = 0x41;
+    *p++ = 0x4d;
+    *p++ = 0x53;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xb9;                /* MOV ECX,0x20 */
+    *p++ = 0x20;
+    *p++ = 0x00;
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    *p++ = 0xcd;                /* INT 0x15 */
+    *p++ = 0x15;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xb8;                /* MOV EAX, 0x24 */
+    *p++ = 0x24;
+    *p++ = 0x00;
+    *p++ = 0x00;
+    *p++ = 0x00;
+    
+    *p++ = 0xf7;                /* MUL AX, BX */
+    *p++ = 0xe3;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0x21;                /* AND EBX, EBX */
+    *p++ = 0xdb;
+
+    /* don't store if bx = 0 */
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0x0f;                /* JZ next instruction */
+    *p++ = 0x84;
+    *p++ = 0x07;
+    *p++ = 0x00;
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    /* store the amount of blocks in the bootinfo struct */
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0xa3;                /* MOV [bootinfo+0x2c], EAX */
+    *p++ = (bootinfo+0x2c);
+    *p++ = (bootinfo+0x2c) >> 8;
+    *p++ = (bootinfo+0x2c) >> 16;
+    *p++ = (bootinfo+0x2c) >> 24;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0x05;                /* ADD EAX, 0x9004 */
+    *p++ = 0x04;
+    *p++ = 0x90;
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    *p++ = 0x89;                /* MOV DI, AX */
+    *p++ = 0xc7;
+
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0x21;                /* AND EBX, EBX */
+    *p++ = 0xdb;
+
+    /* process next entry */
+    *p++ = 0x66;                /* 32-bit operand size */
+    *p++ = 0x67;                /* 32-bit addr size */
+    *p++ = 0x0f;                /* JNZ mmaploop */
+    *p++ = 0x85;
+    mmaploop = (int)((long)pmmaploop) - ((long)p) - 4;
+    *p++ = mmaploop;
+    *p++ = mmaploop >> 8;
+    *p++ = mmaploop >> 16;
+    *p++ = mmaploop >> 24;
+    
+    /* 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;
+    uint32_t mh_entry_addr;
+    uint32_t mh_load_addr;
+    uint32_t mb_kernel_size;
+    uint32_t mb_bootinfo = 0x90000;
+    uint32_t tmp_size;
+
+    // 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
+        uint64_t elf_entry;
+        int kernel_size;
+        fclose(f);
+        kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL);
+        if(kernel_size < 0) {
+            fprintf(stderr, "Error while loading elf kernel\n");
+            exit(1);
+        }
+        mh_load_addr = mh_entry_addr = elf_entry;
+        mb_kernel_size = kernel_size;
+
+        fprintf(stderr, "qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n",
+                mb_kernel_size, (size_t)mh_entry_addr);
+    } else {
+        /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
+        uint32_t mh_header_addr = ldl_p(header+i+12);
+        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);
+        uint8_t *mb_kernel_addr = phys_ram_base + (mh_load_addr);
+        uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
+
+        mh_entry_addr = ldl_p(header+i+28);
+        mb_kernel_size = get_file_size(f) - mb_kernel_text_offset;
+
+        /* 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);
+        
+        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);
+    }
+
+
+    // load modules
+
+    stl_p(phys_ram_base + mb_bootinfo + 20, 0x0); // mods_count
+    if(initrd_filename) {
+        uint32_t mb_mod_info = mb_bootinfo + 0x100;
+        uint32_t mb_mod_start = mh_load_addr;
+        uint32_t mb_mod_length = mb_kernel_size;
+        char *next_initrd;
+        int mb_mod_count = 0;
+        
+        do {
+            next_initrd = strchr(initrd_filename, ',');
+            if(next_initrd)
+                *next_initrd = '\0';
+printf("multiboot loading module: %s\n", initrd_filename);
+            f = fopen(initrd_filename, "rb");
+            if(f) {
+                mb_mod_start = (mb_mod_start + mb_mod_length + (TARGET_PAGE_SIZE - 1))
+                             & (TARGET_PAGE_MASK);
+                mb_mod_length = get_file_size(f);
+
+                if ((tmp_size=fread((phys_ram_base + mb_mod_start), 1, mb_mod_length, f))
+                     != mb_mod_length) {
+                    fprintf(stderr, "qemu: read error on multiboot module '%s' (%#x != %#x)\n",
+                            initrd_filename, tmp_size, mb_mod_length);
+                    exit(1);
+                }
+
+                mb_mod_count++;
+                stl_p(phys_ram_base + mb_mod_info + 0, mb_mod_start);
+                stl_p(phys_ram_base + mb_mod_info + 4, mb_mod_start + mb_mod_length);
+printf("mod_start: %#x\nmod_end:   %#x\n", mb_mod_start, mb_mod_start + mb_mod_length);
+                stl_p(phys_ram_base + mb_mod_info + 8, 0x0); // string
+                stl_p(phys_ram_base + mb_mod_info + 12, 0x0); // reserved
+            }
+            initrd_filename = next_initrd+1;
+            mb_mod_info += 16;
+        } while(next_initrd);
+        stl_p(phys_ram_base + mb_bootinfo + 20, mb_mod_count); // mods_count
+        stl_p(phys_ram_base + mb_bootinfo + 24, mb_bootinfo + 0x100); // mods_addr
+    }
+
+    /* Commandline support */
+    stl_p(phys_ram_base + mb_bootinfo + 16, mb_bootinfo + 0x200);
+    strcpy((char*)(phys_ram_base + mb_bootinfo + 0x200), kernel_cmdline);
+        
+    // the kernel is where we want it to be now
+
+#define MULTIBOOT_FLAGS_MEMORY (1 << 0)
+#define MULTIBOOT_FLAGS_BOOT_DEVICE (1 << 1)
+#define MULTIBOOT_FLAGS_CMDLINE (1 << 2)
+#define MULTIBOOT_FLAGS_MODULES (1 << 3)
+#define MULTIBOOT_FLAGS_MMAP (1 << 6)
+    stl_p(phys_ram_base + mb_bootinfo, MULTIBOOT_FLAGS_MEMORY
+                                     | MULTIBOOT_FLAGS_BOOT_DEVICE
+                                     | MULTIBOOT_FLAGS_CMDLINE
+                                     | MULTIBOOT_FLAGS_MODULES
+                                     | MULTIBOOT_FLAGS_MMAP);
+    stl_p(phys_ram_base + mb_bootinfo + 4, 640 * 1024); // mem_lower
+    stl_p(phys_ram_base + mb_bootinfo + 8, ram_size); // mem_upper
+    stl_p(phys_ram_base + mb_bootinfo + 12, 0x8001ffff); // XXX: use the -boot switch?
+    stl_p(phys_ram_base + mb_bootinfo + 48, 0x9000); // mmap_addr
+
+    fprintf(stderr, "multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
+
+    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)
@@ -510,10 +915,15 @@ static void load_linux(const char *kernel_filename,
 #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 related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2008-03-12 14:37 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-30 18:43 [Qemu-devel] [PATCH] x86 Multiboot support (extended) Alexander Graf
2008-01-31  9:58 ` Kevin Wolf
2008-01-31 10:19   ` Alexander Graf
2008-01-31 17:31   ` Alexander Graf
2008-02-01  8:41     ` Kevin Wolf
2008-03-11 23:44     ` Aurelien Jarno
2008-03-12  4:57       ` Anthony Liguori
2008-03-12  8:24         ` Alexander Graf
2008-03-12 14:37           ` Anthony Liguori
2008-03-12  7:32       ` Alexander Graf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).