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

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  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
  0 siblings, 2 replies; 10+ messages in thread
From: Kevin Wolf @ 2008-01-31  9:58 UTC (permalink / raw)
  To: qemu-devel

Hi,

I like this idea. When I just tried to load my multiboot kernel it
failed, though, because of the following piece of code:

> +    // 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) {

I wonder if there is any reason why you didn't just replace the 1024 by
8192 in load_linux but added an XXX. Would this cause any problems I
missed? With this change and replacing 256 by 8192 in the above code it
works for my kernel, too.

Anyway, I think the for condition should be i < 4 * (256 - 12), even
without changing the 1024.

Kevin

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  2008-01-31  9:58 ` Kevin Wolf
@ 2008-01-31 10:19   ` Alexander Graf
  2008-01-31 17:31   ` Alexander Graf
  1 sibling, 0 replies; 10+ messages in thread
From: Alexander Graf @ 2008-01-31 10:19 UTC (permalink / raw)
  To: qemu-devel


On Jan 31, 2008, at 10:58 AM, Kevin Wolf wrote:

> Hi,
>
> I like this idea. When I just tried to load my multiboot kernel it
> failed, though, because of the following piece of code:
>
>> +    // 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) {
>
> I wonder if there is any reason why you didn't just replace the 1024  
> by
> 8192 in load_linux but added an XXX. Would this cause any problems I
> missed? With this change and replacing 256 by 8192 in the above code  
> it
> works for my kernel, too.

The main reason behind it was that I did not want to change currently  
running code if not necessary. If nobody complains about it, I will  
gladly make the Linux loader load 8kb.

>
>
> Anyway, I think the for condition should be i < 4 * (256 - 12), even
> without changing the 1024.

Uhm. Yes. This looks wrong ;-). Thanks for the catch.

Alex

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  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
  1 sibling, 2 replies; 10+ messages in thread
From: Alexander Graf @ 2008-01-31 17:31 UTC (permalink / raw)
  To: qemu-devel

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


On Jan 31, 2008, at 10:58 AM, Kevin Wolf wrote:

> Hi,
>
> I like this idea. When I just tried to load my multiboot kernel it
> failed, though, because of the following piece of code:
>
>> +    // 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) {
>
> I wonder if there is any reason why you didn't just replace the 1024  
> by
> 8192 in load_linux but added an XXX. Would this cause any problems I
> missed? With this change and replacing 256 by 8192 in the above code  
> it
> works for my kernel, too.
>
> Anyway, I think the for condition should be i < 4 * (256 - 12), even
> without changing the 1024.

This version should fix the long header issue. I made the linux loader  
fetch the first 8kb as header, so multiboot can search through all  
necessary data.

I also implemented module parameters. Kevin needed this to boot a  
homebrew kernel. You can now pass commandline parameters to the  
multiboot modules by adding them after the filename, seperated through  
a space. This is the very same approach grub takes.

To boot a xen kernel you would give a command line like this:

qemu -hda image -kernel xen -initrd "vmlinux-xen root=/dev/hda,initrd- 
xen"

This way the vmlinux module gets the command line parameter "root=/dev/ 
hda".

Regards,

Alex

[-- Attachment #2: multiboot.new.patch --]
[-- Type: application/octet-stream, Size: 17877 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..4c5ee94 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -480,6 +480,416 @@ 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;
+
+    // Ok, let's see if it is a multiboot image
+    for(i = 0; i < 8144; i += 4) { // the header is 12x32bit long, so
+                                   // the latest entry may be 8192 - 48
+        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_cmdline = mb_bootinfo+ 0x300;
+        uint32_t mb_mod_start = mh_load_addr;
+        uint32_t mb_mod_length = mb_kernel_size;
+        char *next_initrd;
+        char *next_space;
+        int mb_mod_count = 0;
+        
+        do {
+            next_initrd = strchr(initrd_filename, ',');
+            if(next_initrd)
+                *next_initrd = '\0';
+            /* if a space comes after the module filename, treat everything after that as parameters */
+            strcpy(phys_ram_base + mb_mod_cmdline, initrd_filename);
+            stl_p(phys_ram_base + mb_mod_info + 8, mb_mod_cmdline); // string
+            mb_mod_cmdline += strlen(initrd_filename) + 1;
+            if(next_space = strchr(initrd_filename, ' '))
+                *next_space = '\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 + 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)
@@ -490,7 +900,7 @@ static void load_linux(const char *kernel_filename,
     uint16_t real_seg;
     int setup_size, kernel_size, initrd_size, cmdline_size;
     uint32_t initrd_max;
-    uint8_t header[1024];
+    uint8_t header[8192];
     uint8_t *real_addr, *prot_addr, *cmdline_addr, *initrd_addr;
     FILE *f, *fi;
 
@@ -500,7 +910,7 @@ static void load_linux(const char *kernel_filename,
     /* load the kernel header */
     f = fopen(kernel_filename, "rb");
     if (!f || !(kernel_size = get_file_size(f)) ||
-	fread(header, 1, 1024, f) != 1024) {
+	fread(header, 1, 8192, f) != 8192) {
 	fprintf(stderr, "qemu: could not load kernel '%s'\n",
 		kernel_filename);
 	exit(1);
@@ -510,10 +920,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 */
@@ -606,7 +1021,7 @@ static void load_linux(const char *kernel_filename,
     }
 
     /* store the finalized header and load the rest of the kernel */
-    memcpy(real_addr, header, 1024);
+    memcpy(real_addr, header, 8192);
 
     setup_size = header[0x1f1];
     if (setup_size == 0)
@@ -615,7 +1030,7 @@ static void load_linux(const char *kernel_filename,
     setup_size = (setup_size+1)*512;
     kernel_size -= setup_size;	/* Size of protected-mode code */
 
-    if (fread(real_addr+1024, 1, setup_size-1024, f) != setup_size-1024 ||
+    if (fread(real_addr+8192, 1, setup_size-8192, f) != setup_size-8192 ||
 	fread(prot_addr, 1, kernel_size, f) != kernel_size) {
 	fprintf(stderr, "qemu: read error on kernel '%s'\n",
 		kernel_filename);

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  2008-01-31 17:31   ` Alexander Graf
@ 2008-02-01  8:41     ` Kevin Wolf
  2008-03-11 23:44     ` Aurelien Jarno
  1 sibling, 0 replies; 10+ messages in thread
