进入保护模式后执行jmp触发故障——汇编引导加载器问题排查
分析你的保护模式进入问题
看起来你已经离成功不远了——CPU能进入保护模式但很快报错,问题大概率出在模式切换后的指令解析阶段。让我帮你拆解几个关键问题:
1. 错误的[bits 32]放置位置
看你的switch_to_pm函数:
switch_to_pm: cli lgdt [gdt_descriptor] mov eax, cr0 or eax, 0x1 mov cr0, eax [bits 32] jmp CODE_SEG:init_pm
当你执行mov cr0, eax后,CPU进入保护模式,但此时指令流水线里还残留着16位模式的执行逻辑。你在这里直接加[bits 32],会让汇编器把紧接着的jmp CODE_SEG:init_pm编译成32位指令,但CPU此时仍然按16位模式解析这条指令,必然会出现指令解码错误,直接导致崩溃。
修复方法:把[bits 32]移到init_pm标签的前面,确保远跳完成(流水线彻底刷新)后才开始编译32位指令:
switch_to_pm: cli lgdt [gdt_descriptor] mov eax, cr0 or eax, 0x1 mov cr0, eax ; 这里不要加[bits 32],保持16位汇编逻辑 jmp CODE_SEG:init_pm ; 远跳刷新流水线,正式进入32位模式 [bits 32] ; 移到这里,标记后续代码为32位指令 init_pm: mov ax, DATA_SEG mov ds, ax ; ... 后续代码
2. 额外的规范优化(非致命但能避免潜在问题)
switch_to_pm函数最后是远跳指令,永远不会返回,所以调用它之后的jmp $是多余的,可以删掉:call switch_to_pm ; jmp $ // 这条指令永远不会执行,直接删除即可进入保护模式设置完段寄存器后,建议加一个
nop空操作,确保段寄存器的缓存完全更新(虽然大部分现代CPU不需要,但这是编写保护模式代码的稳妥习惯):init_pm: mov ax, DATA_SEG mov ds, ax mov ss, ax mov es, ax mov fs, ax mov gs, ax nop ; 新增空操作,确保段缓存更新 mov ebp, 0x90000 mov esp, ebp
修复后的核心代码片段
调整后的switch_to_pm和init_pm部分:
switch_to_pm: cli lgdt [gdt_descriptor] mov eax, cr0 or eax, 0x1 mov cr0, eax jmp CODE_SEG:init_pm ; 远跳进入32位模式,刷新流水线 [bits 32] init_pm: mov ax, DATA_SEG mov ds, ax mov ss, ax mov es, ax mov fs, ax mov gs, ax nop mov ebp, 0x90000 mov esp, ebp call BEGIN_PM BEGIN_PM: mov ebx, MSG_PM call print_string_pm jmp $
把这个修改后重新编译,应该就能正常进入保护模式并打印success了。
内容的提问来源于stack exchange,提问作者Braiden Grant
相关产品推荐
相关产品推荐

