x86 保护模式编程

一、保护机制概念

       80x86支持两类保护机制。

        (1)任务之间的保护机制

       给每个任务不同的虚拟地址(逻辑地址   段地址:偏移地址)空间来完全隔离各个任务。这是通过给每个任务不同的(从逻辑地址到物理地址的)变换映射机制(函数)。

       因此两个不同任务对相同的逻辑地址(虚拟地址)的引用将变换映射到不同的物理地址。这使得操作系统可以给予每个任务的相同的虚拟地址,但仍然可以隔离每个任务的地址空间。

         (2)特权级保护

   对任务进行操作时需要满足特权级保护的规则,以保护操作系统内存段(数据段和代码段等)和处理器特殊寄存器(TR,GDTR,IDTR等)不被应用程序访问。linux特权级用数字0和3表示,0代表最高特权级(内核态)和3代表最低特权级(用户态)。每个内存段都和一个特权级相关联。

二、一致性与非一致性代码段

  • CPL是当前进程的权限级别(Current Privilege Level),是当前正在执行的代码所在的段的特权级,存在于cs和ss寄存器的低2位(第0,1位)。CPL表示的是程序或者说任务的当前特权级,它不属于某个段,当前的程序或任务不可能同时表现出2种特权级,那么CS和SS的第0位和第1位应该总是相同的。尝试将RPL异于CPL的数据段选择子装入SS会引起异常。它们总是相同的。
  • DPL(Descriptor Priviliege Level):表示段或者门的特权等级。它存储在段或者门描述符的DPL字段中。当前代码段试图访问一个段或者门时,DPL将会和CPL以及RPL作比 较,根据段或者门类型的不同,DPL将会被区别对待:数据段:DPL规定了可以访问此段的最低特权等级。非一致代码段(不使用调用门的情况下):DPL规定了访问此段的特权级。调用门:与数据段一致。一致代码段和通过调用门访问的非一致代码段:DPL规定了访问此段的最高特权等级。TSS:与数据段一致。
  • RPL说明的是进程对段访问的请求权限(Request Privilege Level), 是对于段选择子而言的,每个段选择子有自己的RPL,它说明的是进程对段访问的请求权限,有点像函数参数。而且RPL对每个段来说不是固定的,2次访问同一段时的RPL可以不同。RPL可能会削弱CPL的作用,例如当前CPL=0的进程要访问一个数据段,它把段选择符中的RPL设为3,这样它对该段仍然只有特权为3的访问权限。处理器通过检查RPL和CPL来确认一下请求是否合法。即便提出访问请求的代码段有足够的特权级,如果RPL不够也是不行的。(CPL <= DPL) && (RPL <= DPL)


         对于一致代码段:也就是共享的段.

        <1>.特权级高的程序不允许访问特权级低的数据:核心态不允许调用用户态的数据.

        <2>.特权级低的程序可以访问到特权级高的数据.但是特权级不会改变:用户态还是用户态.

        对于普通代码段.也就是非一致代码段:

        <1>.只允许同级间访问.

        <2>绝对禁止不同级访问:核心态不使用用户态.用户态也不使用核心态.

