赞
踩
3 长模式
本书虽然不是讲述64位操作系统的,但是讲到这里就快能够进入64位了,就写下这张供极客研究。不想看这章的话也可以直接跳过其余部分,只看3.3。这章的代码与前后一章是平行的,也就是说这章的代码是独立的。
长模式是AMD64(与Intel的IA-32e)中的一个模式。进入长模式后,寄存器变成了64位,我们也可以访问64位的内存空间[1]了。长模式中也含有兼容模式,如果GDT中当前代码段的CS.L是0,就会进入兼容模式。长模式忽略了分段,也就是说完全只靠分页机制。
多说也不说,先来掌握以下基本概念。
我们知道,由实模式进入到保护模式由两步指令完成,CR0.PE=1和一个重载CS的跳转。
由保护模式进入长模式很相似,不过稍稍麻烦了点。我们看看步骤:
(1) 首先,要进入保护模式,即CR0.PE=1
(2) 其次,先初始化页表!但注意,千万不能打开先分页机制!
(3) 打开CR4.PAE
(4) 打开EFER.LME=1
(5) 打开CR0.PG=1,分页使能。
(6) 这时候处理器自动将EFER.LMA设置,进入长模式
(7) 一个跳转
你会想,这不是我们都做过的吗。但是,你错了,在长模式下,分页机制变成了4层。
图3.2.1地址转换图
先来拿图吓你一下,不过我们不会马上讲。我们还不知道我们的CPU够不够格呢。
CPUID是一个指令,没有操作数。实际使用时,将一个入口参数放在EAX中然后执行指令CPUID,我们就可以在寄存器中找到CPU的各种特性。这个指令从486时代就有了。虽然我们应该先建立异常处理机制防止386的电脑出错(其实也可以用EFLAGS来判断CPUID是否可以用,但比之比较懒),但是现在已经没有386的电脑了,我们大可以缩小我们的操作系统能支持的范围。从486开始也包含了很多老式的电脑了呢。
我们先来简单地熟悉一下CPUID命令。
CPUID指令功能号0h |
||
入口参数 |
EAX |
0h,表示获得基本信息 |
出口参数 |
EAX |
最大可用功能号 |
EBX |
Vendor(生产厂商) Intel的处理器按顺序(EBX,EDX,ECX)是 GenuineIntel |
|
EDX |
||
ECX |
表3.3.1 CPUID,EAX=0
由于CPUID内容实在太多,我把东西都放在cpuid.inc内,在loader中include了一下。
;=====================================================
; ShowCPUFeatures();显示CPU信息
;-----------------------------------------------------
; registers changed:
; - All GPRs
ShowCPUFeatures:
mov esi, STR_CMIV
call DispStrPM
mov eax,0 ; CPU基本信息
cpuid
call DispInt
mov [VENDOR],ebx
mov [VENDOR+4],edx
mov [VENDOR+8],ecx
mov esi,STR_VEN
call DispStrPM
ret
_STR_CMIV db13,10,"CPUIDMax Input Value: ",0
STR_CMIV equ_STR_CMIV+LOADER_SEGPHYADDR
_STR_VEN db13,10,"Vendor:"
_VENDOR times13db0
STR_VEN equ_STR_VEN+LOADER_SEGPHYADDR
VENDOR equ _VENDOR+LOADER_SEGPHYADDR
代码3.3.1 ShowCPUFeatures V0.1(chapter3/a/boot/include/cpuid.inc)
运行一下,看到最大Bochs支持的最大EAX是0x5,CPU厂商是GenuineIntel。
图3.3.1 CPUID V0.1(被笔者擅自去掉了干扰信息)
其实最重要的信息为功能号0x1,但是笔者比较想知道处理器的名字。其实我们也可以用CPUID功能号1获得CPU家族,但名字的话,这里我们要用到扩展CPUID指令。
扩展CPUID指令功能号80000000h |
||
入口参数 |
EAX |
80000000h,表示获得扩展信息 |
出口参数 |
EAX |
最大可用扩展功能号 |
EBX |
保留 |
|
EDX |
||
ECX |
表3.3.2扩展CPUID
如果出口EAX=0表示不支持扩展CPUID指令。如果不为0,我们可以接着获得CPU名字了。
扩展CPUID指令功能号80000002h~80000004h |
||
入口参数 |
EAX |
80000002h~80000004h,表示获得处理器品牌字符串 |
出口参数 |
EAX~EDX |
处理器品牌字符串的一部分。 |
mov eax,0x80000000; CPU扩展信息
cpuid
cmp eax,0x80000004
jb .end ;不支持就算了
mov esi, BRAND
mov eax,0x80000002
cpuid
mov [esi],eax
mov [esi+4],ebx
mov [esi+8],ecx
mov [esi+12],edx
add esi,16
mov eax,0x80000003
cpuid
mov [esi],eax
mov [esi+4],ebx
mov [esi+8],ecx
mov [esi+12],edx
add esi,16
mov eax,0x80000004
cpuid
mov [esi],eax
mov [esi+4],ebx
mov [esi+8],ecx
mov [esi+12],edx
mov esi, STR_BRD
call DispStrPM
.end:
ret
_STR_BRD db13,10,"Brand:"
_BRAND times65db0
STR_BRD equ_STR_BRD+LOADER_SEGPHYADDR
BRAND equ _BRAND+LOADER_SEGPHYADDR
代码3.3.2超长的代码 V1.0 (chapter3/a/boot/include/cpuid.inc)
虽然长了点,但是一点都不难。运行一下。
图3.3.2笔者破电脑的CPUID
由于Hyper-V利用硬件虚拟化,所以这个CPUID就是笔者电脑本身的CPUID。而且与控制面板里的一模一样。Well done!
好了,略微扯远了。我们现在就先看看这个CPU支不支持64位长模式。长模式标志位是0x80000001号扩展CPUID功能返回值EDX的29位。
mov eax,0x80000001
cpuid
bt edx,29
jnc .end
mov esi, STR_64
call DispStrPM
.end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。