From: Kevin Wolf @ 2008-02-01  8:41 UTC (permalink / raw)
  To: qemu-devel

Alexander Graf schrieb:
> I also implemented module parameters. Kevin needed this to boot a
> homebrew kernel.

Well, in fact you needed it for Xen as well. ;-)

This new version of the patch works fine for me.

Kevin

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  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  7:32       ` Alexander Graf
  1 sibling, 2 replies; 10+ messages in thread
From: Aurelien Jarno @ 2008-03-11 23:44 UTC (permalink / raw)
  To: qemu-devel

On Thu, Jan 31, 2008 at 06:31:09PM +0100, Alexander Graf wrote:
>
> On Jan 31, 2008, at 10:58 AM, Kevin Wolf wrote:
>
>> Hi,
>>
>> I like this idea. When I just tried to load my multiboot kernel it
>> failed, though, because of the following piece of code:
>>
>>> +    // 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) {
>>
>> I wonder if there is any reason why you didn't just replace the 1024  
>> by
>> 8192 in load_linux but added an XXX. Would this cause any problems I
>> missed? With this change and replacing 256 by 8192 in the above code  
>> it
>> works for my kernel, too.
>>
>> Anyway, I think the for condition should be i < 4 * (256 - 12), even
>> without changing the 1024.
>
> This version should fix the long header issue. I made the linux loader  
> fetch the first 8kb as header, so multiboot can search through all  
> necessary data.
>
> I also implemented module parameters. Kevin needed this to boot a  
> homebrew kernel. You can now pass commandline parameters to the  
> multiboot modules by adding them after the filename, seperated through a 
> space. This is the very same approach grub takes.
>
> To boot a xen kernel you would give a command line like this:
>
> qemu -hda image -kernel xen -initrd "vmlinux-xen root=/dev/hda,initrd- 
> xen"
>
> This way the vmlinux module gets the command line parameter "root=/dev/ 
> hda".
>
> 
> 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..4c5ee94 100644
> --- a/hw/pc.c
> +++ b/hw/pc.c
> @@ -480,6 +480,416 @@ 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;
> +    }
> +    

Is it really necessary to have *that much* assembly code within hw/pc.c?

I would rather see multiboot support as a small image generated from
C and/or assembly code, loaded either with -hda or with a new option
having the same effect. This code could read the NVRAM to get the
variables it needs.

-- 
  .''`.  Aurelien Jarno	            | GPG: 1024D/F1BCDB73
 : :' :  Debian developer           | Electrical Engineer
 `. `'   aurel32@debian.org         | aurelien@aurel32.net
   `-    people.debian.org/~aurel32 | www.aurel32.net

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  2008-03-11 23:44     ` Aurelien Jarno
@ 2008-03-12  4:57       ` Anthony Liguori
  2008-03-12  8:24         ` Alexander Graf
  2008-03-12  7:32       ` Alexander Graf
  1 sibling, 1 reply; 10+ messages in thread
From: Anthony Liguori @ 2008-03-12  4:57 UTC (permalink / raw)
  To: qemu-devel

Aurelien Jarno wrote:
> On Thu, Jan 31, 2008 at 06:31:09PM +0100, Alexander Graf wrote:
>   
> Is it really necessary to have *that much* assembly code within hw/pc.c?
>
> I would rather see multiboot support as a small image generated from
> C and/or assembly code, loaded either with -hda or with a new option
> having the same effect. This code could read the NVRAM to get the
> variables it needs.
>   

Better yet to package it as an option ROM and eliminate all the boot 
sector hacks.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  2008-03-11 23:44     ` Aurelien Jarno
  2008-03-12  4:57       ` Anthony Liguori
@ 2008-03-12  7:32       ` Alexander Graf
  1 sibling, 0 replies; 10+ messages in thread
From: Alexander Graf @ 2008-03-12  7:32 UTC (permalink / raw)
  To: qemu-devel


