一、概述
在保护模式下,80x86提供了段级和页级保护机制。这种保护机制根据特权级提供了对某些段和页面的访问限制能力。例如,操作系统代码和数据存放在要比普通应用程序具有搞特权级的段中。此后处理器的保护机制将会限制应用程序只能按照受控制的和规定的方式访问操作系统的代码和数据。
当使用保护机制时,每个内存引用都将受到检查以验证内存引用是否符合各种保护要求。因为检查操作是与地址变换同时并行操作,所以处理器性能并没有受到影响。所有违反保护的操作都将导致产生一个异常。所进行的保护检查可分为以下几类:
- 段界限检查
- 段类型检查
- 特权级检查
- 可寻址范围限制
- 过程入口点限制
- 指令集限制
二、段限长Limit检查
段描述符的段限长字段用于防止程序或过程寻址到段外内存地址。段限长的有效值依赖于颗粒度G标志的设置状态。对于数据段,段限长还与标志E(扩展方向)和标志B(默认栈指针大小和/或上界限)。E标志是数据段类型的段描述符在类型字段的一个比特位。
当G标志清零时(字节颗粒度),有效的段长度是20位的段描述符中段限长字段Limit的值。在这种情况下,Limit的范围从0到0xFFFFF(1MB)。 当G标志置位时(4KB页颗粒度),处理器把Limit字段的值乘上一个因子4K,在这种情况下,有效的Limit范围是从0xFFF到0xFFFFFFFF(4GB)。 当设置了G标志时,段偏移的低12位不会与Limit进行对照检查。例如,当段限长Limit等于0时,偏移值0到0xFFF任然是有效的。
除了下扩段(向下扩展的段)以外的所有段类型,有效Limit的值是段中允许被访问的最后一个地址,它要比段长度小1个字节。任何超出段限长字段指定的有效地址范围都将导致产生一个一般保护异常。
对于下扩数据段,段限长具有统样的功能,但其含义不同,这里段限长指定了段中最后一个不允许访问的地址,因此在设置了B标志的情况下,有效偏移范围是从(有效段偏移+1)到0xFFFFFFFF;当B清零时,有效偏移范围是从(有效段偏移+1)到0xFFFF。当下扩段的段限长为0时,段会有最大长度。
除了对段限长进行检查,处理器也会坚持描述符表的长度。GDTR、IDTR和LDTR寄存器中的16位限长值,处理器用它来防止程序在描述符表的外面选择描述符。描述符表的限长值指明了表中最后一个有效字节。因为每个描述符是8字节长,因此含有N个描述符项的表应该具有限长值8N-1。
选择符可以具有0值。这样的选择符指向GDT表中的第一个不用的描述符项。尽管这个空选择符可以被加载进一个段寄存器中,但是任何使用这种描述符引用内存的企图都将产生一个一般保护异常。
三、段类型TYPE检查
除了应用程序代码和数据段有描述符以外,处理器还有系统段和门两种描述符类型。这些数据结构用于管理任务以及异常和中断。需要注意的是,并非所有的描述符都定义一个段,门描述符中存放有指向一个过程入口点的指针。段描述符在两个地方含有类型信息,即描述符中的S标志和类型字段TYPE。处理器利用这些信息对由于非法使用段或门导致的编程错误进行检测。
S标志用于指出一个描述符是系统类型还是代码或数据类型的。 TYPE字段另外提供了4个比特位用于定义代码,数据和系统描述符的各种类型。具体查看代码,数据和系统描述符的各种类型请看这两篇文章:
当操作段选择符和段描述符时,处理器会随时检查类型信息。主要在以下两种情况下检查类型信息:
- 当一个描述符的选择符加载进段寄存器中。此时某些段寄存器只能存放特定类型的描述符
- cs寄存器中只能被加载进一个可执行的段选择符;
- 不可读可执行段的选择符不能被加载进数据段寄存器中;
- 只有可读可写的段的选择符才能被加载进SS寄存器中。
- 当指令访问一个段,而该段的描述符已经加载进段寄存器中。指令只能使用某些预定义的方法来访问某些段。
- 任何指令不能写一个可执行段;
- 任何指令不能写一个可写位没有置位的数据段;
- 任何指令不能读一个可执行段,除非可执行段设置了可读标志。
四、特权级
处理器的断保护机制可以识别4个特权级(或特权层),0级到3级。数值越大,特权越小。下图说明了这些特权级如何能被解释成保护环形式。 环中心(保留给最高级的代码、数据和堆栈)用于最紧要软件的段,通常用于操作系统核心部分;中间两个换用于较为紧要的软件。只使用2个特权级的系统应该使用特权级0和3。
处理器利用特权级来防止运行在较低特权级的程序或任务访问具有较高特权级的一个段,除非是在受控的条件下。当处理器检测到一个违反特权级的操作时,它就会产生一个一般保护性异常。
为了在各个代码段和数据段之间进行特权级检测处理,处理器可以识别以下三种类型的特权级:
当前特权级CPL,CPL是当前长在执行程序或任务的特权级。它存放在CS和SS段寄存器的位0和位1中。通常,CPL等于当前代码段的特权级。当程序把控制转移到另一个具有不同特权级的代码段中时,处理器就会改变CPL。当访问一个一致性代码段时,则处理器对CPL的设置有些不同,特权级值高于(即低特权级)或等于一致代码段DPL的任何段都可以访问一致代码段。并且当处理器访问一个特权级不同与CPL的一致代码段时,CPl并不会被修改成一致带码段的DPL。
- 描述符特权级DPL,DPL是一个段或门的特权级。它存放在段或门描述符的DPL字段中。在当前执行代码段试图访问一个段或门时,段或门的DPL会用来与CPL以及段或门选择符中的RPL作比较。根据被访问的段或门的类型不同,DPL也有不同的含义:
- 数据段,其DPL指出允许访问本数据段的程序或任务应具有的最大特权级数值。例如,如果数据段的特权级DPL是1,那么只有运行在CPL为0或1的程序可以访问这个段。
- 调用门,其DPL指出访问调用门的当前执行程序或任务可处于的最大特权级数值(与数据段访问规则相同)。
- 非一致代码段(不使用调用门),其DPL指出程序或任务访问该段必须具有的特权级。例如,如果某个非一致代码段的DPL是0,那么只有运行在CPL为0的程序能够访问这个段。
- 一致和非一致代码段(通过调用门访问),其DPL指出允许访问本代码段的程序或任务应具有的最小特权级数值。例如,如果一致代码段的DPL是2,那么运行在CPL为0的程序就不能访问这个代码段。
- 任务状态段TSS,其DPL指出访问TSS的当前执行程序或任务可处于的最大特权级数值(与数据段访问规则相同)。
- 请求特权级RPL,RPL是一种赋予段选择符的超越特权级,它存放在选择符的位0和位1中。处理器会同时检查RPL和CPL,以确定是否运行访问一个段。及时程序或任务具有足够的特权级(CPL)来访问一个段,但是如果提供的RPL特权级 不足的话访问也将被拒绝。也即如果段选择符的RPL其数值大与CPL,那么RPL将覆盖CPL(即使用RPL作为检查比较的特权级),反之亦然。即始终取RPL和CPL中数值最大的特权级作为访问段时的比较对象。因此,RPL可以用来确保高特权级的代码不会代表应用程序去访问一个段,除非应用程序自己具有访问这个段的权限(??)。
当段描述符的段选择符被加载进一个段寄存器时就会进行特权级检查操作,但用于数据访问的检查方式和那些用于在代码段之间进行程序控制转移的检查方式不一样。