鱼C论坛

 找回密码
 立即注册
查看: 2936|回复: 0

[学习笔记] X86汇编语言-从实模式到保护模式—笔记(21)-第12章 存储器的保护(3)

[复制链接]
发表于 2017-11-30 15:45:42 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 兰陵月 于 2017-12-5 21:52 编辑

【12.2.2 创建GDT并安装段描述符】

在进入保护模式前,必须要定义全局描述符表(GDT)。而全局描述符表(GDT)的跟踪、访问要靠处理器内部的一个48位的寄存器,它的名字叫做全局描述符表寄存器(GDTR),该寄存器的0~15位是全局描述符表的界限值(共16位),16~47位是全局描述符表的线性基地址(32位)。

准备工作1:设置好全局描述符表GDT的线性起始基地址。

根据上面的知识,我们知道了,要定义全局描述符表,必须要设置好线性基地址和表界限值。线性基地址我们在设计程序的时候已经确定好了,在源程序的第108行定义了一个双字“0x00007E00”作为全局描述符表的起始线性基地址。但是不要忘记,现在我们还在16位实模式下,32位的线性基地址我们是不能直接访问操作的,我们只能用“16位段地址:16位偏移地址”这种方式访问内存,所以现在必须把32位的线性基地址转化为实模式下的“16位段地址:16位偏移地址”这种逻辑地址,这样才能继续后面的“安装段描述符”、“确定GDT界限值”、“用LGDT加载全局描述符表的线性基地址和界限到GDTR寄存器”等操作。如下图图3,程序第12行~18行,分解GDT的32位起始线性基地址。

003.png                           

图3中,把32位线性基地址值传送给寄存器EAX,将寄存器EDX清零,设置除数为16,做32位除法。得到的商在寄存器EAX中,作为段地址数据(仅低16位有效)传送给寄存器DS[符合上文中编译后不带前缀0x66的规则],得到的余数在寄存器EDX中(仅低16位有效),作为偏移地址数据传送给寄存器EBX。

得到了GDT的起始线性基地址,我们就可以对GDT进行操作了,比如添加描述符。当然描述符的内容—即具体数值,我们必须要先准备好。

004.png


准备工作2:设计好进入保护模式时需要用到的各个段的描述符,并安装到GDT的相关位置。

第1个描述符(索引号“0”)必须是空描述符。上一章的内容中说过,处理器规定,GDT中的第1个描述符必须是空描述符,或者叫哑描述符或NULL描述符。当然空描述符也是一个描述符,所以它也一样要占用8个字节。如上图图4第21行、第22行,创建0#描述符。

第2个描述符(索引号“1”)是数据段描述符,我们要设计的是一个可以访问0~4GB空间内的任意一个单元的段。因此这个数据段的基本数据应该如下:线性基地址0x00000000;段界限0xFFFFF;32位可读可写向上扩展的段;段粒度4KB,特权级00。因此该数据段的描述符低32位为:0x0000FFFF,高32位为:0x00CF9200。该描述符应该安装到段地址DS、偏移地址[EBX+0x08]处的2个双字。如上图图4第25行、第26行。

第3个描述符(索引号“2”)是代码段描述符,我们现在所在的段运行到当前行时,还处于16位实模式,但是运行到第59行后,就已经真正进入了32位保护模式了,所以这个代码段描述符就是为我们正在编写的这个段准备的,因此,这个代码段的基本数据应该如下:线性基地址0x00007C00;段界限0x001FF;32位的向上扩展只执行的段;段粒度为字节,特权级00。因此该代码段的描述符低32位为:0x7C0001FF,高32位为:0x00409800。该描述符应该安装到段地址DS、偏移地址[EBX+0x10]处的两个双字。如上图图4第29行、第30行。

第4个描述符(索引号“3”)是代码段的别名描述符。在保护模式下,代码段是不可写入的。所谓不可写入,并非是说改变了内存的物理性质,使得内存写不进去,而是说,通过该段的描述符来访问这个区域时,处理器不允许向里面写入数据或者更改数据。但是很多时候,我们需要对代码段做一些修改,比如在调试程序时,需要加入断点指令int3;比如说我们正在编写的这个程序中,有一个功能要对本段内的一些数据进行排序(定义本程序第105行),同样需要对本段进行写入操作。不管怎么样,如果需要访问代码段内的数据,只能重新为该段安装一个新的描述符,并将其定义为可读可写的数据段。这样,当需要修改代码段内的数据时,可以通过这个新的描述符来进行。这个描述符就叫做别名描述符,别名可以有多个。本程序中的别名描述符的基本数据为:线性基地址0x00007C00;段界限0x001FF;32位的向上扩展可读可写的数据段;段粒度为字节,特权级00。因此该代码段的描述符低32位为:0x7C0001FF,高32位为:0x00409200。该描述符应该安装到段地址DS、偏移地址[EBX+0x18]处的两个双字。如上图图4第33行、第34行。