On Mar 12, 2008, at 12:44 AM, Aurelien Jarno wrote:

> On Thu, Jan 31, 2008 at 06:31:09PM +0100, Alexander Graf wrote:
>>
>> On Jan 31, 2008, at 10:58 AM, Kevin Wolf wrote:
>>
>>> Hi,
>>>
>>> I like this idea. When I just tried to load my multiboot kernel it
>>> failed, though, because of the following piece of code:
>>>
>>>> +    // 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) {
>>>
>>> I wonder if there is any reason why you didn't just replace the 1024
>>> by
>>> 8192 in load_linux but added an XXX. Would this cause any problems I
>>> missed? With this change and replacing 256 by 8192 in the above code
>>> it
>>> works for my kernel, too.
>>>
>>> Anyway, I think the for condition should be i < 4 * (256 - 12), even
>>> without changing the 1024.
>>
>> This version should fix the long header issue. I made the linux  
>> loader
>> fetch the first 8kb as header, so multiboot can search through all
>> necessary data.
>>
>> I also implemented module parameters. Kevin needed this to boot a
>> homebrew kernel. You can now pass commandline parameters to the
>> multiboot modules by adding them after the filename, seperated  
>> through a
>> space. This is the very same approach grub takes.
>>
>> To boot a xen kernel you would give a command line like this:
>>
>> qemu -hda image -kernel xen -initrd "vmlinux-xen root=/dev/ 
>> hda,initrd-
>> xen"
>>
>> This way the vmlinux module gets the command line parameter "root=/ 
>> dev/
>> hda".
>>
>>
>> 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..4c5ee94 100644
>> --- a/hw/pc.c
>> +++ b/hw/pc.c
>> @@ -480,6 +480,416 @@ 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;
>> +    }
>> +
>
> Is it really necessary to have *that much* assembly code within hw/ 
> pc.c?
>
> I would rather see multiboot support as a small image generated from
> C and/or assembly code, loaded either with -hda or with a new option
> having the same effect. This code could read the NVRAM to get the
> variables it needs.

Well, we can't compile x86 assembly easily on platforms other than  
x86. This is basically the reason all these binary blobs firmware  
files are in.

So the only way would be to have a part in qemu that puts variables in  
the NVRAM, loads the kernel directly into RAM and loads other code  
(magically) from an external precompiled file to actually run the  
multiboot kernel. This would break down the code to two parts that  
necessarily depend on each other. It would also make it even harder to  
compile everything from source. I don't think it's that much of an  
improvement, do you?

I'm not saying it's not worth considering or basically a bad idea :-).  
I am merely thinking that this is something that should be in one  
place and is not too much of a problem for anyone, as it ends up being  
only a few functions in the C code.

Alex

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  2008-03-12  4:57       ` Anthony Liguori
@ 2008-03-12  8:24         ` Alexander Graf
  2008-03-12 14:37           ` Anthony Liguori
  0 siblings, 1 reply; 10+ messages in thread
From: Alexander Graf @ 2008-03-12  8:24 UTC (permalink / raw)
  To: qemu-devel


On Mar 12, 2008, at 5:57 AM, Anthony Liguori wrote:

> Aurelien Jarno wrote:
>> On Thu, Jan 31, 2008 at 06:31:09PM +0100, Alexander Graf wrote:
>>  Is it really necessary to have *that much* assembly code within hw/ 
>> pc.c?
>>
>> I would rather see multiboot support as a small image generated from
>> C and/or assembly code, loaded either with -hda or with a new option
>> having the same effect. This code could read the NVRAM to get the
>> variables it needs.
>>
>
> Better yet to package it as an option ROM and eliminate all the boot  
> sector hacks.

Better yet to package all kernel loading hacks in your option ROM and  
eliminate the bootsector hacks. Mind to do it? ;-).


Alex

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
  2008-03-12  8:24         ` Alexander Graf
@ 2008-03-12 14:37           ` Anthony Liguori
  0 siblings, 0 replies; 10+ messages in thread
From: Anthony Liguori @ 2008-03-12 14:37 UTC (permalink / raw)
  To: Alexander Graf; +Cc: qemu-devel

Alexander Graf wrote:
>
> On Mar 12, 2008, at 5:57 AM, Anthony Liguori wrote:
>
>> Aurelien Jarno wrote:
>>> On Thu, Jan 31, 2008 at 06:31:09PM +0100, Alexander Graf wrote:
>>>  Is it really necessary to have *that much* assembly code within 
>>> hw/pc.c?
>>>
>>> I would rather see multiboot support as a small image generated from
>>> C and/or assembly code, loaded either with -hda or with a new option
>>> having the same effect. This code could read the NVRAM to get the
>>> variables it needs.
>>>
>>
>> Better yet to package it as an option ROM and eliminate all the boot 
>> sector hacks.
>
> Better yet to package all kernel loading hacks in your option ROM and 
> eliminate the bootsector hacks. Mind to do it? ;-).

It's on my TODO, just not very high at the moment.

Regards,

Anthony Liguori

>
> Alex

^ permalink raw reply	[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).