You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

INT 13h AH=42h调用失败(AH=1,CF=1),Bootloader磁盘读取求助

INT 13h AH=42h读取失败(AH=1、CF=1)排查与解决

我正在编写一个简单Bootloader,目标是将LBA=1位置的程序加载至内存0x1000并跳转执行。使用QEMU的-drive参数仿真时,调用INT 13h AH=42h读取磁盘块失败,返回AH=1、CF=1。已排查参数传递和DAP结构,未发现明显问题,附上相关源码、Makefile、链接脚本及调用前寄存器状态,请求排查原因并解决。

源码文件(GNU as编译)

boot0.S

.code16

.include "source/constants.S"

.global _start0
.global PRINT_STRING
.global HALT

.section .text
.global _start0
_start0:
        ljmp $0, $L0                # Canonicalize (%CS):%IP.
L0:
        xorw %ax, %ax
        movw %ax, %ds                # Load Segment Registers.
        movw %ax, %es
        movw %ax, %ss
        movw %ax, %fs
        movw %ax, %gs

        movw $STACK_TOP, %sp        # Prepare stack.

        sti                         # Set interrupt flag.
        
        movw $BOOT0SEQ_STR, %si
        movb $0x07, %dh
        call PRINT_STRING

INIT_ATTEMPT1:
        movb $0, %ah                # Reset drive controller.
        int $0x13
        jnc INIT_SUCCESS

INIT_ATTEMPT2:
        movb $0, %ah                # Try again.
        int $0x13
        jc FAILURE

INIT_SUCCESS:
        cmpb $0x80, %dl             # Check whether we boot from hard drive or floppy.
        jb LBA_FAILURE_OR_FLOPPY

HARDDRIVE:                          # We boot from a hard-drive.
        movb $0x41, %ah             # Check whether we support Disk Address Packet.
        movw $0x55AA, %bx
        int $0x13
        jc LBA_FAILURE_OR_FLOPPY
        cmpw $0xAA55, %bx
        jne LBA_FAILURE_OR_FLOPPY
        test $1, %cx
        jz LBA_FAILURE_OR_FLOPPY

LBA_SUCCESS:                        # We can read from a disk using Disk Address Packet.
        movb $0x42, %ah
        movw $DAP, %si              # %ds is already set to zero.
        int $0x13
        jc FAILURE                  # Test whether read was successful.

        jmp SUCCESS

LBA_FAILURE_OR_FLOPPY:              # We boot from a floppy disk or the extension check failed.
FLOPPY_ATTEMPT1:
        movw $0x0210, %ax
        movw $0x0001, %cx
        movb $0, %dh
        movw $BOOT1_START, %bx      # %es is already set to zero.
        int $0x13
        jnc FLOPPY_SUCCESS

FLOPPY_ATTEMPT2:                    # Try reading for a second time!
        movw $0x0210, %ax
        movw $0x0001, %cx
        movw $BOOT1_START, %bx
        int $0x13
        jc FAILURE

FLOPPY_SUCCESS:
        jmp SUCCESS

SUCCESS:
        movw $BOOT0_SUCCESS_STR, %si
        movb $0x0A, %dh             # 0x0A -- Light Green.
        call PRINT_STRING
        jmp BOOT1_START

FAILURE:                            # Something went wrong!
        movw $ERROR_MESSAGE, %si 
        movb $0x04, %dh             # 0x04 -- Red.
        call PRINT_STRING
        jmp HALT

HALT:
        cli
1:      hlt 
        jmp 1b

PRINT_STRING:                       # PRINT_STRING(string : %si, color : %dh)
        movb (%si), %al
        inc %si
        orb %al, %al
        jz PRINT_RET
        call PRINT_CHAR
        jmp PRINT_STRING

PRINT_RET:
        ret

PRINT_CHAR:
        movb $0x0E, %ah
        movb $0x00, %bh
        movb %dh, %bl
        int $0x10
        ret

.balign 4
DAP:        # Disk Address Packet structure.
DAP_packet_size:    .byte 0x10
DAP_zero:           .byte 0x00
DAP_Nsectors:       .word 0x01
DAP_buffer:         .long BOOT1_START
DAP_lower_bits:     .long 0x01
DAP_upper_bits:     .long 0x00

.byte 0x00

BOOT0SEQ_STR:       .asciz "[[ BootSeq0 ]]: "
BOOT0_SUCCESS_STR:  .asciz "Success!\n\r"
ERROR_MESSAGE:      .asciz "Error: Could not find Boot1!\n\r"

.space 510 - (. - _start0)
.word 0xAA55    # Boot Signature.

constants.S

