特权级转移比较复杂,但可以归纳为两大类.
1.对于代码段,只能从低到高访问.
2.对于数据段,只能从高到低访问.
然后再分解:
代码段从低向高(一致,目标特权级转换为访问者特权级)或相同(非一致).
数据段总是非一致.
描述符本身是数据段.比如调用门,TTS本身都是数据段,所以必须从高特权级
向低特权级访问,即访问者特权级<=门,TTS的DPL.(值小于等于表示特权级高).
而通过门来访问代码段,无论是一致还是非一致代码段,原则上是从低向高访问.
在非一致代码段上,只有jmp指令要求访问者特权级和通过门访问的目标代码段相等,
其它的都可以从低向高访问.
所以要通过门调用条件是:1.先要有权访问门本身(从高到低),然后就可以从低到高.
最后加上RPL.当RPL比CPL低时,RPL覆盖CPL.这样即使访问者的段有足够的特权级但RPL
不够时也访问不了高特权级的数据.防止低特权级构造高特权级的选择子来访问高特权
级的数据(当前RPL会被放置到选择子的RPL中).
如果目标代码段特权级低,则无法通过call和jmp转移进去.只能通过retf指令跳转.
为什么通过retf可以降低特权级呢?
既然call,jmp是从调用者的低特权级向被调用者的高特权级访问的(非一致代码段相等),
那么在ret/retf时,是从被调用者的高特权级向调用者的低特权级返回,所以处理器的判断
逻辑是要返回目标代码(调用者)的RPL,DPL都应该比当前代码段(被调用者)低.当调用者
的代码段的RPL比当前的CPL低时,就应该切换到更低的RPL特权级运行.
当然原来调用者的cs,eip都是处理器在调用之前压栈保存的,但是如果当前代码段在
高特级下,就可以访问到要跳转的低特权级选择子和描述符,堆栈段,那么我们就可以把
要进入的低特权级ss,esp,cs,eip象从它调用高特权级之前被CPU保存那样手工构造出来.
然后调用retf来模拟从高特权级的被调用环境中返回调用者低特权级环境中,从而实现从
高特权级向低特权级的跳转.
这里的跳转一定要使用retf,因为ret虽然可以实现长跳转,但它不返回cs,而切换特权级
就是根据cs的RPL决定的.
ret:
(ip)=((ss)*16+(sp))
(sp)=(sp)+2
->pop ip
retf:
(ip)=((ss)*16+(sp))
(sp)=(sp)+2
(cs)=((ss)*16+(sp))
(sp)=(sp)+2
->pop ip
->pop cs
不同特权级的代码段使用不同的堆栈段,为了防止低特级权的代码段大量使用高特权级的
栈而使内核崩溃,所以在不同特权级之间切换要进行栈转移,TTS就是为了从低到高访问时
指示目标代码段的ss和esp,同时将原来的ss和esp压入新栈中以便返回时调用.
直接跳转(jmp,call)对一致代码段只能从低到高,而且CPL不能改变,非一致代码段只能在
相同的特权级之间.
使用调用门在一致和非一致代码段都也可以实现*跳转.