multiboot2 标头正确进入“启用引导服务的 EFI amd64 机器状态”- hlt 指令不起作用?

问题描述

我有一个基本的 start.asm(nasm 汇编程序)和一个 multiboot2_header.asm 的简单设置,它们在编译后链接在一起。 multiboot2 标头位于最终 ELF64-x86_64 文件的开头。要运行它,我使用 QEMU -> OVMF (UEFI) -> GRUB -> <my-binarỳ>。当我使用最小的 multiboot2 标头时,一切正常:我处于 32 位模式,我可以设置自己的堆栈和调用函数。为了验证这一点,我检查了 QEMU 中的寄存器。但现在我想启动到 EFI amd64 machine state with boot services enabled,它在 3.5 multiboot2 规范 [1] 部分中定义,但这会导致问题。

我如何努力实现我的目标: 规范说明,multiboot2 标头必须包含两个标签 EFI boot services tag: leaves UEFI boot services enabledEFI amd64 entry address tag of Multiboot2 header tag。我相信我做对了(代码如下)。

问题(更新 2021-06-17) 经过更多调查,看起来我的方法大部分(?)是正确的。问题是,我的 hlt 指令被忽略了。这样,执行的代码比预期的要多,并且某些执行使 eax 中毒。如果我将起始符号中的 hlt 更改为此 jmp 的地址的无限无条件 jmp,则 eax 中的值是正确的! (更新结束)

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:Rust 工具 bootinfo [2] 正确识别了 multiboot2 标头和我最终 ELF 中的指定标签。

解决方法

我最初的方法是正确的,实际上也是正确的。由于两个误解,我无法正确检查:

  • 我实际上处于 64 位长模式,但尝试执行 32 位代码
  • hlt 指令没有工作,因为 QEMU 有一些中断并且 hlt 被清除

如果启动符号如下所示,您可以在启动期间/切换后轻松验证 QEMU 中的寄存器:

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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...