如何使用GCC生成最小的BIOS hello world启动扇区,它可以在真实硬件上使用USB记忆棒?

Cir*_*四事件 12 x86 assembly qemu gnu-assembler bare-metal

我设法生成一个与QEMU 2.0.0 Ubuntu 14.04一起使用的最小引导扇区:

.code16
.global _start
_start:
    cli
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"
.org 510
.word 0xaa55
Run Code Online (Sandbox Code Playgroud)

编译:

as -o main.o main.S
ld --oformat binary -o main.img -Ttext 0x7C00 main.o
Run Code Online (Sandbox Code Playgroud)

这个例子可以在这个回购中找到:https://github.com/cirosantilli/x86-bare-metal-examples/tree/2b79ac21df801fbf4619d009411be6b9cd10e6e0/no-ld-script

一旦:

qemu -hda main.img
Run Code Online (Sandbox Code Playgroud)

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

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

sudo dd if=main.img of=/dev/sdb
Run Code Online (Sandbox Code Playgroud)

然后将USB插入ThinkPad T400或T430,点击F12,然后选择我观察到的USB:

  • 一些启动消息很快显示出来
  • 然后屏幕变为空白,顶部只有一个下划线光标

我也用Ubuntu 14.04映像测试了相同的USB,它启动正常,因此USB工作正常.

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

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

这记录在哪里?

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

Cir*_*四事件 9

正如@Jester所提到的,我不得不为DS:

@@ -4,2 +4,4 @@ _start:
     cli
+    xor %ax, %ax
+    mov %ax, %ds
     mov $msg, %si
Run Code Online (Sandbox Code Playgroud)

请注意,不可能mov立即ds:我们必须通过ax:8086-为什么我们不能将立即数据移动到段寄存器中?

所以问题的根源是QEMU的初始状态和真实硬件的初始状态之间的差异.

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

 .code16
cli
/* This sets %cs to 0. TODO Is that really needed? */
ljmp $0, $1f
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? https://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process
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
Run Code Online (Sandbox Code Playgroud)

我还发现了这个密切相关的问题:C内核 - 在VM上运行正常而不是真正的计算机吗?

英特尔手册卷3系统编程指南- 2015年325384-056US月 9.10.2"STARTUP.ASM清单"中含有大量的初始化例子.

  • 提示:要使寄存器清零,请使用xor%reg,%reg。这样可以将机器代码的长度从三个字节减少到一个字节,并且在现代8086兼容机上更快。只需使用`xor%ax,%ax`而不是`mov $ 0,%ax`。 (2认同)
  • BIOS例程不需要设置FS和GS.除非特定的BIOS例程将其作为传入参数的一部分,否则不需要设置ES. (2认同)
  • 至于BP,它可以用于任何事情。除非您有理由使用它,否则不需要对其进行初始化。BIOS 调用不使用堆栈帧,除非您试图与某种类型的约定兼容,否则不需要在您自己的代码中使用堆栈帧。BIOS 不在乎。如果 BIOS 调用需要 BP 作为参数,文档将列出该要求,并且只需要在调用之前进行设置。SS:SP 是另一回事。BIOS 例程和中断需要一个可用的 SS:SP。 (2认同)