CPU三种工作模式

实模式

特点

  • 运行真实的指令
  • 发往内存的地址是真实的

实模式寄存器

下面是x86 CPU实模式下的寄存器,均为16位

寄存器 描述
AX, BX, CX, DX, DI, SI, BP 通用寄存器
IP 指向下一条指令的地址
SP 栈指针寄存器,始终指向栈顶
CS, DS, ES, SS 段寄存器,存放一个内存段的基地址
FLAGS 存放CPU运算产生的标志位

实模式访问内存

14633ea933972e19f3439eb6aeab3d13.webp

代码段由CS和IP确定,栈段由SS和SP确定

示例汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data SEGMENT ;定义一个数据段存放Hello World!
hello DB 'Hello World!$' ;注意要以$结束
data ENDS
code SEGMENT ;定义一个代码段存放程序指令
ASSUME CS:CODE,DS:DATA ;告诉汇编程序,DS指向数据段,CS指向代码段
start:
MOV AX,data ;将data段首地址赋值给AX
MOV DS,AX ;将AX赋值给DS,使DS指向data段
LEA DX,hello ;使DX指向hello首地址
;下面MOV和INT搭配实现不同的系统调用(输出,结束程序等)
MOV AH,09h ;给AH设置参数09H,AH是AX高8位,AL是AX低8位,其它类似
INT 21h ;执行DOS中断输出DS指向的DX指向的字符串hello
MOV AX,4C00h ;给AX设置参数4C00h
INT 21h ;调用4C00h号功能,结束程序
code ENDS
END start

实模式中断

  • 硬件中断:中断控制器直接给CPU发射电子信号,CPU作出应答,随后中断控制器发送中断号
  • 软件中断:CPU执行了INT指令

为了实现中断机制,我们需要构建中断向量表。(IDTR是CPU的特定寄存器)

cd6ed6b49bf06b8de6bcd47e82e24051.webp

保护模式

保护模式寄存器

寄存器 描述
EAX, EBX, ECX, EDX, EDI, ESI, EBP 32位通用寄存器(名字在实模式基础上加了E)
EIP 32位程序指针
ESP 32位栈指针
CS, DS, ES, SS, FS, GS 16位段寄存器,里面存放内存段的描述符索引
EFLAGS 32位CPU标志寄存器
CR0, CR1, CR2, CR3 32位CPU控制寄存器,控制CPU功能

保护模式特权级

特权级分为4级,从R0-R3,R0 可以执行所有指令,R1、R2、R3 依次递减,它们只能执行上一级指令数量的子集。

d29yyb3f4ac30552e4c0835525d72b2b.webp

保护模式段描述符

目前为止,内存还是分段模型,要对内存进行保护,就可以转换成对段的保护

为了实现对段的保护,我们需要段描述符记录段的信息,其中包括该段的权限级别,从而确定能否被访问

又由于CPU拓展后,地址均为32位,段长度最大为(1 << 20)

因此我们要记录一个段的信息,至少需要32+20(记录该段长度)=52位,再加上些标识位,凑到64位:

b40a64dd5ca1dc1efd8957525e904634.webp

多个段描述符形成全局的段描述符表,该表的基地址和长度记录在CPU的GDTR寄存器中

ab203e85dd8468051eca238c3ebd81f7.webp

段寄存器中不再存放段基地址,而是具体段描述符的索引,访问一个内存地址时,段寄存器中的索引首先会结合 GDTR 寄存器找到内存中的段描述符,再根据其中的段信息判断能不能访问成功。

保护模式段选择子

CS-GS这些段寄存器有16位,实际上它们还均附有影子寄存器(64位,用于段描述符的高速缓存),但这些影子寄存器位于硬件,对程序员不可见也不可操作。

d08ec3163c80a5dd94e488a71588f8a4.webp

低三位之所以能放 TI 和 RPL,是因为段描述符 8 字节对齐,每个索引低 3 位都为 0

TI: 我们不用关注 LDT,只需要使用 GDT 全局描述符表,所以 TI 永远设为 0。

RPL: 通常情况下,CS 和 SS 中 RPL 就组成了 CPL(当前权限级别),所以常常是 RPL=CPL,进而 CPL 就表示发起访问者要以什么权限去访问目标段,当 CPL 大于目标段 DPL 时,则 CPU 禁止访问,只有 CPL 小于等于目标段 DPL 时才能访问。

保护模式平坦模型

分段模型有很多缺陷,这在后面讲内存管理时有详细介绍,其实现代操作系统都会使用分页模型(这点在后面讲 MMU 那节课再探讨)。

但是 x86 CPU 并不能直接使用分页模型,而是要在分段模型的前提下,根据需要决定是否要开启分页。因为这是硬件的规定,程序员是无法改变的。但是我们可以简化设计,来使分段成为一种“虚设”,这就是保护模式的平坦模型。