.set BOOT1_START,  0x1000
.set BOOT1_END,    0x6000
.set STACK_TOP,    0x9000

Makefile(支持i686-elf或普通GNU binutils)

ARCH := i686-elf
CC := $(ARCH)-gcc
AS := $(ARCH)-as
LD := $(ARCH)-ld
BUILD_DIR := ./binaries
SRC_DIR := .

qemu: $(BUILD_DIR)/bootloader.img
	qemu-system-i386 -drive format=raw,file=$< # -S -gdb tcp::1234

$(BUILD_DIR)/bootloader.img: $(BUILD_DIR)/boot0.o
	$(LD) -T $(SRC_DIR)/linker.ld -format binary -o $@ $<

$(BUILD_DIR)/boot0.o: $(SRC_DIR)/boot0.S $(BUILD_DIR)
	$(AS) $< -o $@

$(BUILD_DIR):
	mkdir $(BUILD_DIR)

$(SRC_DIR)/boot0.S:
	echo "Error: no boot0.S file found!"

clean:
	rm -rf $(BUILD_DIR)

linker.ld

ENTRY(_start0)

SECTIONS
{
    . = 0x7C00;
    .boot0 : {
        binaries/boot0.o(.text*)
    }
}

INT 13h AH=42h调用前寄存器状态

eax            0x4200              16896
ecx            0x7                 7
edx            0x780               1920
ebx            0xaa55              43605
esp            0x9000              0x9000
ebp            0x0                 0x0
esi            0x7c9c              31900
edi            0x0                 0
eip            0x7c48              0x7c48
eflags         0x202               [ IOPL=0 IF ]
cs             0x0                 0
ss             0x0                 0
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0

排查情况

目前已确认参数传递和Disk Address Packet(DAP)结构设置无明显问题,但调用INT 13h AH=42h仍返回AH=1(无效函数参数)、CF=1。

问题原因与解决方法

原因1:DAP缓冲区地址格式不兼容

INT 13h AH=42h的DAP结构中,DAP_buffer字段要求是段:偏移的32位组合(高16位为段值,低16位为偏移值),当前代码直接使用线性地址0x1000,虽逻辑上等价于段0x0000+偏移0x1000,但部分BIOS对这种写法兼容性不佳。

解决方法:显式构造段:偏移格式的缓冲区地址

DAP_buffer:         .word BOOT1_START, 0x0000  # 偏移0x1000,段0x0000

原因2:磁盘镜像LBA=1位置无有效数据

当前Makefile生成的bootloader.img仅包含boot0的512字节,LBA=1位置为空,BIOS读取不存在的扇区会返回错误。

解决方法:构建包含boot1的完整磁盘镜像

  1. 添加boot1编译规则(假设存在boot1.S):
$(BUILD_DIR)/boot1.o: $(SRC_DIR)/boot1.S $(BUILD_DIR)
	$(AS) $< -o $@

$(BUILD_DIR)/boot1.bin: $(BUILD_DIR)/boot1.o
	$(LD) -Ttext 0x1000 -o $@ $< --oformat binary
  1. 修改镜像生成规则,合并boot0和boot1:
$(BUILD_DIR)/bootloader.img: $(BUILD_DIR)/boot0.o $(BUILD_DIR)/boot1.bin
	$(LD) -T $(SRC_DIR)/linker.ld -format binary -o $(BUILD_DIR)/boot0.bin $<
	cat $(BUILD_DIR)/boot0.bin $(BUILD_DIR)/boot1.bin > $@
	# 可选:用dd填充到整扇区
	# dd if=$@ of=$@.tmp bs=512 count=2
	# mv $@.tmp $@

原因3:QEMU磁盘识别类型错误

QEMU默认可能将磁盘识别为软盘,代码中判断%dl >= 0x80才进入LBA模式,导致实际进入软盘分支,但软盘读取参数可能不匹配。

解决方法:强制QEMU识别为硬盘
修改QEMU启动命令:

qemu-system-i386 -drive format=raw,file=$(BUILD_DIR)/bootloader.img,index=0,media=disk

原因4:LBA支持检测逻辑过严

部分BIOS支持AH=42h,但AH=41h检测返回的bx或cx值不符合当前代码的判断条件。

解决方法:放宽检测逻辑,直接尝试AH=42h读取

HARDDRIVE:
        # 跳过严格检测,直接尝试LBA读取
        movb $0x42, %ah
        movw $DAP, %si
        int $0x13
        jnc SUCCESS
        # 失败后回退到CHS分支
        jmp LBA_FAILURE_OR_FLOPPY

内容的提问来源于stack exchange,提问作者Alex Gendelbergen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 09:35:57