Bootloader无法跳转至Stage 2显示消息的问题排查求助
32位内核Bootloader Stage1到Stage2跳转失败问题排查
我正在为32位内核开发Bootloader,遇到了Stage 1到Stage 2的跳转异常问题,代码未按预期跳转至Stage 2执行。使用NASM汇编、QEMU仿真,已通过HxD验证.img文件内容,但QEMU运行时Stage 2的消息Hello from Stage 2! :)并未显示。
Bootloader设计逻辑:
- 配置段寄存器和栈;
- 从磁盘读取Stage 2至内存;
- 跳转至Stage 2的加载地址执行。
相关代码与构建文件
Stage 1 代码
[BITS 16] ; 工作在16位实模式 [org 0x7c00] ; BIOS加载Bootloader的起始物理地址 start: ; 代码入口点 jmp main ; 跳过数据区,直接执行主逻辑 main: ; Bootloader主程序开始 ; ------------------------- ; 设置段寄存器 ; ------------------------- cli ; 关闭中断,避免段寄存器设置被干扰 mov ax, 0x7C0 ; AX = 0x7C0,对应物理地址0x7C00(0x7C0*16) mov ds, ax ; 数据段指向Bootloader所在内存 mov es, ax ; 附加段指向Bootloader所在内存 mov fs, ax ; FS段设置 mov gs, ax ; GS段设置 ; ------------------------- ; 设置栈 ; ------------------------- xor ax, ax ; AX清零 mov ss, ax ; 栈段设为0 mov sp, 0xFFFF ; 栈指针指向内存顶部,栈向下生长 sti ; 重新启用中断 ; ------------------------- ; 从磁盘加载Stage 2 ; ------------------------- mov ah, 02h ; BIOS INT13h功能:读磁盘扇区 mov al, 01h ; 读取1个扇区(注释写63扇区,与实际值不符) mov ch, 00h ; 柱面号0(注释写1,与实际值不符) mov cl, 02h ; 扇区号2(从1开始计数) mov dh, 00h ; 磁头号0 mov dl, 0x80 ; 目标磁盘:第一块硬盘 mov es, ax ; ES设为0x7C0 mov bx, 0x8000 ; BX设为0x8000,ES:BX指向物理地址0xFC00 int 13h ; 调用BIOS中断读磁盘 jc disk_read_error ; 若读盘失败(进位标志置位),跳转到错误处理 pass: ; 读盘成功后执行 jmp 0x0800:0x0000 ; 跳转至物理地址0x8000(与加载地址不匹配) disk_read_error: int 18h ; 调用INT18h尝试其他引导方式 TIMES 510-($-$$) DB 0 ; 填充至512字节 DW 0xAA55 ; BIOS引导签名
Stage 2 代码
[BITS 16] [ORG 0x8000] start: mov si, message call print_string cli hlt print_string: mov ah, 0x0E .next_char: lodsb cmp al, 0 je done int 0x10 jmp .next_char done: ret message db "Hello from Stage 2! :)", 0 TIMES 510-($-$$) db 0 ; 冗余的引导扇区填充 DW 0xAA55 ; 冗余的引导签名
Makefile
PROJECT_ROOT := $(CURDIR) STAGE1_SRC := $(PROJECT_ROOT)\Boot\Stage1\stage1.asm STAGE2_SRC := $(PROJECT_ROOT)\Boot\Stage2\stage2.asm STAGE1_BIN := $(PROJECT_ROOT)\Boot\Stage1\stage1.bin STAGE2_BIN := $(PROJECT_ROOT)\Boot\Stage2\stage2.bin IMG := C:\Users\ilanv\OS_32Bit\Code\bootloader.img QEMU_IMG := "C:/QEMU/qemu-img.exe" DD := "C:/dd/dd.exe" output: $(STAGE1_BIN) $(STAGE2_BIN) $(IMG) $(STAGE1_BIN): $(STAGE1_SRC) @echo "Assembling Stage 1..." nasm -f bin "$(STAGE1_SRC)" -o "$(STAGE1_BIN)" $(STAGE2_BIN): $(STAGE2_SRC) @echo "Assembling Stage 2..." nasm -f bin "$(STAGE2_SRC)" -o "$(STAGE2_BIN)" $(IMG): $(STAGE1_BIN) $(STAGE2_BIN) @echo "Creating bootloader.img..." $(QEMU_IMG) create -f raw "$(IMG)" 10M $(DD) if=$(STAGE1_BIN) of=$(IMG) bs=512 count=1 $(DD) if=$(STAGE2_BIN) of=$(IMG) bs=512 seek=1 clean: @echo "Cleaning up..." if exist "$(STAGE1_BIN)" del "$(STAGE1_BIN)" if exist "$(STAGE2_BIN)" del "$(STAGE2_BIN)" if exist "$(IMG)" del "$(IMG)"
问题原因及修复方案
1. 加载地址与跳转地址不匹配(核心错误)
Stage1中,你设置ES=0x7C0、BX=0x8000,对应物理加载地址为:0x7C0 * 16 + 0x8000 = 0x7C00 + 0x8000 = 0xFC00
但跳转地址0x0800:0x0000对应的物理地址是:0x0800 * 16 + 0x0000 = 0x8000
两个地址完全不匹配,导致跳转后执行的是空白内存,而非Stage2代码。
修复:修改Stage1的磁盘加载段寄存器配置,让加载地址与跳转地址一致:
mov ax, 0x0800 ; 段地址设为0x0800,对应物理地址0x8000 mov es, ax mov bx, 0x0000 ; 偏移设为0,ES:BX指向0x8000
2. 代码注释与实际值不符
mov al, 01h注释写“读63扇区”,但实际只读取1扇区,修正注释避免混淆;mov ch, 00h注释写“柱面1”,但实际是柱面0,修正注释。
3. Stage2冗余的引导扇区填充
Stage2不是BIOS需要识别的引导扇区,不需要TIMES 510-($-$$) db 0和DW 0xAA55,删除这些冗余代码。
4. 可选优化:增加错误提示
当前磁盘读错误直接调用int 18h,可以添加错误字符串打印逻辑,方便调试:
disk_read_error: mov si, error_msg call print_string ; 需要实现类似Stage2的print_string函数 int 18h error_msg db "Disk read failed!", 0
内容的提问来源于stack exchange,提问作者Ilan Vinograd
相关产品推荐
相关产品推荐

