保护模式的段式机制小结``

来源:互联网 发布:mac系统终端登录锐捷 编辑:程序博客网 时间:2024/06/09 20:20

保护模式之段式存储机制综述:

 

谈到保护模式,其实都是与实模式相对而言的。总的来说,32位的cpu有两种工作模式:实模式与保护模式。当我们开机后,开始cpu是工作在实模式下的,经过某种转换机制后进入了保护模式。

 

回忆下实模式下的寻址机制:Intel 8086就是典型的16cpu,它有16位的寄存器,16位的数据总线和20位的地址总线和1MB的寻址能力。它的物理地址由段地址和偏移量两部分组成。

 

保护模式下段寻址机制:8038632位的寄存器(通用寄存器在保护模式下都变成32位的,但段寄存器没有改变,仍然是16位的),32位的地址线,寻址能力达4GB。保护模式下的寻址机制是利用一个称作段选择子(由16位的csds等段寄存器表示)的偏移量,从而到描述符表(GDT表也可能是LDT表)找到相应的描述符,而这个描述符中就放着真正的段的物理首地址,再加上偏移量就可以到段的任意位置处。

     选择子——段描述符表——段描述符(含段物理首地址)+偏移量—— 相应段内

 

保护模式下涉及到的关键术语:

段选择子

段描述符(GDTLDT)

段描述符表

段选择子(16位)通俗的说其实就是一个指针,指向段描述符表中的某一个描述符,通常是由csds等寄存器保存。

段描述符GDT(64位)是一种数据结构,由段基址(32位)、段界限(16位)、段属性构成,由于历史问题(具体情况不清楚),段基址被拆开了,分布在第2.3.4.7字节里。初始化段描述符时会用到。

段描述符LDT(局部描述符)是和GDT差不多的一种结构体数据类型,它也可以建立一个LDT表,即含有多个LDT段描述符,即可以把多个段封装在一个LDT里。

段描述符表其实也是一种数据结构,是由多个段描述符构成的一张表。

另外要知道的是段描述符表还分配有个相应的寄存器叫GDTR48位的),低16位为GDT表的界限,高32位是GDT表的基地址。其实cpu是通过GDTRGDT的基地址)和选择子(GDT表中的偏移量)来找到一个固定的段描述符的。

 

了解了保护模式,之后我们在来看一下怎么在dos下实现从实模式到保护模式的转换。

从上面已经知道,保护模式下段的寻址是通过段选择子找到相应的段描述符来实现的,因此在跳转之前需要在实模式下初始化段描述符,也就是给段描述符一个相应的物理首地址(分布在2347字节里);

再就是初始化GDTR寄存器,用指令lgdt加载;

之后就是打开A20地址线(原因主要是Intel公司考虑的兼容问题,不过测试了下,现在的主板都是默认打开的,不用程序中打开也行);

最后就是置cr0PE位;

jmp实现跳转,并加载段寄存器cs

 

特权级规则及其堆栈切换原理

CPL:当前特权级别。当前正在运行的代码所处的特权级别,保存在CSSS的低2位中。

DPL:描述符特权级别,即段描述符中DPL字段的值,是与描述符相关的特权级别。

RPL:请求特权级别,即用于访问代码、数据或者堆栈的段选择子低2位的值。

注意特权级别的表示:有034个特权级别,0级最高,3级最低,即数字大则特权级别低。表中CPLRPLDPL等都是代表数字的。

1)一致码段:无论采用哪种方式跳转到一致码段,CPL都不改变(不变化为目标代码段的DPL),也即在加载目标代码段选择子时(段选择子加载到cs寄存器),只加载高14位,表示CPL的低2位保持不变。

2)非一致码段:无论采用哪种方式跳转到非一致码段,CPL都发生改变,也即在加载目标代码段选择子时,将整个选择子放入到CS中。

一致的意思就是:代码段被调用执行时不使用自己的描述符的DPL,而采用调用者特权级别,CS的低2位保持不变,与调用者特权级别保持一致。

特权级的检验规则总的来说,可以归纳为如下的一张表格:

 

目标代码段

Jmp指令

Call指令

直接调用

通过调用门

一致码段

CPL>=DPL,不检查RPL

可以跳转到相同或者更高特权级别执行

CPL<=DPL_GRPL<=DPL_G

CPL>=DPL

访问调用门的规则同访问数据段,即调用门只能被特权级别不低于其DPL(DPL_G)的代码访问。可以跳转到相同或者更高特权级别执行。

非一致码段

RPL<=CPL=DPL

只能跳转到同特权级别的代码段

 

任务状态段TSS

为避免相互干扰,要求不同特权级别的代码运行时使用不同的堆栈,也就是在特权级别发生改变时必须切换堆栈段。012特权级别的堆栈指针保存在TSS中,在跳转到相应级别时从TSS中取出相应的堆栈指针进行堆栈切换。因为只有从低特权级别跳转到高特权级别时才需要从TSS中取得新的堆栈指针,所以TSS中不存在最低特权级别3的堆栈指针。故TSS状态段应该在程序开始处根据要求初始化,以便后来方便使用。

 

call指令使用调用门且发生特权级改变时,相应的堆栈发生了切换。

根据被调用段的DPLTSS中找到相应的SSESP并加载,之后把调用者的SSESP压栈以便返回时恢复调用者的堆栈段,再把调用者的段中的参数复制到新的堆栈中(根据DCOUNT而定),最后再压入调用者的cseip以便恢复。这个压栈的工作可以由cpu处理,也可以由程序员加载指令执行。通过调用门可以实现任意级别的转换,从低到高的转换用retf指令(在它之间可以把ssespcseip等需要返回到的段的信息压栈);从高到低的转换用call调用调用门实现。在程序中灵活运用就行。

在涉及到特权级的每一步中,处理器都会对CPURPLDPL等进行比较,检查是否满足特权级转换要求,这其实也是一种保护措施,保护程序之间的合法访问,禁止非法的越级访问!

2009 3 23日晚

原创粉丝点击