为何OS加载器在Bochs中正常运行却无法在QEMU中工作?
OS加载器在Bochs正常但QEMU无法工作的问题排查
我开发的OS加载器在Bochs模拟器中运行完全正常,但在QEMU中无法正常工作。通过GDB调试发现,QEMU环境下MBR未能将OS加载器正确读取到0x900内存地址,但IO端口操作无异常。以下是相关代码、配置及排查方向:
MBR代码
config/boot.asm
%ifndef BOOT_H %define BOOT_H ; Kernel loader address. LOADER_BASE_ADDR: equ 0x900 ; The logical sector address of the kernel loader on the disk. LOADER_START_SECTOR: equ 0x2 ; The number of sectors occupied by the kernel loader. LOADER_SECTORS: equ 4 %endif
mbr.asm
section MBR vstart=0x7C00 jmp main %include "config/boot.asm" [bits 16] main: mov ax, 0 mov ss, ax mov ds, ax mov fs, ax mov es, ax mov sp, $$ push bp mov bp, sp call init ; Read starting sector from disk. call read_disk ; Started supporting OS loader. jmp LOADER_BASE_ADDR init: push bp mov bp, sp ; Clean up BIOS output. mov ax, 0xB800 mov es, ax mov cx, 2000 mov di, 0 clean_screen: mov word [es: di], 0 add di, 2 loop clean_screen leave ret ; Read OS loader from disk. read_disk: push bp mov bp, sp ; Set the number of sectors to read. mov dx, 0x1F2 mov ax, LOADER_SECTORS out dx, al ; Set the logical sector address. mov ax, LOADER_START_SECTOR mov dx, 0x1F3 out dx, al mov cl, 8 mov dx, 0x1F4 shr ax, cl out dx, al shr ax, cl mov dx, 0x1F5 out dx, al shr ax, cl and al, 0x0F or al, 0xE0 mov dx, 0x1F6 out dx, al ; Send a read command. mov dx, 0x1F7 mov al, 0x20 out dx, al ; Wait for the hard drive to prepare the data. mov dx, 0x1F7 .read_disk_wait: nop in al, dx and al, 0x88 cmp al, 0x08 jnz .read_disk_wait ; The hard disk is ready to start reading data. mov di, LOADER_BASE_ADDR mov ax, LOADER_SECTORS mov dx, 256 mul dx mov cx, ax mov dx, 0x1F0 .read_disk_read: in ax, dx mov [di], ax add di, 2 loop .read_disk_read leave ret times 510-($-$$) db 0 db 0x55, 0xAA
内核加载器代码
section CORE_LOADER vstart=LOADER_BASE_ADDR jmp main %include "config/boot.asm" %include "config/gdt.asm" %include "print.asm" MESSAGE: db "Hello World", 0 STRLEN: equ $ - MESSAGE [bits 16] main: mov sp, $$ push bp mov bp, sp ; Load GDT, turn on protected mode. in al, 0x92 or al, 2 out 0x92, al lgdt [GDT_PTR] mov eax,cr0 or eax, 1 mov cr0, eax jmp dword SELECTOR_CODE:p_mode_start [bits 32] p_mode_start: mov esp, $$ mov ax, SELECTOR_DATA mov ds, ax mov ss, ax mov gs, ax mov es, ax ; clean screen call clean_screen ; Output "Hello World". push MESSAGE call print add esp, 8 jmp $
注:print代码与GDT结构已确认无问题
Makefile
SOURCE=src BUILD_DIR=build ASSEMBLER=@nasm -I $(SOURCE) DD=@dd OS_IMG=$(BUILD_DIR)/aszswaz.img MBR=$(BUILD_DIR)/mbr.bin OS_LOADER=$(BUILD_DIR)/os-loader.bin IMG_SECTOR=60 all: $(BUILD_DIR) \ $(OS_IMG) $(BUILD_DIR): @mkdir -p $@ .PHONY: clean clean: @rm -rf $(BUILD_DIR) # Build an OS image. $(OS_IMG): $(MBR) $(OS_LOADER) $(DD) if=/dev/zero of=$@ bs=1M count=$(IMG_SECTOR) >> /dev/null 2>&1 $(DD) if=$(MBR) of=$@ bs=512 count=1 conv=notrunc >> /dev/null 2>&1 $(DD) if=$(OS_LOADER) of=$@ bs=512 seek=2 conv=notrunc >> /dev/null 2>&1 $(MBR): $(SOURCE)/mbr.asm $(SOURCE)/print.asm $(SOURCE)/config/boot.asm $(ASSEMBLER) $< -o $@ $(OS_LOADER): $(SOURCE)/os-loader.asm $(SOURCE)/print.asm $(SOURCE)/config/boot.asm $(SOURCE)/config/gdt.asm $(ASSEMBLER) $< -o $@
Bochs配置
megs: 32 # Set BIOS and vga. romimage: file=/usr/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest boot: disk log: bochs.log mouse: enabled=0 keyboard: keymap=/usr/share/bochs/keymaps/x11-pc-us.map # Set up the hard disk. ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 ata0-master: type=disk, path=build/aszswaz.img,mode=flat,cylinders=121,heads=16,spt=63
QEMU启动命令
$ qemu-system-i386 \ -name 'guest=aszswaz' \ -m 1M \ -boot 'menu=on,strict=on' \ -drive 'file=build/aszswaz.img,format=raw'
版本信息
$ bochs --help ======================================================================== Bochs x86 Emulator 2.7 Built from SVN snapshot on August 1, 2021 Timestamp: Sun Aug 1 10:07:00 CEST 2021 ======================================================================== ... $ qemu-system-i386 -version QEMU emulator version 7.1.0 Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
排查方向及解决方案
1. 磁盘几何参数不匹配
Bochs配置中明确指定了磁盘参数cylinders=121,heads=16,spt=63,但QEMU启动时未指定这些参数,QEMU会自动推断磁盘几何结构,可能与Bochs的解析逻辑不一致,导致LBA地址映射错误。
解决方法:在QEMU命令中添加磁盘几何参数:
$ qemu-system-i386 \ -name 'guest=aszswaz' \ -m 32M \ -boot 'menu=on,strict=on' \ -drive 'file=build/aszswaz.img,format=raw,cyls=121,heads=16,secs=63'
2. 磁盘就绪等待逻辑缺陷
当前代码中等待磁盘就绪的逻辑是and al, 0x88后判断等于0x08,但正确的磁盘就绪判断应该先检查忙位(bit7)是否为0,再检查就绪位(bit3)是否为1。QEMU的磁盘状态变化更快,可能导致当前逻辑无法正确触发。
修改后的等待逻辑:
.read_disk_wait: in al, dx test al, 0x80 ; 检查忙位,为1表示磁盘忙 jnz .read_disk_wait test al, 0x08 ; 检查就绪位,为1表示磁盘就绪 jz .read_disk_wait
3. 内存限制差异
Bochs设置了32M内存,而QEMU仅设置1M内存。虽然0x900在1M范围内,但BIOS在不同内存配置下的初始化行为可能有差异,导致目标内存区域被覆盖。
解决方法:将QEMU的内存调整为32M,即-m 32M。
4. 加载器文件大小验证
确认os-loader.bin的大小是否正好是4个扇区(2048字节),如果实际大小不足,QEMU读取时会获取到空数据。
验证命令:
wc -c build/os-loader.bin
如果大小不足2048字节,可在加载器代码末尾添加times 2048-($-$$) db 0填充到4个扇区。
内容的提问来源于stack exchange,提问作者aszswaz
相关产品推荐
相关产品推荐

