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

进入保护模式后执行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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:17:49