16位x86自制OS问题:Bootloader无法将内核加载至RAM
16位x86 Bootloader无法加载内核至RAM(INT 0x13调用失败)
问题现象
制作简易16位x86操作系统时,Bootloader执行到INT 0x13指令后触发错误,跳转到read_kernel_error分支,提示内核加载失败。内核仅实现打印"Hello World"功能,调整load_kernel段寄存器值后问题仍未解决。
相关代码
Bootloader代码(boot.asm)
; boot.asm BITS 16 ORG 0x7C00 %define ENDL 0x0D, 0x0A entry: jmp main main: mov si, msg_booting call prints mov si, dbug0 call prints jmp load_kernel prints: lodsb cmp al, 0 je return mov ah, 0x0E mov bh, 0x00 mov bl, 0x07 int 0x10 jmp prints load_kernel: mov ax, 0x0000 mov bx, 0x1000 mov cx, 0x0001 mov dx, 0x00 mov ah, 0x02 int 0x13 ; error appears to be here jc read_kernel_error jmp 0x1000 read_kernel_error: mov si, dbug1 call prints jmp done return: ret done: hlt msg_booting db "Booting...", ENDL, 0 dbug0 db "Jumping to 'load_kernel'...", ENDL, 0 dbug1 db "Kernel could not be loaded correctly", ENDL, 0 times 510 - ($ - $$) db 0 dw 0xAA55
内核代码(kernel.asm)
; kernel.asm BITS 16 ORG 0x1000 main: mov si, msg_hw call prints jmp done prints: lodsb cmp al, 0 je return mov ah, 0x0E mov bh, 0x00 mov bl, 0x07 int 0x10 jmp prints return: ret done: hlt msg_hw db "Hello World", 0x0D, 0x0A, 0
编译与运行步骤
编译命令
nasm -f bin -o boot.bin boot.asm nasm -f bin -o kernel.bin kernel.asm
制作软盘镜像
dd if=/dev/zero of=floppy.img bs=512 count=2880 dd if=boot.bin of=floppy.img bs=512 count=1 conv=notrunc dd if=kernel.bin of=floppy.img bs=512 seek=1 conv=notrunc
运行命令
qemu-system-i386 -drive format=raw,file=floppy.img
问题分析与修复方案
核心错误点
- INT 0x13参数错误:
- 未设置
AL寄存器(读取扇区数量),默认值为0,尝试读取0个扇区直接触发错误。 - 扇区号指定错误:内核被写入软盘的0-based第1扇区(对应
dd seek=1),而INT 0x13使用1-based扇区号,正确扇区号应为2,但代码中CX=0x0001指定的是扇区1(读bootloader自身)。 - 未明确设置驱动器号
DL,部分BIOS会使用错误值导致读取失败。
- 未设置
- 段寄存器未初始化:
- BIOS启动时仅保证
CS=0x0000、IP=0x7C00,DS、ES值未定义,导致字符串打印(依赖DS:SI)和内存地址指向(ES:BX)存在未定义行为。
- BIOS启动时仅保证
- 跳转地址不明确:
jmp 0x1000依赖当前CS值,未明确指定段地址,存在歧义。
修复后的Bootloader关键代码
1. 初始化段寄存器(main函数开头)
main: ; 初始化段寄存器,匹配ORG 0x7C00的地址映射 mov ax, 0x7C00 mov ds, ax mov es, ax mov si, msg_booting call prints ; ... 后续代码
2. 修正load_kernel函数
load_kernel: ; 设置目标地址ES:BX = 0x0000:0x1000,匹配内核ORG 0x1000 mov ax, 0x0000 mov es, ax mov bx, 0x1000 ; 配置INT 0x13读扇区参数 mov ah, 0x02 ; 功能号:磁盘读扇区 mov al, 0x01 ; 读取1个扇区(内核大小小于512字节) mov ch, 0x00 ; 柱面号:0 mov cl, 0x02 ; 扇区号:2(1-based,对应dd seek=1的0-based扇区) mov dh, 0x00 ; 磁头号:0 mov dl, 0x00 ; 驱动器号:0x00表示软盘A: int 0x13 jc read_kernel_error ; 明确跳转至内核入口(0x0000:0x1000) jmp 0x0000:0x1000
验证修复
修改代码后重新执行编译、制作镜像、运行步骤,Bootloader将成功加载内核并打印"Hello World"。
内容的提问来源于stack exchange,提问作者Felix Hötzendorfer
相关产品推荐
相关产品推荐

