I have a simple setup of a basic start.asm
(nasm assembler) and a multiboot2_header.asm
that get linked together once compiled. The multiboot2 header is at the beginning of the final ELF64-x86_64
-file. To run it, I use QEMU -> OVMF (UEFI) -> GRUB -> <my-binarỳ>
. When I use a minimal multiboot2 header, everything works: I'm in 32-bit mode and I can set up my own stack and call functions. To verify this, I check the registers in QEMU. But now I want to boot into EFI amd64 machine state with boot services enabled
, which is defined in section 3.5 multiboot2 spec [1], but this causes problems.
How I try to accomplish my goal:
The spec tells, that the multiboot2 header must contain the two tags EFI boot services tag: leaves UEFI boot services enabled
and EFI amd64 entry address tag of Multiboot2 header tag
. I'm confident I have done this right (code below).
Problem (UPDATE 2021-06-17)
After some more investigating, it looks like my approach is mostly(?) correct. The problem is, that my hlt
instruction is ignored. This way, more code got executed than expected and some execution poisoned eax
. If I change the hlt
in my start-symbol to an infinite unconditional jmp
to the address of this jmp
, the value in eax
is correct! (update end)
multiboot2-header.asm:
; This file uses "Netwide Assembler Syntax" and can be compiled by running
; `nasm -f elf64 multiboot2_header.asm -o multiboot2_header.o`
;
; External symbol, that comes "start.asm"
EXTERN start
ALIGN 8 ; according to spec, the header must be 64-bit (8 byte) aligned
section .multiboot_header
header_start:
; dd => int 32, see https://www.cs.uaf.edu/2017/fall/cs301/reference/x86_64.html
dd 0xe85250d6 ; magic number (multiboot 2 spec)
dd 0 ; architecture 0 (protected mode i386; spec doesn't specify many options)
dd header_end - header_start ; header length
; checksum
dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))
; OPTIONAL MULTIBOOT2 TAGS (additional to required END TAG)
; In order to boot into "EFI amd64 machine state with boot services enabled" (3.5 in Spec, 2021-06)
; machine state, we must specify a few additional tags:
;
; ------------------------------------------------------------------------------------
; "EFI boot services tag": leaves UEFI boot services enabled: its our task to exit them
ALIGN 8 ; alignment in bits, according to multiboot2 spec, tags are 8-byte (64bit) aligned
dw 7 ; type (16bit)
dw 0 ; flags (16bit)
dd 8 ; size (32bit)
; ------------------------------------------------------------------------------------
; "EFI amd64 entry address tag of Multiboot2 header tag"
ALIGN 8
dw 9 ; type (16bit)
dw 0 ; flags (16bit)
dd 12 ; size (32bit)
; TODO I'm not entirely sure how this works together with the "start" symbol from the linker script:
; perhaps the start symbol in the linker script is a fallback, if this is not found
dd start ; entry_addr (32bit)
; ------------------------------------------------------------------------------------
; REQUIRED END TAG
ALIGN 8
dw 0 ; type (16bit)
dw 0 ; flags (16bit)
dd 8 ; size (32bit)
header_end:
PS: The Rust tool bootinfo [2] correctly recognizes the multiboot2 header and the specified tags in my final ELF.
My initial approach was right and actually correct. I just couldn't properly check this because of two misunderstandings:
hlt
instruction didn't worked because QEMU got some interrupt and the hlt
got clearedYou can verify the registers in QEMU easily during startup/after handoff if the start-symbol looks like this:
GLOBAL start
SECTION .text
; produce x-bit x86 code (even if this will be an ELF-64 file)
[BITS 64]
start:
mov r12, 0xffffeeeeddddcccc ; verify that we are in 64-bit mode (otherwise this fails)
cli ; without clear interrupts, the hlt is ignored (I don't know what interrupt comes.. maybe the key input in qemu)
hlt