讨论特权级的比较

  1. CPL表示的是程序或者说任务的当前特权级,它不属于某个段,当前的程序或任务不可能同时表现出2种特权级,那么CS和SS的第0位和第1位应该总是相同的。尝试将RPL异于CPL的数据段选择子装入SS会引起异常。另外后面在论述跳转的时候可以看到,它们总是相同的。
  2. DPL表示的是某个段或门的特权级,当程序要访问段A的时候,会将CPL和段A的DPL作比较,以确定程序是否有权限访问段A。
  3. RPL表示选择子是否有权限访问其所指向的段。选择子指向一个段描述符,段描述符指向一个段,段的特权级由段描述符中的DPL决定。当程序需要访问段 A的时候,需要先通过段A的选择子加载段A的段描述符,然后才能访问段A,在这个过程中CPU会先将段A选择子中的RPL和段A描述符中的DPL作比较,确定选择子是否有权限访问其所指向的段,成功后才是CPL和段A的DPL的比较。RPL、 DPL之间的比较规则与CPL、DPL之间的比较规则一致。举个例子解释前面这句话的意思:比如当前程序要访问一个数据段A,那么CPL不能大于段A的 DPL,否则失败(后面会讲到这一点)。这是CPL和段A的DPL的比较规则,那么同样段A的RPL、DPL也遵循这样的规则,也就是段A的RPL不能大于段A的DPL。RPL和CPL不会进行比较。
  4. 不管什么情况下,相同特权级之间的访问总是不会错的,所以后面的讨论中通常会忽略相同特权级之间的比较。
  5. 保护模式下代码段中可以存放数据,但数据段中不能存放代码(跳转不过去)。所以代码段既可以获取代码段中的数据(被读的代码段属性需要可读,就算是获取自身段内数据也需要可读),也可以获取数据段中的数据(数据段总是可读的),还可以跳转到其他代码段。而数据段除了被读取,什么都做不了。
  6. 代码段分为一致代码段和非一致代码段,这会对特权级比较产生影响,是否一致由段描述符的第42位决定。数据段和代码不同,它总是非一致的。代码段只在作为被访问一方(或者说目标代码段)时一致性才会对特权级比较产生影响,在作为访问一方时没有区别。是否一致代码段的区别是,在段间跳转过程中,如果目标代码段是一个特权级更高的一致代码段,那么跳转成功,并且CPL不会改变(CS和SS都不会变),于是CPL异于目标代码段的RPL(CPL数值更大,特权级更低);如果目标代码段是一个特权级更高的非一致代码段,那么跳转是会失败的,此时需要使用调用门。另外,如果目标代码段是一个特权级更低的代码段(不论是否一致),那么跳转总是会失败的,除非使用RETF跳转。
  7. 如果当前程序要访问一个数据段A,那么CPL不能大于段A的DPL,否则失败。也就是说当前程序不能访问特权级更高的数据段。CPL可以小于段A的 DPL,也就是说当前程序可以访问特权级更低的数据段。当前程序对调用门和TSS的访问规则与此一致。
  8. 特权级的检查是在选择子被装入段寄存器的时候进行的。先看相对简单一点的数据段A选择子装入DS的情况(装入ES,FS,GS类似,这里以DS为例说明):段A的DPL必须大于RPL,同时还必须大于CPL(第7点),否则产生异常,段A的RPL不和CPL进行比较。另外,当程序向低特权级跳转时,会检查CPL和DS指向的段的DPL,如果DPL小于CPL,那么DS会被加载空描述符的选择子。
  9. 数据段A选择子装入SS的情况(只有数据段选择子才能装入SS,代码段选择子不能装入SS,不管是否可读):段A的RPL和DPL都必须和CPL相等,否则失败。来看下为什么会这样。CPU要保证CS和SS中的第0和第1位(CPL)一致,所以段A的RPL必须等于CPL。CPU还要保证当前程序的特权级和当前使用的堆栈段的特权级一致,所以段A的DPL要等于CPL。
  10. 代码段A选择子装入DS的情况(装入ES,FS,GS类似,这里以DS为例说明):如果段A不可读,装入失败。如果段A是可读的非一致代码段,那么 CPL、段A的RPL都必须等于段A的DPL,否则装入就会失败。如果段A是可读的一致代码段,那么CPL、段A的RPL都可以大于段A的DPL,但不能小于段A的DPL。代码段A用段超越前缀CS读取自身段内数据的时候,虽然没有选择子的装入过程,但CPU会检查段A是否可读,如果不可读会产生异常。
  11. 最后是代码段的段间跳转。段间跳转可以用JMP、CALL、RETF和调用门。从高特权级到低特权级只能用RETF;从低特权级到高特权级非一致代码段,只能用调用门,而且还必须是用CALL指令来使用调用门;从低特权级到高特权级一致代码段JMP、CALL、调用门都可以。
  12. 保护模式下的“一致”与”非一致“,指的是当前特权级CPL和要访问的目标代码段的DPL的关系说的。

    对于“non-conforming code segment”来说,当合法访问到目标代码段时,当前特权级CPL会随着跳转被设定成目标代码段的DPL,因此是“非一致的”;

    对于"conforming code segment"来说,当合法访问到目标代码段时,CPL不发生改变,因此是“一致的”;


发布了59 篇原创文章 · 获赞 8 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章