如何使用 GCC 生成最小的 BIOS hello world 引导扇区,该引导扇区可以在真实硬件上从 USB 记忆棒运行?

How to produce a minimal BIOS hello world boot sector with GCC that works from a USB stick on real hardware?

我已经成功地生成了一个与 QEMU 2.0.0 一起工作的最小引导扇区 Ubuntu 14.04:

.code16
.global _start
_start:
    cli
    mov $msg, %si
    mov [=10=]x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int [=10=]x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"
.org 510
.word 0xaa55

编译:

as -o main.o main.S
ld --oformat binary -o main.img -Ttext 0x7C00 main.o

示例可在此存储库中找到:https://github.com/cirosantilli/x86-bare-metal-examples/tree/2b79ac21df801fbf4619d009411be6b9cd10e6e0/no-ld-script

基于:

qemu -hda main.img

它按预期在模拟器屏幕上显示 hello world

但是如果我尝试刻录到 USB:

sudo dd if=main.img of=/dev/sdb

然后将 USB 插入 ThinkPad T400 或 T430,按 F12,然后 select USB 我观察到的是:

我还用 Ubuntu 14.04 映像测试了相同的 USB,它启动正常,所以 USB 工作正常。

我应该如何更改此示例,以便它在硬件上启动并显示 hello world 消息?

Ubuntu 图片和我创建的图片有什么区别?

这个记录在哪里?

我已将 sudo dmidecode 在 T400 上的输出上传到:https://gist.github.com/cirosantilli/d47d35bacc9be588009f#file-lenovo-t400

如@Jester 所述,我必须将 DS 归零:

@@ -4,2 +4,4 @@ _start:
     cli
+    xor %ax, %ax
+    mov %ax, %ds
     mov $msg, %si

注意不可能mov立即到ds:我们必须通过ax8086- why can't we move an immediate data into segment register?

所以问题的根源是QEMU的初始状态和真实硬件的不同。

我现在将以下 16 位初始化代码添加到我的所有引导加载程序中,以保证更清晰的初始状态。正如 Michael Petch 在评论中提到的那样,并非所有这些都是强制性的。

 .code16
cli
/* This sets %cs to 0. TODO Is that really needed? */
ljmp [=11=], f
1:
xor %ax, %ax
/* We must zero %ds for any data access. */
mov %ax, %ds
/* The other segments are not mandatory. TODO source */
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
/*
TODO What to move into BP and SP? 
Setting BP does not seem mandatory for BIOS.
*/
mov %ax, %bp
/* Automatically disables interrupts until the end of the next instruction. */
mov %ax, %ss
/* We should set SP because BIOS calls may depend on that. TODO confirm. */
mov %bp, %sp

我还发现了这个密切相关的问题:

Intel Manual Volume 3 System Programming Guide - 325384-056US September 20159.10.2》STARTUP.ASM上市 " 包含一个大型初始化示例。