保护模式下32位寄存器操作异常的SMP AP启动问题求助
问题根因
你遇到的所有异常表现,本质原因是32位代码段的段描述符配置错误:GDT中偏移为0x8的代码段描述符的D位(默认操作数大小位)未设置为1,导致处理器跳转到该代码段后,仍然以16位默认操作数模式解码指令。
现象对应解释
NASM的[bits 32]伪指令仅告诉汇编器生成32位指令编码,不会修改处理器的实际运行模式。当代码段D位为0时,处理器默认用16位规则解码指令,和你观测的现象完全匹配:
- 32位模式下无操作数前缀的
xor eax, eax,在16位默认模式下会被解码为仅操作16位寄存器的xor ax, ax,因此只有低16位被清零,高16位保留原值。 - 32位模式下汇编
xor ax, ax时,NASM会自动添加0x66操作数前缀切换操作数宽度,16位默认模式下识别到该前缀后会切换为32位操作,反而会完整清零整个eax寄存器。 - 同理
mov eax, 0xDEADBEEF在16位默认模式下被解码为mov ax, 0xBEEF,后续的高16位立即数会被误判为下一条指令的操作码,因此仅低16位生效。 - 你提到的ES寄存器需要赋值两次才能生效,也是指令解码错误导致的执行异常,属于非预期的巧合结果。
解决方案
- 优先检查GDT中
0x8代码段的描述符配置,确保符合32位代码段要求:- 段类型设置为非一致可执行代码段
- D位(描述符第22位)设置为1,指定默认操作数宽度为32位
- 段基地址设置为0,段限长覆盖全部32位地址空间(或至少覆盖你的内核代码运行范围)
- 若使用4KB粒度,G位(粒度位)设置为1
- 检查GDTR加载的正确性:
实模式下访问内存使用物理地址,若BSP已开启分页,需确保g_gdtr变量以及AP引导代码所在的内存区域,属于物理地址与虚拟地址相等的恒等映射区,否则实模式下加载的GDT内容会出错。 - 确认
__smp_ap_pm_entry的地址低于1MB:
你在16位实模式下执行push __smp_ap_pm_entry时仅能压入16位偏移,若目标地址高于0xFFFF会导致偏移截断,跳转目标错误。
内容的提问来源于stack exchange,提问作者Dalex
相关产品推荐
相关产品推荐

