2. 案例2
任务目标
本案例实现x86平台Bootloader从实模式(16位)切换到保护模式(32位)的完整流程。核心工作包括:构建全局描述符表(GDT)并加载、启用A20地址线、修改CR0寄存器控制位,最终跳转到32位代码段执行。代码以可引导的Bootloader扇区形式呈现,可在QEMU中直接验证。
环境与依赖
- 汇编器:NASM(≥2.14)
- 虚拟机:QEMU(≥5.0)
- 目标平台:x86(IA-32)
核心实现
以下代码为完整的Bootloader实现,包含实模式入口、GDT定义、A20启用、CR0切换及保护模式下的显存输出验证。代码经nasm -f bin编译后可直接作为引导扇区运行。
;--------------------------------------------------------
; boot.asm - 实模式到保护模式切换
; 汇编: nasm -f bin boot.asm -o boot.bin
; 运行: qemu-system-i386 -drive format=raw,file=boot.bin
;--------------------------------------------------------
[bits 16]
org 0x7c00
start:
; 保存引导驱动器号
mov [boot_drive], dl
; 关闭中断(切换过程不允许被打断)
cli
; 启用A20地址线(使用快速门控端口0x92)
in al, 0x92
or al, 0x02
and al, 0xFE ; 保留bit0(禁止复位)
out 0x92, al
; 等待A20生效(简单延时)
mov cx, 0x100
a20_wait:
loop a20_wait
; 加载GDT描述符
lgdt [gdt_desc]
; 修改CR0的PE位(bit0),切换到保护模式
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
; 清空流水线并跳转到32位代码段
jmp 0x08:protected_mode
[bits 32]
protected_mode:
; 初始化数据段寄存器为GDT数据段选择子(0x10)
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; 设置栈顶(位于1MB以下,避免与显存冲突)
mov esp, 0x90000
; 验证:向显存写入字符 'P'(绿色)
mov byte [0xb8000], 'P'
mov byte [0xb8001], 0x0A
; 死循环,保持CPU在保护模式
halt:
hlt
jmp halt
;--------------------------------------------------------
; GDT定义
;--------------------------------------------------------
align 8
gdt_start:
; 空描述符(必须为全0)
dd 0x00000000
dd 0x00000000
; 代码段描述符(选择子0x08)
; base=0x0, limit=0xFFFFF, G=1, D/B=1, S=1, P=1, DPL=0, TYPE=0xA(可执行/可读)
dw 0xFFFF ; limit[15:0]
dw 0x0000 ; base[15:0]
db 0x00 ; base[23:16]
db 0x9A ; P=1, DPL=0, S=1, TYPE=0xA
db 0xCF ; G=1, D/B=1, limit[19:16]=0xF
db 0x00 ; base[31:24]
; 数据段描述符(选择子0x10)
; base=0x0, limit=0xFFFFF, G=1, D/B=1, S=1, P=1, DPL=0, TYPE=0x2(可读/写)
dw 0xFFFF ; limit[15:0]
dw 0x0000 ; base[15:0]
db 0x00 ; base[23:16]
db 0x92 ; P=1, DPL=0, S=1, TYPE=0x2
db 0xCF ; G=1, D/B=1, limit[19:16]=0xF
db 0x00 ; base[31:24]
gdt_end:
gdt_desc:
dw gdt_end - gdt_start - 1 ; GDT长度减1
dd gdt_start ; GDT基地址
;--------------------------------------------------------
; 数据区
;--------------------------------------------------------
boot_drive: db 0x00
; 填充至510字节并写入引导签名
times 510-($-$$) db 0
dw 0xAA55
编译与运行
nasm -f bin boot.asm -o boot.bin
qemu-system-i386 -drive format=raw,file=boot.bin
验证方法
运行QEMU后,观察屏幕左上角是否出现绿色字符P。若出现,说明切换成功:
- 实模式入口:BIOS将引导扇区加载至0x7C00并跳转执行;
- A20启用:通过端口0x92的bit1置位,解除地址线回绕;
- GDT加载:
lgdt指令加载包含代码段(0x08)和数据段(0x10)的描述符表; - CR0切换:置位PE位后,CPU进入保护模式;
- 远跳转刷新流水线:
jmp 0x08:protected_mode加载代码段选择子并跳转至32位代码; - 段寄存器初始化:将DS/ES/SS等加载为数据段选择子;
- 显存输出:向0xB8000写入字符
P及其属性字节(绿色),验证32位模式下内存访问正常。
若屏幕无输出或QEMU报错,请检查:GDT描述符的基地址/界限是否正确、A20端口是否被虚拟机支持(QEMU默认支持)、CR0修改后是否立即执行远跳转。
常见问题
- 屏幕无输出:确认GDT中代码段描述符的TYPE位为0xA(可执行),且跳转选择子0x08与描述符索引1对应。
- 三声报警后重启:通常因GDT加载失败或CR0切换后未正确跳转。检查
gdt_desc中的基地址是否为gdt_start的实际线性地址(实模式下为CS:IP组合,此处使用org 0x7c00确保正确)。 - A20未生效:部分环境需通过键盘控制器(端口0x64/0x60)启用,但QEMU支持0x92快速门控,本实现已覆盖。