Page 1 of 1

QEMU 64 bit multiboot image

Posted: Tue Mar 23, 2021 8:33 am
by pvc
By default QEMU internal loader (`-kernel` option) can't load multiboot images from 64 bit ELF files. On the other hand GRUB does it perfectly fine. I wonder if there is any reason for this. `-kernel` option makes kernel debugging much easier, because there is no need to rebuild or modify whole disk image every time.

IMO, this limitation serves no purpose at all. I've removed the check from QEMU source and I am using it for over a month now. Nothing seems to be broken by my modification. I wanted to contribute my change to main QEMU repository but their contribution procedure is way too much hassle for an outsider. If anyone here is involved in QEMU project, feel free to forward my patch to the main project repo.

Code: Select all

diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c
index 9e7d69d470..e8d337744b 100644
--- a/hw/i386/multiboot.c
+++ b/hw/i386/multiboot.c
@@ -193,14 +193,10 @@ int load_multiboot(FWCfgState *fw_cfg,
         int kernel_size;
         fclose(f);
 
-        if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
-            error_report("Cannot load x86-64 image, give a 32bit one.");
-            exit(1);
-        }
-
         kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry,
                                &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE,
                                0, 0);
+
         if (kernel_size < 0) {
             error_report("Error while loading elf kernel");
             exit(1);

Re: QEMU 64 bit multiboot image

Posted: Tue Mar 23, 2021 8:03 pm
by bzt
pvc wrote:By default QEMU internal loader (`-kernel` option) can't load multiboot images from 64 bit ELF files.
That's no wonder. Neither multiboot nor the x86 Linux boot protocol supports 64 bit. (Using a protmode entry point in an elf64 image is not normal, that's a hack, only supported because the Linux kernel has it's trampoline code in place)

Or you could implement the x86 Linux boot protocol in your kernel, it's not so different to multiboot (a special structure at a fixed location), however won't set up framebuffer for you the simple way, you must provide a video card specific mode code for that.
pvc wrote:On the other hand GRUB does it perfectly fine.
I don't think GRUB can do that, multiboot specification is 32 bit only. There's a hack with multiboot2 headers to support 64 bit, but qemu only speaks the legacy multiboot protocol, and not multiboot2.
pvc wrote:I wonder if there is any reason for this. `-kernel` option makes kernel debugging much easier, because there is no need to rebuild or modify whole disk image every time.
Another possibility that I use often, is to wrap your kernel in ROM (like here, just a few lines). You could wrap the final binary too with a 10 SLoC minimal C code. I mean write a ROM header in Assembly that switches to long mode and calls your kernel's entry point. Then with a small C tool (or with incbin "kernel.elf64" if your Assembler supports that) add that header to your elf64 binary, calculate the checksum and you're good to go.

(The example I've linked loads the kernel over serial line, but you could just add the kernel to the image instead. Otherwise that small Assembly detects if the kernel is in elf32 or elf64 format, sets up environment correctly, parses the elf header for the entry point and jumps there. Less than 512 bytes altogether)

Disadvantage:
1. you need a small realmode-protmode-longmode trampoline code (but not in your kernel)

Advantages:
1. you can use "qemu -option-rom" command line switch to load your kernel quickly (just like with "-kernel"),
2. the trampoline code is part of the "header", no need to mess up your kernel with mixed code sections
2. no multiboot header nor other special data sections needed in your kernel
3. it works with other virtual machines (like bochs) too
4. since it is your header code that switches to long mode and jumps to the kernel, the kernel can be in any format: elf32, elf64, pe, pe32+, dwarf etc.
pvc wrote:IMO, this limitation serves no purpose at all.
Well, according to the qemu doc, the "-kernel" format supports multiboot and x86 Linux boot protocol. Neither of those support long mode, only 32 bit, so it makes sense. Allowing a kernel in elf64 format does not mean it will started in long-mode, and that only works for the Linux kernel because it has its own trampoline code (not necessarily the case for other elf64 binaries). It is expected that an elf64 binary requires a 64 bit long-mode entry point, something that qemu can't provide.
pvc wrote:I've removed the check from QEMU source and I am using it for over a month now. Nothing seems to be broken by my modification. I wanted to contribute my change to main QEMU repository but their contribution procedure is way too much hassle for an outsider. If anyone here is involved in QEMU project, feel free to forward my patch to the main project repo.
Yep, that's the same for insiders too, according to my experience. Specially a modification like this, removing a check that's mandated by the specifications could potentially brake things (just because it works for you, there's no guarantee that it will work correctly for all 64 bit kernel images). This needs huge amount of testing before it could be added imho. Be patient, that's my advice.

(I can see that qemu users start to complain, qemu accepts my elf64 kernel, but it does not switch to long mode for me, it calls the entry point in 32 bit mode!)

Cheers,
bzt