BIOS探秘:调试开源项目SeaBIOS

4 阅读5分钟

BIOS探秘:调试开源项目SeaBIOS

BIOS(Basic Input/Output System)是个人计算机启动过程中最先执行的软件,负责初始化硬件、检测系统配置,并将控制权移交给操作系统。在现代虚拟化技术中,模拟一个完整的BIOS系统仍然具有重要意义。

SeaBIOS是一个开源的x86 BIOS实现项目,它被广泛应用于QEMU/KVM等虚拟化平台。相比传统BIOS,SeaBIOS代码简洁、易于理解,是研究BIOS工作原理的绝佳入口。接下来,让我们走进BIOS的世界,学习如何编译和调试这个项目。


一、环境准备与编译

1.1 获取源码

首先,我们需要下载SeaBIOS的源代码。这里以1.17.0稳定版本为例:

# 通过git克隆仓库
git clone https://github.com/coreboot/seabios.git
cd seabios
git checkout rel-1.17.0

1.2 编译过程

SeaBIOS的编译过程非常简洁,正如官方README所言:

对于急性子的开发者,只需要简单两步就能在QEMU上构建并测试SeaBIOS:

make
qemu -bios out/bios.bin

切换到源码目录后,直接执行 make 命令即可开始编译:

cd seabios
make

编译完成后,会在 out/ 目录下生成 bios.bin 文件,这就是我们要的BIOS镜像。

1.3 准备测试环境

要测试这个BIOS,我们需要一个引导程序(MBR)。下面是一个极简的MBR示例,它会在屏幕上打印字母"X":

; boot.asm - 极简MBR引导程序
[bits 16]          ; 16位实模式
[org 0x7c00]       ; BIOS将MBR加载到0x7C00处
​
start:
    ; 初始化段寄存器
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    
    ; 设置栈指针(向低地址增长)
    mov sp, 0x7c00
​
    ; 使用BIOS中断0x10打印字符
    mov ah, 0x0e    ; 功能号:以TTY方式显示字符
    mov al, 'X'     ; 要显示的字符
    int 0x10        ; 调用视频服务中断
​
    ; 无限循环
    jmp $
​
; 填充至510字节,并加上MBR签名
times 510-($-$$) db 0
dw 0xaa55          ; MBR结束标志

使用NASM编译这个汇编程序:

nasm -f bin boot.asm -o boot.bin

1.4 运行测试

现在,我们可以使用QEMU来运行SeaBIOS并加载我们的MBR:

qemu-system-i386 \
    -drive format=raw,file=boot.bin \
    -bios out/bios.bin \
    -nographic

参数说明:

  • -drive format=raw,file=boot.bin:将boot.bin作为原始磁盘镜像
  • -bios out/bios.bin:使用我们编译的SeaBIOS
  • -nographic:禁用图形界面,使用控制台输出

如果一切正常,你将看到如下输出:

SeaBIOS (version ?-20260412_212404-VM-4-15-ubuntu)
Booting from Hard Disk...
X

字母"X"的出现证明BIOS成功识别了我们的MBR并将其加载执行!


二、源码级调试实战

仅仅运行BIOS还不够,作为开发者,我们需要深入了解其内部工作机制。SeaBIOS支持通过GDB进行源码级调试。

2.1 启用调试选项

首先,我们需要开启调试信息的编译。SeaBIOS使用Kconfig配置系统,可以通过两种方式修改配置:

方式一:使用menuconfig(推荐)

make menuconfig
# 在菜单中设置:
# - Debug level: 8
# - Serial debugging: enabled

方式二:直接编辑.config文件

# 编辑配置文件
cat > .config << 'EOF'
CONFIG_RELOCATE_INIT=n
CONFIG_DEBUG_LEVEL=8
CONFIG_DEBUG_SERIAL=y
EOF

# 重新编译
make clean
make

关键配置项解释:

  • CONFIG_DEBUG_LEVEL=8:设置最高调试级别,输出最详细的日志
  • CONFIG_DEBUG_SERIAL=y:启用串口调试输出

2.2 启动调试会话

QEMU内置了GDB服务器功能,可以让GDB远程连接调试。

步骤1:以调试模式启动QEMU

qemu-system-i386 \
    -drive format=raw,file=boot.bin \
    -bios out/bios.bin \
    -nographic \
    -S -s

新增参数说明:

  • -S:启动时暂停CPU,等待GDB连接
  • -s:在TCP端口1234上启动GDB服务器(等同于 -gdb tcp::1234

此时QEMU会暂停,终端显示等待状态。

步骤2:使用GDB连接调试

在另一个终端窗口中启动GDB:

gdb out/rom.o

out/rom.o 是编译生成的带调试信息的ELF文件。

步骤3:连接远程目标并调试

# 连接到QEMU的GDB服务器
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()

# 查看maininit函数的地址
(gdb) info address maininit
Symbol "maininit" is a function at address 0xe2fc1.

# 在maininit入口设置断点
(gdb) b *0xe2fc1
Breakpoint 1 at 0xe2fc1: file ./src/post.c, line 198.

# 继续执行,等待断点触发
(gdb) c
Continuing.

# 断点命中!
Breakpoint 1, maininit () at ./src/post.c:198
198     {
(gdb) 

调试要点:

  • 0x0000fff0 是x86处理器复位后的起始地址,这是BIOS入口点
  • maininit 函数是BIOS初始化阶段的核心函数,位于 src/post.c
  • 断点成功命中后,你就可以使用GDB的各种命令(stepnextprint等)单步跟踪BIOS的执行流程

三、总结

通过本文,我们完成了SeaBIOS的完整探索之旅:

  1. 编译阶段:了解了SeaBIOS简洁的构建流程,成功生成了BIOS镜像
  2. 运行阶段:编写了简单的MBR引导程序,在QEMU中验证BIOS功能
  3. 调试阶段:掌握了使用GDB进行源码级调试的方法,为深入分析BIOS源码打下基础

SeaBIOS的代码结构清晰,是学习x86系统底层原理的绝佳素材。建议读者接下来:

  • 深入研究 src/post.c 中的POST(Power-On Self Test)流程
  • 探索中断处理机制(src/stacks.c
  • 理解实模式到保护模式的切换过程

希望这篇指南能帮助你开启BIOS探秘之旅!


参考资源