环境
- Time 2022-11-12
- WSL-Ubuntu 22.04
- QEMU 6.2.0
- NASM 2.15.05
前言
说明
参考:https://os.phil-opp.com/entering-longmode//
目标
定义一个长模式检查函数,验证 CPU 是否支持长模式。
长模式也就是 64 位模式。
定义栈
需要先定义栈信息,后面的检查需要使用栈。
section .bss
stack_bottom:
resb 64
stack_top:
检查 CPUID
check_cpuid:
; 检查 CPUID 是否支持可以通过翻转 ID 位,即第 21 位。
; 如果在 FLAGS 标志寄存器中,我们能够翻转它,CPUID 就是可用的。
; 通过栈拷贝 FLAGS 寄存器的值到 EAX 寄存器
pushfd
pop eax
; 将 EAX 的值拷贝到 ECX,后面要用
mov ecx, eax
; 翻转第 21 位
xor eax, 1 << 21
; 把 EAX 的值拷贝回 FLAGS 寄存器
push eax
popfd
; 拷贝 FLAGS 寄存器的值回 EAX 寄存器,检查是否翻转成功,成功翻转则支持 CPUID
pushfd
pop eax
; 通过 ECX 还原 EFLAGS 中的值
push ecx
popfd
; 比较,如果两个一样,则翻转不成功,不支持CPUID;如果翻转成功,则支持CPUID
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "1"
jmp error
检查长模式
check_long_mode:
; 检查是否有扩展的处理器信息可用
mov eax, 0x80000000 ; CPUID 的隐式参数
cpuid ; 获取最高支持的参数
cmp eax, 0x80000001 ; 如果支持长模式,至少是 0x80000001
jb .no_long_mode ; 如果小于,则不支持长模式
; 使用扩展信息验证是否支持长模式
mov eax, 0x80000001 ; 扩展处理器参数信息
cpuid ; 将各种特征标记位返回到 ECX 和 EDX
test edx, 1 << 29 ; 第 29 位是 long mode 长模式标记位,检查是否支持
jz .no_long_mode ; 如果为 0,表示不支持长模式
ret
.no_long_mode:
mov al, "2"
jmp error
主逻辑
global start
section .text
bits 32
start:
; 栈是否高地址往低地址增长
mov esp, stack_top
call check_cpuid
call check_long_mode
总结
通过对 CPUID 和长模式的检查,确认能够进入 64 位模式。
附录
源码
section .multiboot_header
header_start:
dd 0x1BADB002 ; 魔法数字,固定值
dd 0
dd -0x1BADB002 ; 定义的这三个数字相加需要等于0
header_end:
global start
section .text
bits 32
start:
; 栈是否高地址往低地址增长
mov esp, stack_top
call check_cpuid
call check_long_mode
check_cpuid:
; 检查 CPUID 是否支持可以通过翻转 ID 位,即第 21 位。
; 如果在 FLAGS 标志寄存器中,我们能够翻转它,CPUID 就是可用的。
; 通过栈拷贝 FLAGS 寄存器的值到 EAX 寄存器
pushfd
pop eax
; 将 EAX 的值拷贝到 ECX,后面要用
mov ecx, eax
; 翻转第 21 位
xor eax, 1 << 21
; 把 EAX 的值拷贝回 FLAGS 寄存器
push eax
popfd
; 拷贝 FLAGS 寄存器的值回 EAX 寄存器,检查是否翻转成功,成功翻转则支持 CPUID
pushfd
pop eax
; 通过 ECX 还原 EFLAGS 中的值
push ecx
popfd
; 比较,如果两个一样,则翻转不成功,不支持CPUID;如果翻转成功,则支持CPUID
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "1"
jmp error
check_long_mode:
; 检查是否有扩展的处理器信息可用
mov eax, 0x80000000 ; CPUID 的隐式参数
cpuid ; 获取最高支持的参数
cmp eax, 0x80000001 ; 如果支持长模式,至少是 0x80000001
jb .no_long_mode ; 如果小于,则不支持长模式
; 使用扩展信息验证是否支持长模式
mov eax, 0x80000001 ; 扩展处理器参数信息
cpuid ; 将各种特征标记位返回到 ECX 和 EDX
test edx, 1 << 29 ; 第 29 位是 long mode 长模式标记位,检查是否支持
jz .no_long_mode ; 如果为 0,表示不支持长模式
ret
.no_long_mode:
mov al, "2"
jmp error
; 打印 `ERR: ` 和一个错误代码并停住。
; 错误代码在 al 寄存器中
error:
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt
section .bss
stack_bottom:
resb 64
stack_top:
标签:检查,mov,模式,0177,eax,cpuid,CPUID,check
From: https://www.cnblogs.com/jiangbo4444/p/18301108