我尝试将以下代码用于兼容Multiboot2内核的标头,但是当我在grub中尝试multiboot2
命令时,它给出了以下错误消息:
错误:不支持的标记:0xc
我的多重引导 2 标头定义为:
section .multiboot align=4096
mbhead: dd 0xe85250d6
dd 0
dd 76
dd 0 - 76 - 0xe85250d6 ; TODO the linker and assembler get angry if we calculate this with symbols, we need to do this by hand
dw 1 ; multiboot information request
dw 0
dd 20
dd 1
dd 2
dd 6
dw 4 ; console flags
dw 0
dd 12
dd 0x3
dw 5 ; framebuffer settings
dw 1
dd 12
dd 80
dd 25
dd 0
dw 0 ; last tag
dw 0
dd 8
我的项目存储库提供完整的源代码。我生成一个带有make test_longmode.iso
的 ISO。我用QEMU测试。
导致标签错误的问题是什么,我该如何解决?
主要问题
GRUB 错误:
错误:不支持的标记:0xc
这是因为您没有按照 Multiboot2 规范对每个标签进行 8 字节对齐:
3.1.3 一般标签结构
标签构成结构的缓冲区,必要时相互填充,以便每个标签从 8 字节对齐的地址开始。标记由类型为"0"且大小为"8"的标记终止。每个结构都有以下格式
要轻松解决此问题,您可以在每个标记之前使用align 8
,并且可以让汇编程序处理对齐。此对齐方式不会计算到标记的长度中。
可以更好地计算校验和,以免因让 NASM 使用截断而导致警告。这一行:
dd 0 - 76 - 0xe85250d6
可以更好地表达为:
dd 0x100000000 - 76 - 0xe85250d6
代码可读性
我认为关于这个代码片段的第一件事是它的不可读性。引起我注意的是这条评论:
TODO 链接器和汇编器会生气,如果我们用符号计算这个,我们需要手动完成
似乎您可能已经手动计算了值,因为您在尝试以其他方式执行此操作时遇到了问题。在这里有帮助的NASM指令是EQU指令:
3.2.4 EQU:定义常量
EQU 将符号定义为给定的常量值:使用 EQU 时,源行必须包含标签。EQU 的操作是将给定的标签名称定义为其(仅)操作数的值。这个定义是绝对的,以后不能改变。所以,例如,
message db 'hello, world' msglen equ $-message
将 MSGLEN 定义为常量 12。msglen 以后可能不会重新定义。这也不是预处理器定义:msglen 的值在定义点使用 $ 的值(有关 $ 的说明,请参阅第 3.5 节)进行一次评估,而不是在引用它的地方进行评估并在引用点使用 $ 的值。
通过使用EQU,我们可以使代码更具可读性。
其次,您可以在每个标签的开头和结尾添加标签,然后让汇编程序为您计算每个标签的长度。长度是每个标签的结束标签和开始标签之间的差值。
改进的代码
具有上述建议更改的 NASM 程序集代码可能如下所示:
MB2_ARCH EQU 0 ; 0 = x86/x86-64
MB2_LEN EQU (mbend-mbhead)
MB2_MAGIC EQU 0xe85250d6
section .multiboot align=4096
mbhead:
dd MB2_MAGIC ; Multiboot2 magic number
dd MB2_ARCH ; Architecture
dd MB2_LEN ; Multiboot header length
dd 0x100000000 - MB2_LEN - MB2_ARCH - MB2_MAGIC
; Checksum
mb2_tag_info_start:
dw 1 ; multiboot information request
dw 0
dd mb2_tag_info_end - mb2_tag_info_start
dd 1
dd 2
dd 6
mb2_tag_info_end:
align 8
mb2_tag_console_start:
dw 4 ; console flags
dw 0
dd mb2_tag_console_end - mb2_tag_console_start
dd 0x3
mb2_tag_console_end:
align 8
mb2_tag_fb_start:
dw 5 ; framebuffer settings
dw 1
dd mb2_tag_fb_end - mb2_tag_fb_start
dd 80
dd 25
dd 0
mb2_tag_fb_end:
align 8
mb2_tag_end_start:
dw 0 ; last tag
dw 0
dd mb2_tag_end_end - mb2_tag_end_start
mb2_tag_end_end:
mbend:
这可以进一步改进,但即使这样看起来也比原始代码片段更具可读性和自我记录性。如果您更改标签或标头的大小,将为您计算所有长度。如果大小发生变化,align 8
指令将为您计算正确的对齐方式。这使得代码更易于维护。