由于CPU为32位,我们最大有1<<32的地址,而段长度最大为20位,粒度最大为2^12,因此一个段最长也为1<<32。

我们可以设置所有的段均从0开始,长度均为1<<20,粒度均为1<<12,那么所有的段均为1<<32地址空间

下面是当前该OS的段描述符表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GDT_START:
knull_dsc: dq 0
;第一个段描述符CPU硬件规定必须为0
kcode_dsc: dq 0x00cf9e000000ffff
;段基地址=0,段长度=0xfffff
;G=1,D/B=1,L=0,AVL=0
;P=1,DPL=0,S=1
;T=1,C=1,R=1,A=0
kdata_dsc: dq 0x00cf92000000ffff
;段基地址=0,段长度=0xfffff
;G=1,D/B=1,L=0,AVL=0
;P=1,DPL=0,S=1
;T=0,C=0,R=1,A=0
GDT_END:

GDT_PTR:
GDTLEN dw GDT_END-GDT_START-1
GDTBASE dd GDT_START

G=1,则段长度等于 0xfffff 个 4KB(G位为1,粒度为4KB,即最大的1<<12)

DPL=0,这说明需要最高权限即 CPL=0 才能访问。

保护模式中断

实模式下中断无需权限检查,而保护模式需要权限检查以及权限切换,因此同样的,我们需要中断描述符描述每个中断向量的信息。

我们将一个中断的中断描述符称为中断门描述符:

e11b9de930a09fb41bd6ded9bf12620b.webp

同理,我们通过IDTR+中断号获取对应的中断门描述符:

ff5c25c85a7fa28b17f386848f19fb5b.webp

产生中断后,CPU依次进行下列操作:

  • 检查中断号是否大于最后一个中断门描述符(x86 CPU最多支持0-255)
  • 检查描述符类型
  • 检查描述符段选择子指向的段选择符
  • 权限检查:如果 CPL 小于等于中断门的 DPL,并且 CPL 大于等于中断门中的段选择子所指向的段描述符的 DPL,就指向段描述符的 DPL。
  • 加载描述符中的段选择子到CS, 代码段偏移到EIP

CPL 小于等于中断门的 DPL,说明有权限能执行中断。 CPL 大于等于所指向的段描述符的 DPL,如果不提升 CPL,就会导致没有权限调到该内存,所以指向段描述符的 DPL。

切换到保护模式

x86 CPU在加电和reset后,会进入实模式,因此我们需要写代码使CPU切换到保护模式。

第一步:准备全局段描述符表(代码同上):

1
2
3
4
5
6
7
8
GDT_START:
knull_dsc: dq 0
kcode_dsc: dq 0x00cf9e000000ffff
kdata_dsc: dq 0x00cf92000000ffff
GDT_END:
GDT_PTR:
GDTLEN dw GDT_END-GDT_START-1
GDTBASE dd GDT_START

第二步:将GDTR寄存器指向全局段描述符表,

1
2
lgdt [GDT_PTR]
;lgdt x指令会将x加载到GDTR寄存器中,而[]起到取地址的作用

第三步:设置CR0寄存器,开启保护模式

1
2
3
4
;开启 PE
mov eax, cr0
bts eax, 0 ; CR0.PE =1
mov cr0, eax

bts指令的第一个操作数称为位基址,亦称位串;第二个操作数为位偏移值(bit offset)。

bts就是根据位偏移值从位串中取出一位放入CF中,然后将位串中的该位置成1。

第四步:进行长跳转,加载CS段寄存器,即段选择子

1
jmp dword 0x8 :_32bits_mode ;_32bits_mode为32位代码标号即段偏移

我们无法直接或间接 mov 一个数据到 CS 寄存器中,因为刚刚开启保护模式时,CS 的影子寄存器还是实模式下的值,所以需要告诉 CPU 加载新的段信息。接下来,CPU 发现了 CRO 寄存器第 0 位的值是 1,就会按 GDTR 的指示找到全局描述符表,然后根据索引值 8,把新的段描述符信息加载到 CS 影子寄存器,当然这里的前提是进行一系列合法的检查。

到此为止,CPU 真正进入了保护模式,CPU 也有了 32 位的处理能力。

长模式

特点

长模式又名 AMD64,因为这个标准是 AMD 公司最早定义的,它使 CPU 在现有的基础上有了 64 位的处理能力,既能完成 64 位的数据运算,也能寻址 64 位的地址空间。这在大型计算机上犹为重要,因为它们的物理内存通常有几百 GB。

长模式寄存器

长模式相比于保护模式,增加了一些通用寄存器,并扩展通用寄存器的位宽,所有的通用寄存器都是 64 位,还可以单独使用低 32 位。

cce7aa5fe43552357bc51455cd86a734.webp

长模式段描述符