第5个描述符(索引号“4”)是栈段描述符。这个栈段也是进入保护模式后程序使用。因此,这个栈段的基本数据应该如下:线性基地址0x00007C00;段界限0xFFFFE;32位的向下扩展可读可执行的段;段粒度为4KB,特权级00。因此该代码段的描述符低32位为:0x7C00FFFE,高32位为:0x00CF9600。该描述符应该安装到段地址DS、偏移地址[EBX+0x20]处的两个双字。如上图图4第36行、第37行。

到目前为止,程序运行在保护模式下需要用到的描述符都已经安装完成。

准备工作3:加载全局描述符表的线性基地址和界限到GDTR寄存器。

全局描述符表GDT的线性基地址我们已经设置好。GDT的界限是多少呢?界限值等于表的大小(总字节数)减1。当前GDT有5个描述符,每个描述符为8个字节,因此总共有40个字节,所以表界限值为40-1=39。

GDT的线性基地址和界限都已经知道,我们就可以用LGDT指令将两个数据加载到GDTR里,即将GDTR初始化。LGDT指令操作一个48位(6字节)数,要求低16位(前16位)是GDT的界限值,高32位(后32位)是GDT的线性基地址。如下图图5中,第40行,将GDT的界限值39传送到段地址CS,偏移地址pgdt+0x7C00处。偏移地址pgdt+0x7C00+0x02处的线性基地址我们编写程序时已经给定,程序在开始运行到此处的过程中,其值并未发生改变。(此时还在实模式下,可以使用“段地址:偏移地址”的内存寻址方式);第42行将段地址CS,偏移地址pgdt+0x7C00处的6个字节加载到GDTR里,将其初始化。

005.png

准备工作4:打开第21条地址线的开关A20。

8086只有20根地址线,但80386系统有32根地址线,实模式下,第21根地址线是没有打开的,因此能使用的地址线是低20根,这时候寻址范围也只有1M。但是运行保护模式后,必须要用到全部的32根地址线,因此第21根地址线必须要打开。80386系统输入输出控制器集中芯片ICH的处理器接口部分,有一个用于兼容老式设备的端口0x92,该端口的位1用于控制A20。因此上图图5中第44行~第46行,先从端口0x92读出原数据,接着,第45行将第2位(位1)置“1”,然后第46行,将改写后的数据写入0x92端口,这样就打开了A20。

准备工作5:将寄存器CR0第1位设置为“1”。

控制实模式与保护模式切换的开关原始在一个叫CR0的寄存器里。CR0寄存器的第1位(位0)是保护模式允许位(PE位),是开启保护模式大门的门把手,如果把该位置“1”,则处理器进入保护模式,按保护模式的规则开始运行。因此上图图5中第50行,将CR0寄存器中的原有内容传送到寄存器EAX。第51行,将它的第1位(位0)置“1”,其他各位保持不变。第52行,将修改后的内容重新写回到CR0寄存器,这直接导致处理器的运行变成保护模式。

注意:保护模式下的中断机制和实模式不同,因此,原有的中断向量表不再适用,而且,必须要知道的是,在保护模式下,BIOS中断都不能再用,因为它们是实模式下的代码。在重新设置保护模式下的中断环境之前,必须关中断,所以程序第48行,关闭中断响应。

准备工作6:用jmp dword清空流水线并串行化处理器,同时最终进入32位保护模式。

虽然CR0寄存器的PE位置“1”,但要真正进入保护模式,还有两个问题需要解决。一是实模式下段寄存器的描述符高速缓存器的内容仍然残留,但这些残留内容是无效的,迟早会在执行某些指令的时候出问题。因此,需要尽快刷新CS、SS、DS、ES、FS和GS的内容,包括它们的段选择器和描述符高速缓存器。二是进入保护模式前,很多指令已经进入流水线。因为处理器工作在实模式下,所以它们都是按16位操作数和16位地址长度进行译码的,即使是那些用bits 32编译的指令。进入保护模式后,受CS段描述符高速缓存器中实模式残留内容的影响,处理器进入16位保护模式工作。如果保护模式下的代码是16位的,影响可能不大,但如果是bits 32编译的,那么,由于对操作数和默认地址大小的解释不同,指令的执行结果可能会不正确,所以必须清空流水线。同时,那些通过乱序执行得到的中间结果也是无效的,必须清理掉,让处理器串行化执行,即重新按指令的自然顺序执行。

解决办法是使用远转移指令jmp或者远过程调用指令call。处理器最怕转移指令,遇到这种指令,一般会清空流水线,并串行化执行;另一方面,远转移会重新加载段选择器CS,并刷新描述符高速缓存器中的内容。

不管是16位远转移,还是32位远转移,因为已经处于保护模式下,处理器都将把第一个操作数视为段选择子,而不是实模式下的逻辑段地址。


本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-24 22:34

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表