《真象还原》读书笔记——第四章 保护模式入门

===================== 第一部分 ====================

4.3 全局描述符表

全局描述符表(GDT)。

4.3.1 段描述符

问:段描述符是什么,做什么用?
实模式下访问内存使用的是 段基址:偏移地址。
在保护模式下为了安全性,给其中的段基址添加了很多的安全属性同时也增长了段基址长度,让总长度增加到了8字节,使用段选择子替代段基址
段选择子中就有者段描述符的"下标"
段描述符

name长度功能备注
段界限20bit描述段的拓展范围,界限粒度由G来决定 。CPU硬件会负责检测偏移地址是否越界段界限边界值 =(描述符中段界限+1)*(段界限的粒度大小:4KB或是1)-1,因为从零算,所以多一行,因为从零算,所以少一个。大小分为1MB/32GB
G1bit为0,界限粒度为1Byte;为1,界限粒度为4KB=2^12Byte
S1bit0,系统段;1,数据段各种门的结构即属于"系统段"。例如:调用门,系统门等。
type4bit标记段类型,配合S使用,如表4-10段描述符的type类型
段描述符type类型【非系统段】
A设置为0cpu访问后会置为1
C0,非一致性代码段,1,一致性代码段一致不一致,是和和转移后的段比较,和不和转移后的段特权级一致。1 则用转移前的特权级 CPL <= CPL,0 则用目标的特权级 CPL <= DPL。一致代码段和非一致代码段
R/W0,不可读;1,可读;0,不可写;1,可写
X0,不可执行;1,可执行
E0,向上拓展;1,向下拓展和前面的拓展范围有关联,这里E决定了拓展方向。代码段,数据段向上拓展(地址越来越高);栈段像下拓展。(地址越来越低)
name长度功能备注
DPL2bit共4种特权级,0,1,2,3操作系统0,用户等级3权限最小
P1bit0,不存在;1,存在检查是否存在于内存中
AVL1bit没有专门用途
L1bit1,为64位代码段;0,32位代码段
D/B1bit对于代码段D:0/1,有效地址和操作数为16bit/32bit,指令有效地址用IP/EIP寄存器;对于栈段B:涉及到栈指针的选择和栈地址上限;0/1使用的是SP/esp寄存器,也就是栈的起始位置是16/32位寄存器的最大寻址范围。0xFFFF/0xFFFFFFFF

全局描述符表GDT、局部描述符LDT及选择子

问:这么多描述符放在哪里?
答:放在全局描述符表——GDT中,GDT由GDTR寄存器指向它,方便CPU找它。lgdt指令。
使用格式:lgdt 48位内存数据
GDTR

4.3.1.1 段的选择子

使用段选择子替代段基址。
段选择子也是16bit和段寄存器一样。
段的选择子

RPL2bit0,1,2,3特权级此处为当前的特权级
IT1bit0,在GDT中;1,在LDT中
index描述符的索引值位相当于GDT数组中的下标
此时访问内存,直接用选择子对应的”段描述符中的段基址“加上”段内偏移地址“就是要访问的内存地址。另外,GDT中的第0个段描述符不可用。
4.3.1.2 LDT

只不过lgdt不怎么被用到。寄存器为LDTR,使用 lldt 指令加载LDT。
使用格式为:lldt 16位寄存器/16位内存。

问:为什么这里lldt 的是16位的,而不是像lgdt中的那样?
答:因为LDT所在的位置被划分成段,只能用段选择子直接访问了,而段选择子正是16bit。

注意:与GDT不同的是LDT的第0个段描述符是可用的。

4.3.3 打开A20地址线

早期实模式最大地址为 0xFFFF0+0xFFFF=>0x10FFEF,而实模式下地址线是20,即为只能访问到0xFFFFF。因此cpu采取将超过1MB的部分自动回绕到0地址这种方式来解决"地址溢出"。也称为“地址回绕”。
所以控制这第21根线A20(第一根线是A0)。

A20Gate 打开访问0x10 0000~0x10 FFFF时候访问的是真正的该地址
A20Gate 关闭访问0x10 0000~0x10 FFFF时候采用的是8086/8088的地址回绕
如今是要进入安全模式,突破第20条地址线到更多的地址上去。所以,开启A20Gate必不可少。
4.3.3.1 打开A20Gate的方式

端口0x92 的第一个位置置1即可。只需三部:

in  al,   0x92
or  al,   0000_0010B
out 0x92, al

4.3.4 保护模式的开关,CR0寄存器的PE位

我们要用到CR0寄存器的第0位,即PE位,此位用于启动保护模式。
CR0
PE置1:

mov eax, cr0
or  eax, 0000_0001B
mov cr0, eax

CR0描述

4.3.5 让我们进入保护模式

保护模式是在loader.bin中进入的。

  1. 打开A20
  2. 加载GDT
  3. 将cr0的PE位置置为1

========== 第二部分 ==========

4.5 使用远跳转指令清空流水线,更新段描述符缓冲寄存器

  jmp dword SELECTOR_CODE:p_mode_start ;刷新流水线

进入保护模式后,32位模式下存放在缓冲区中的16位代码就不能继续使用了,就要清空。使用jmp清空缓冲区。该处直接使用SELECTOR_CODE选择子

4.6 保护模式之内存段的保护

4.6.1段寄存器加载选择子时候的保护

段的选择子

  1. 判断选择子指向的描述符是否越界/存在。
    1. 描述符表基地址+选择子中的索引值8+7 <= 描述符表基地址+描述符表界限值;因为描述符一个8字节所以 *8,第0个描述符是空的所以+7。不符合<=则抛出异常。
  2. 检查段描述符中的type是否符合相应的段寄存器。
    • 只有具备 可执行(代码段) 才能加载到CS寄存器
    • 只具备 执行(代码段)不能加载到除了CS寄存器之外的寄存器
    • 只有具备 可写 (数据段)才能加载到SS栈寄存器
    • 至少具备 可读的段才能加载到DS,ES,FS,GS段寄存器中.
  3. 检查描述符P位判断该内存是否是存在的。P由操作系统设置CPU检查,A是CPU设置的

4.6.2 代码段和数据段的保护

EIP偏移地址+指令/数据长度-1<=实际边界长度大小
指令/数据不能"骑在"边界上。
举例:段边界为0x12345,基地址0x00000000
G=0:实际到达0x12345
G=1:实际到达0x12345*0x1000+0xFFF=0x12345FFF
当G=1时候,访问0x12345FFF,获取的是1字节则可以。但mov ax,word[0x12345FFF]像这样2字节的,因为是数据所以向上延伸,操作的是0x12345FFF和0x12346000的数据。就会出现“骑墙”的情况而报错。

4.6.3 栈段的保护

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值