Linux内核:保护模式初级
保护模式初级
前言
本章为之前学习《操作系统真象还原》一书所做的笔记
内容为第四章、第五章的保护模式
全局描述符表
全局描述符表( Global Descriptor Table, GDT )是保护模式下内存段的登记表,这是不同于实模式的显著特征之一
段描述符
首先,对于 IA32 架构的处理器(就是我们大多数人现在所用的处理器),访问内存采用“段基址:段内偏移地址”形式,即使到了保护模式,也是绕不开这个限制的
同时,为了保证内存访问的安全问题,保护模式应运而生
在之前的16位模式下,内存访问的上限收到了寄存器的限制,为此人们甚至需要将16位寄存器左移4位来扩大寻址空间,由此,通过内存寻址的想法就有了,因为其不受寄存器大小限制,而且还可以为段添加上更多的描述信息
段描述符如下:
段描述符的大小为8字节64位
- 段界限
- 段界限表示段边界的扩展最值,以限制内存访问的范围
- 在代码段和数据段,段的扩展方向是向上,在栈段,扩展方向为向下
- 可以看到段界限由20个比特位共同描述,而最终段的边界为段界限值 * 单位,单位要么为1字节,要么为4kb(2^12次方字节),所以段大小要么为2^20=1MB大小,要么为2^20 * 2^ 32=4GB大小
- 单位大小由G位决定,G为0则大小为1byte,G位为0则大小为4kb
- 访问若超出段的边界,CPU就会抛出异常
- 段基址
- 16-31位存储段基址的0-15位,高32位中,0-7位存储段基址的16-23,24-31存储段基址的24-31
- S位
- S位用于描述是系统段还是数据段,在 CPU 眼里,凡是硬件运行需要用到的东西都可称之为系统,凡是软件(操作系统也属于软件, CPU 眼中,它与用户程序无区别)需要的东西都称为数据,无论是代码,还是数据,它们都作为硬件的输入,都是给硬件的数据而己,所以代码段在段描述符中也属于数据段(非系统段)
- S位为0,系统段,S位为1,数据段(非系统段)
- S位需要和type字段配合使用
- type字段
- type字段共4位,用于表示内存段或门的子类型
- type字段说明如下图所示:
- 主要关注非系统段
- A位表示Accessed位,这是由CPU来设置的,每当该段被CPU访问过后,CPU就将此位置1,所以,创建个新段描述符时,应该将此位置0
- C表示一致性代码段,也称为依从代码段, Conforming。一致性代码段是指如果自己是转移的目标段,并且自己是一致性代码段,自己的特权级一定要高于当前特权级,转移后的特权级不与自己的 DPL 为主,而是与转移前的低特权级一致,也就是昕从、依从转移前的低特权级。 C为1时则表示该段是一致性代码段, C为0时则表示该段为非一致性代码段
- R表示可读,R为1表示可读,R为0表示不可读。这个属性一般用来限制代码段的访问
- X表示该段是否可执行,EXecutable。X为1表示代码段可执行,X为0表示代码段不可执行
- E是用来标识段的扩展方向,Extend。E为0表示向上扩展,通常用于代码段和数据段。E为1表示向下扩展,地址越来越低,通常用于栈段
- W是指段是否可写, Writable。W 为1表示可写,通常用于数据段。W为0表示不可写入,通常用于代码段
- DPL
- 段描述符的第 13-14 位是 DPL 字段, Descriptor Privilege Level ,即描述符特权级,指描述符所代表的内存段的特权级
- 2个比特能表示四个特权级,即0,1,2,3,数字越小特权级越大。操作系统处于最高的0,用户程序处于3,某些指令只能在0特权级下执行,从而保证了安全
- P字段
- 段描述符的第 15 位是P字段,Present,即段是否存在,1为存在,0为不存在
- AVL字段
- 占一位,表示Available,可用的
- D/B字段
- 段描述符的第 22 位是 D/B 字段,用来指示有效地址(段内偏移地址)及操作数的大小
- 对于代码段来说,此位是D位,若D为0,表示指令中的有效地址和操作数是 16 位,指令有效地址用IP寄存器。若D为1,表示指令中的有效地址及操作数是 32 位,指令有效地址用 EIP 寄存器
- 对于栈段来说,此位是B位,用来指定操作数大小,此操作数涉及到栈指针寄存器的选择及栈的地址上限。若B为0,使用的是 sp 寄存器,也就是栈的起始地址是 16 位寄存器的最大寻址范围, 0xFFFF。若B为1,使用的是 esp 寄存器,也就是栈的起始地址是 32 位寄存器的最大寻址范围, 0xFFFFFFFF
- G字段
- 段描述符的第 23 位是G字段, Granularity ,粒度,用来指定段界限的单位大小
全局描述符表GDT、局部描述符表LDT及选择子
全局描述符表 GDT 相当于是描述符的数组,数组中的每个元素都是8宇节的描述符。可以用选择子中提供的下标在 GDT 中索引描述符
全局描述符表位于内存中,有专门的GDTR寄存器指向其地址,GDTR是个48位的寄存器,如图所示
由专门的指令lgdt来修改GDTR寄存器
lgdt指令在实模式和保护模式下都可执行,在保护模式下重新换个 GDT 的原因是实模式下只能访问低端 1MB 空间,所以 GDT 只能位于 1MB 之内
lgdt 的指令格式是: lgdt48 位内存数据
48 位内存数据划分为两部分,其中前 16 位是 GDT 以字节为单位的界限值,所以这 16 位相当于GDT 的字节大小减1。后 32 位是 GDT 的起始地址。由于 GDT 的大小是 16 位二进制,其表示的范围是16 次方等于 65536 字节。每个描述符大小是8字节,故GDT 中最多可容纳的描述符数量是 65536/8=8192个,即 GDT 中可容纳 8192 个段或门
由于段基址存入了段描述符中,此时段寄存器中存入的是一个叫做选择子的东西——selector,用于在GDT中索引相应的段描述符
段选择子结构如下:
- RPL
- 0-1位用于存储RPL,由于是2位,因此可以表示0,1,2,3四个特权级
- RPL即请求特权级,可以理解为请求者的当前特权级
- TI位
- 第2位是TI位,即 Table Indicator ,用来指示选择子是在 GDT 中,还是 LDT 中索引描述符
- 0表示在GDT中索引描述符,1表示在LDT中索引描述符
- 索引值
- 选择子的3-15位是描述符的索引值,用此值来索引描述符
- 2^13=8192,故最多可以索引8192个描述符
本质上还是通过段基址+偏移的方式来完成内存访问的,段描述符与内存段的关系:
值得注意的是,GDT 中的第0个段描述符是不可用的,原因是定义在 GDT 中的段描述符是要用选择子来访问的,如果使用的选择子忘记初始化,选择子的值便会是0,这便会访问到第0段描述符。为了避免出现这种因忘记初始化选择子而选择到第0个段描述符的情况,GDT 中的第0个段描述符不可用。也就是说,若选择到了 GOT 中的第0个描述符,处理器将发出异常
局部描述符表,叫 LDT, Local Descriptor Table,其为支持多任务而创建,按照CPU的设想为一个任务对应一个LDT,但其实现代操作系统LDT并不多用
LDT表也需要加载到LDTR寄存器中,加载指令为lldt
lldt指令的格式为:lldt 16 位寄存器/16 位内存
LDT 为系统段,其也是一片内存区域,所以也需要用个描述符在 GDT 中先注册
同时LDT表的第0项是可用的,因为TI位为显式初始化的必然结果
打开A20地址线
在实模式下,存在wrap-around,即地址回绕的问题,也就是最大寻址为0xffff0+0xffff=0x10ffef,显然这已经超过了1MB的内存
所以CPU的做法是会从头开始,产生回绕,相当于把地址对1MB求模 超过1MB但多余出来的内存被称为高端内存区 HMA
当CPU到了80286时候,地址总线由20位变成了24位,出于兼容性的考虑,便有了A20Gate,即对第21根地址线进行控制
- 如果 A20Gate 被打开,当访问到 0x100000-0x10FFEF 之间的地址时, CPU 将真正访问这块物理内存
- 如果 A20Gate被禁止,当访问到 0x100000-0x10FFEF 之间的地址时, CPU 将采用 8086/8088 的地址回绕
开启A20Gate的方式也极其简单,将端口0x92的第1位置1:
1 | in al, 0x92 |
保护模式开关
CRx系列控制寄存器是CPU的窗口,用来展示CPU内部状态及控制CPU运行机制
保护模式的开关为CR0寄存器:
CR0寄存器的第0位为PE位,即Pro tionEnable ,此位用于启用保护模式
开启保护模式的最后一步:
1 | mov eax, cr0 |
保护模式之内存段的保护
向段寄存器加载选择子时的保护
当引用一个内存段时,实际上就是往段寄存器中加载个选择子,处理器会做出一些检查
- 选择子检查
- 需要判断高13位的索引值是否正确,一定要小于等于描述符表中的描述符个数,了一通过段描述符表的界限值来判断
- 先检查TI的值,选择是从GDT还是LDT中取描述符
- 段描述符检查
- 检查type字段,检测段寄存器的用途和段类型是否匹配,原则如下:
- 具有可执行属性的段才能加载到CS
- 只具备执行属性的段不允许加载到除CS外的段寄存器
- 具备可写属性的段才能加载到SS栈段
- 至少具备可读才能加载到DS、ES、FS、GS段寄存器中
- 总结,如下表所示:
- 检查完type后检查p位,验证该段是否存在
- 设置A位为1,表示CPU已访问过
- A位由CPU设置,P位通常由操作系统设置
- 检查type字段,检测段寄存器的用途和段类型是否匹配,原则如下: