qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)


From: Alexander Graf
Subject: Re: [Qemu-devel] [PATCH] x86 Multiboot support (extended)
Date: Wed, 12 Mar 2008 08:32:29 +0100


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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]