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

如何在x86 PC分区引导扇区中检测CPU保护模式及子模式?

可靠检测CPU模式的硬件级方法(适配你的PBR调试场景)

由于你的BootManager会拦截BIOS中断,咱们完全不能依赖BIOS提供的服务来检测模式,必须用纯x86硬件寄存器和指令来实现——这些操作直接和CPU交互,不受BootManager的拦截影响。下面是分步骤的检测逻辑和对应的16位汇编实现:

核心检测思路

CPU的运行模式完全由控制寄存器(CR0、CR4)、扩展功能寄存器(IA32_EFER)和标志寄存器(EFLAGS)的特定位决定,咱们按从粗到细的顺序排查:

1. 区分实模式 vs 保护模式类(含虚拟8086、长模式)

首先检查CR0寄存器的PE位(第0位):

  • PE=0:CPU处于实模式(Real Mode),这是最基础的16位模式
  • PE=1:CPU处于保护模式类,需要进一步细分具体子模式
; 读取CR0寄存器,检测PE位
mov eax, cr0
test eax, 1          ; 掩码PE位
jz .detect_real_mode ; PE=0,跳转到实模式处理逻辑

2. 检测是否处于长模式(64位)

长模式是x86-64的核心模式,需要先确认CPU支持该特性,再检查相关寄存器位:

  1. 先通过CPUID指令确认CPU支持扩展功能(能读取IA32_EFER寄存器)
  2. 检查IA32_EFER的LME位(第8位)、CR0的PG位(第31位)、CR4的PAE位(第5位)是否都为1
  3. 最后检查CS段描述符的L位(第21位),确认是64位子模式还是32位兼容模式
; 检测是否支持CPUID(排除老款386/486)
pushfd
pop eax
mov ecx, eax
xor eax, 1 << 21     ; 翻转EFLAGS的ID位
push eax
popfd
pushfd
pop eax
cmp eax, ecx
jz .skip_long_mode_detect ; 不支持CPUID,无法进入长模式

; 检测是否支持长模式相关CPUID扩展
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .skip_long_mode_detect ; 不支持长模式扩展

; 读取IA32_EFER的LM标志
mov eax, 0x80000001
cpuid
test edx, 1 << 29    ; 检查EDX第29位(LM位)
jz .skip_long_mode_detect

; 检查CR0.PG和CR4.PAE是否启用
mov eax, cr0
test eax, 1 << 31    ; PG位(分页启用)
jz .skip_long_mode_detect
mov eax, cr4
test eax, 1 << 5     ; PAE位(物理地址扩展)
jz .skip_long_mode_detect

; 检查CS段的L位,区分64位模式和32位兼容模式
mov ax, cs
call .get_cs_descriptor_attr
test eax, 1 << 21    ; L位(64位模式标志)
jz .detect_long_mode_compat ; 32位兼容模式
jmp .detect_long_mode_64    ; 64位模式

.skip_long_mode_detect:
; 进入非长模式的检测分支

3. 区分虚拟8086模式 vs 32位保护模式

如果不是长模式,就检查EFLAGS的VM位(第17位):

  • VM=1:CPU处于虚拟8086模式(Virtual 8086 Mode),这是保护模式下模拟实模式的子模式
  • VM=0:CPU处于32位保护模式(Protected Mode)
pushfd
pop eax
test eax, 1 << 17    ; 掩码VM位
jz .detect_protected_32 ; VM=0,32位保护模式
jmp .detect_virtual_8086 ; VM=1,虚拟8086模式

辅助函数:获取CS段描述符属性

要检测长模式的子模式,需要读取CS段描述符的属性位,这里用SGDT指令获取GDT基址,再计算描述符地址:

.get_cs_descriptor_attr:
; 输入:AX = CS段选择子
; 输出:EAX = 描述符高32位(含L位等属性)
push ebx esi
mov ebx, eax
and ebx, 0xFFF8      ; 提取描述符索引(乘以8)
sgdt [.gdt_temp_ptr] ; 读取GDT指针到临时内存
mov esi, [.gdt_temp_ptr + 2] ; GDT基址
add esi, ebx         ; 定位到CS对应的描述符
mov eax, [esi + 4]   ; 读取描述符高32位
pop esi ebx
ret

.gdt_temp_ptr:
dw 0xFFFF            ; GDT限长(临时值)
dq 0                 ; GDT基址(实模式下低32位有效)

整合到你的PBR中

把这些检测逻辑和你现有的寄存器显示代码结合,比如检测完成后,用int 10h显示对应的模式字符串(注意如果处于保护模式类,int 10h可能被BootManager拦截,但你说调试时显示寄存器正常,应该BootManager会正确模拟该中断)。

另外要注意:所有这些指令都需要386及以上CPU支持,而现代x86 PC都满足这个要求;如果要兼容极老的386,可以加个简单的CPU检测(比如尝试执行mov eax, cr0,如果触发无效指令异常就说明是286及以下,但这种场景下你的BootManager也不可能运行)。

内容的提问来源于stack exchange,提问作者George Robinson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 13:29:06