接上节。
图中第132行的jmp指令,段选择子为SELECTOR_CODE,其RPL的值为RPL0,RPL0定义在include/boot.inc中,其值为0。选择子的索引部分值为1,表示对应GDT中第1个段描述符,该描述符的DPL为0,(它是用include/boot.inc中的DESC_DPL_0定义的,图中未展示)。
在跳转之前,CS为0,其低2位RPL部分为0,也就是CPL为0,当执行跳转指令jmp dword SELECTOR_CODE:p_mode_start时,目标代码段(即第1个段描述符)的DPL为0,与当前特权级一致(处理器会根据CPL、RPL、DPL做特权级检查,此检查过程咱们在介绍完RPL时再讨论),处理器允许转移,所以新的特权级依然是0,该值保存在段寄存器CS的低2位,这就是特级级转移的粗略过程,也是进入保护模式后特权为0的来龙去脉。
说完了CPL,咱们再看看,受访者的特权标签在哪里。
在段描述符中有一个属性还为该内存标明了特权等级,这就是段描述符中的DPL字段的作用,它就是受访者的特权标签。话说,不仅只有段描述符中有DPL字段,以后所介绍的所有描述符都有DPL。
DPL,即Descriptor Privilege Level,描述符特权级,这下您清楚为什么DPL字段在段描述符中占2位的原因了吧,两位能表示4个组合,00b、01b、10b、11b,所有特权级都齐了。
计算机是人发明的,用人的思想来理解计算机原理是再合适不过的。拿校园生活举例,班长权限比班主任低,班长有权限安排学生打扫卫生,班主任有权限查看学生成绩。班长没权限查看学生成绩,但班主任有权限安排学生打扫卫生。这就是拥有高特权级的事物可以访问同级或更低特权级资源,而低特权级的事物无法访问高特权级资源的典型例子。
在计算机中也一样,DPL是段描述符所代表的内存区域的“门槛”权限,访问者能否迈过此门槛访问到本描述符所代表的资源,其特权级至少要等于这个门槛,访问者特权能否大于该门槛?这要看受访资源是代码还是数据啦。不难想像,只有具备“能动”行为的访问者才具备访问的能力,在计算机中真正的访问者是硬件cpu,而指挥cpu行为(访问谁及如何访问)的是具有可执行能力的指令代码,数据是不能访问别人的,所以我们再强调一下访问者就是代码段中的指令,这对理解当前特权级非常重要。
访问者任何时候都不允许访问比自己特权更高的资源,无论受访资源是数据还是代码。在不涉及RPL的前提下,下面咱们要分情况讨论啦。
对于受访者为数据段(段描述符中type字段中未有X可执行属性)来说:
只有访问者的权限大于等于该DPL表示的最低权限才能够继续访问,否则连这个门槛都迈不过去。比如,DPL为1的段描述符,只有特权级为0、1的访问者才有资格访问它所代表的资源,特权为2、3的访问者会被cpu拒之门外。
对于受访者为代码段(段描述符中type字段中含有X可执行属性)来说:
只有访问者的权限等于该DPL表示的最低权限才能够继续访问,即只能平级访问。任何权限大于或小于它的访问者都将被cpu拒之门外。这是为什么呢?自问自答之前先明确一个概念,对于受访者为代码段一这说法,实际上是指处理器从当前运行的代码段上转移到受访者这个目标代码段上去执行,并不是说把该目标代码段当数据一样访问,在真实物理机器上,代码段通常情况下不被当成数据来处理的,但确实可以这么做(话说虚拟机中会把代码当成数据来处理)。
咱们先说为什么比它特权级更高的代码也无法“访问”它(即转移到它上面运行)。
代码指令代表cpu的行为,低特权级的代码能做的事,高特权级代码也能做,换句话说高特权的代码不需要低特权代码的帮助,正常情况下cpu没有理由先自降等级后再去做某事。代码段是cpu执行的指令,不是数据,这里所说的“受访者为代码段”其实就是指cpu从访问者所在的段转移到该代码段上去执行。举个例子,cpu若相当于汽车,代码则相当于司机,它指挥cpu前进的方向。段的变换相当于换了司机,特权级较高的代码段相当于技术水平较高的F1车手,特权级较低的代码段相当于技术水平较低的普通司机,这辆车为了充分展示性能、活得更加精彩,它始终希望它的搭档是驾驶技术高超的F1车手,要是把搭档降级为普通司机,它可万万不能答应啊。
不过,凡事都有例外的时候,这是唯一一种处理器会从高特权降到低特权运行的情况:处理器从中断处理程序中返回到用户态的时候。
中断处理都是在0特权级下进行的,因为中断的发生多半是外部硬件发生了某种状况或发生了某种不可抗力事件而必须要通cpu导致的,所以,在中断的处理过程中需要具备访问硬件的能力,在大多数情况下只有cpu处于0特权级才能访问硬件,这是因为eflags寄存器中的IOPL位的值通常被设置为0(该位的作用就是限制访问IO端口的最低特权级),并且TSS中不存在 IO位图,有关这部分后面马上会讲到。再者,有些中断处理中需要的指令只能在0特权级下使用,这部分指令称为特权指令,所以中断发生后其处理的过程必须在0特权级下进行。用户进程是在3特权级,在运行用户程序时若发生了中断,cpu会暂停用户程序的执行,随后cpu就会自动由3特权级进入到0特权级,在0特权级下将执行用户程序时的现场环境(也就是著名的概念:上下文)保存起来(这个保存上下文的动作可以由cpu通过TSS完成,这是cpu在硬件上提供的功能,但其效率并不高,所以大多数操作系统都是自己写代码手动保存上下文环境),待中断处理完成后,cpu会恢复用户程序的执行,也就是说会回到3特权级。以后在讲了中断和用户进程时大伙儿会更清楚这一点。
现在大家知道了,除了从中断处理过程返回外,任何时候cpu都不允许从高特权级转移到低特权级。再结合之前咱们所说的大前提,访问者任何时候都不允许访问比自己特权更高的资源,代码段也是资源,只不过可以让cpu转移过去执行而已,所以,比目标代码段特权级低的访问者也会被拒绝访问目标代码段。综上所述,对于受访问者为代码段的情况,只能是平级访问。也就是说,假如当前特权级为2,只能转移到DPL为2特权级的代码段上运行,转移到0、1、3特权级都会被处理器拒绝。
不过本质上来说,代码能否运行与代码本身的特权等级并无关系,不同等级下的代码段中的机器码都是一样的,特权级只是写在描述符的DPL中,并没有写在机器码中,所以高特权级的代码并不比低特权级的代码显得“高大上”,目标段(代码段或数据段)的特权级仅仅是在被访问时由处理器检查一次,之后再无用途,所以特权级并不影响处理器执行指令。把计算机资源划分成不等级,只是让处理器知道自己正在处理的资源的“份量”有多重,必须用同样的身份来执行,不能儿戏。
如果处理器仅能平移代码段的话,另外三个特权级的代码将没有机会运行啦。如何穿过特权屏障呢?处理器又提供了多种方式用于从低特权的代码转移到高特权代码。
处理器的特权级升高之后,程序想干什么就干什么,多少都觉得有点恐怖,有没有一种好办法,即执行高特权级代码段上的指令,又不提升特权级?一种方式是利用一致性代码段。
什么是一致性代码段?早在当初介绍段描述符结构时就已经提过它了,不过确实只是提了一下而已以至于您可能完全没有印象^_^。在段描述符中,如果该段为非系统段(段描述符的S字段为0),可以用type字段中的C位来表示该段是否为一致性代码段。C为1时则表示该段是一致性代码段,C为0时则表示该段为非一致性代码段。上面所提到的代码段是非一致性代码段,所以只能平级转移。
一致性代码段也称为依从代码段,Conforming,用来实现从低特权级的代码向高特权级的代码转移。一致性代码段是指,如果自己是转移后的目标段,自己的特权级(DPL)一定要大于等于转移前的CPL,即数值上CPL>=DPL,也就是一致性代码段的DPL是权限的上限,任何在此权限之下的特权级都可以转到此代码段上执行。这是似乎很奇怪但却意料之中,奇怪的是,低特权级访问高特权级居然是可以的,而意料之中的是,这才能实现了代码转移。该关系用公式表示如下:
在数值上,CPL >=一致性代码段的DPL。
一致性代码段的一大特点是,转移后的特权级不与自己的特权级(DPL)为主,而是与转移前的低特权级一致,听从、依从转移前的低特权级,这就是它称为“依从、一致”的原因。也就是说,处理器遇到目标段为一致性代码段时,并不会将CPL用该目标段的DPL替换。
大家注意啦,既然是转移到特权级更高的一致性代码段后CPL不变,这说明这种转移本身并没有提升特权级,只是可以跑到特权级更高的代码段中去执行指令,对计算机而言并未造成因特权级升高而产生潜在危险,所以在特权级检查过程中,请求者的RPL并不参与。
特权级检查是发生在访问者访问受访者的一瞬间,只检查一次,在检查过后,在该段上以后的执行过程中也不会再被检查,处理器也并不会因为执行了高特权级的代码而觉得自己了不起,在它眼里任何级别的指令都是一样的。特权级就是一个个的关卡,仅仅是进门时的检测而已,与关卡后面的代码和数据无关。这种情况类似必须要刷工卡才能进公司一样,即使不刷工卡,您的工位还是那个工位,电脑还是那个电脑,您照样还是可以使用它们,这些资源并不会有什么改变,它们与咱们是否刷工卡是无关的。所以,尽管转移到一致性代码段后CPL还保持为原来的低特权级并未提升,但也没什么好奇怪的,毕竟目的是运行目标代码段上的指令,达到目的就行了,何必在意身份呢,人家西游记里的孙悟空当初只是弼马温的时候,由于其身份卑微是无法参加王母娘娘蟠桃大会的,最后还不是一样把蟠桃吃个够,碉堡了^_^。
顺便说一句,代码段可以有一致性和非一致性之分,但所有的数据段总是非一致的,即,数据段不允许被比本数据段特权级更低的代码段访问。
按理说,把代码划分为不同等级就是为了等级变换,如果还以卑微的身份去运行高特权级的代码,似乎不够体面,人家孙悟空为了面子还号称为齐天大圣呢,其实cpu中实现特权级变换的“门路”多着呢,还有4个“门”。