x86架构段寄存器配置问题:实机启动重启,Qemu运行正常
问题分析与修复方案
核心问题:实模式段寄存器未初始化导致地址计算错误
你的代码在Qemu能运行但真实PC重启,本质是实模式下段寄存器默认值的BIOS差异:Qemu的BIOS可能默认把DS设为0,刚好匹配[org 0x7c00]的地址计算;但真实PC的BIOS可能将DS设为其他值(比如0x07C0),导致lgdt [GDT_descriptor]访问GDT描述符时,实际物理地址和预期不符,触发CPU异常重启。
而你手动把DS/ES设为0x07C0后Qemu崩溃,是因为GDT_descriptor里的基地址是汇编器基于org 0x7c00生成的物理地址(0x7c00 + GDT_start的偏移),当DS=0x07C0时,[GDT_descriptor]的访问地址是0x07C0*16 + 偏移 = 0x7C00 + 偏移,但此时GDT基地址的读取逻辑混乱,CPU找不到正确的GDT表,直接触发异常。
分步修复
1. 初始化实模式段寄存器与栈
在进入保护模式前,必须强制将实模式下的段寄存器统一设为0,同时初始化栈指针,避免地址计算错误和栈溢出:
[org 0x7c00] CODE_SEG equ GDT_code - GDT_start DATA_SEG equ GDT_data - GDT_start ; 关键:初始化实模式段寄存器,确保所有内存访问地址正确 mov ax, 0x0000 mov ds, ax mov es, ax mov ss, ax mov sp, 0x7c00 ; 栈指针指向引导扇区起始,向下生长不会覆盖代码 cli ; 关中断,防止模式切换时触发中断异常 lgdt [GDT_descriptor] ; ... 后续代码不变
2. 完善保护模式段寄存器配置
进入32位保护模式后,要把所有段寄存器(包括FS、GS)都设置为数据段选择子,同时设置一个远离引导扇区的安全栈地址:
[bits 32] start_protected_mode: mov ax, DATA_SEG mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x90000 ; 保护模式栈设到0x90000,避免覆盖引导代码或GDT ; 输出测试字符 mov al, "A" mov ah, 0x0f mov [0xb8000], ax jmp $
完整修复后的代码
[org 0x7c00] CODE_SEG equ GDT_code - GDT_start DATA_SEG equ GDT_data - GDT_start ; 初始化实模式段寄存器与栈 mov ax, 0x0000 mov ds, ax mov es, ax mov ss, ax mov sp, 0x7c00 cli lgdt [GDT_descriptor] ; 切换到保护模式 mov eax, cr0 or eax, 0x01 mov cr0, eax ; 远跳转清空流水线,确保进入保护模式 jmp CODE_SEG:start_protected_mode jmp $ GDT_start: GDT_null: dd 0x0 dd 0x0 GDT_code: dw 0xffff dw 0x0 db 0x0 db 0b10011010 db 0b11001111 db 0x0 GDT_data: dw 0xffff dw 0x0 db 0x0 db 0b10010010 db 0b11001111 db 0x0 GDT_end: GDT_descriptor: dw GDT_end - GDT_start - 1 dd GDT_start [bits 32] start_protected_mode: mov ax, DATA_SEG mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x90000 mov al, "A" mov ah, 0x0f mov [0xb8000], ax jmp $ times 510 - ($ - $$) db 0 dw 0xaa55
内容的提问来源于stack exchange,提问作者Polly
相关产品推荐
相关产品推荐

