【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护

本章目标

1、通过演示如何创建一个任务,并使之投入运行来学习任务的概念及组成要素,包括任务的全局空间和局部空间、TSS、LDT、特权级等。
2、必须了解特权级不是指任务的特权级,而是组成任务的各个部分的特权级。比如:任务的全局部分一般是0、1和2特权级别的,任务的私有部分一般是3特权级别的。
3、必须清楚CPL、DPL和RPL的含义,以及不同特权级别之间的控制转移规则。
4、熟悉调用门的用法。
5、掌握一些在Bochs下调试程序的新手段。
6、学习一些新的x86处理器命令,包括lldt、ltr、pushf/pushfd、popf/popfd、ret n/retf n、arpl等。同时了解像jmp和call这样的传统指令是如何被赋予一些新的功能的。

14.1 任务的隔离和特权级保护

14.1.1 任务、任务的LDT和TSS
  • 任务:程序是记录在载体上的指令和数据,其正在执行的一个副本,叫做任务(Task)。

  • LDT:为了有效地在任务之间实施隔离,处理器建议每个任务都应当具有自己的描述符表,称为局部描述符表(Local Description Table)。

  • TSS:为了保存任务的状态,并在下次重新执行时恢复它们,每个任务应当用一个额外的内存区域保存相关信息,这叫做任务状态段(Task State Segment)。

  • LDTR:处理器使用局部描述符表寄存器(LDT Register:LDTR)来追踪和访问这些LDT

  • TR:处理器用TR寄存器来指向当前任务的TSS。

14.1.2 全局空间和局部空间

每个任务实际上包括两个部分:全局部分和局部部分。全局部分是所有任务共有的,含有操作系统的软件和库程序,以及可以调用的系统服务和数据;私有部分则是每个任务各自的数据和代码,与任务所要解决的具体任务问题有关,彼此并不相同。
因此,全局地址空间是用全局描述符GDT来指定的,而局部地址空间则是由每个任务私有的局部描述符表LDT来定义的。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第1张图片

14.1.3 特权级保护概念
1、处理器特权级设置
  • Intel处理器可以识别4个特权级别,分别是0到3,较大的数值意味着较低的特权级别
    【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第2张图片

  • DPL:描述符特权级,代表着其所描述的段的特权级别

  • CPL:当处理器正在一个代码段中取指令和执行指令时,那个代码段的特权级叫做当前特权级(Current Privilege Level,CPL)。正在执行的这个代码段,其选择子位于段寄存器CS中,其最低两位就是当前特权级数值

2、特权指令

那些只有在当前特权级CPL为0时才能执行的指令,称为特权指令(Privilege Instructions)
典型的特权指令:lgdt、lldt、ltr、mov、停机指令hlt等

  • 处理器还允许对各个特权级别所能执行的I/O操作进行控制,通常这指的是端口访问的许可权。位13和位12是IOPL位,也就是输入输出特权级,它代表着当前任务的I/O特权级别。
    在这里插入图片描述
3、特权级低的应用程序调用特权级高的操作系统例程
  1. 将高特权级的代码段定义为依从的

    • 通过段描述符的TYPE字段的C字段来设置
    • 依从代码段不是在它的DPL特权级上运行的,而是在调用程序的特权级上运行。即,当前控制转移到依从代码段上执行时,不改变当前特权级CPL
  2. 使用门(Gate)

    • 门是另外一种形式的描述符,称为门描述符,简称门。和段描述符不同,段描述符用于描述符内存段,而门描述符则用于描述可执行的代码
    • 类别:调用门,中断门/陷阱门,任务门
    • “jmp far 调用门”不改变当前特权级别;“call far 调用门”当前特权级会提升到目标代码段的特权级别
4、基本特权检查规则
  • 将控制直接转移到非依从代码段
CPL = 目标代码段描述符的DPL
RPL = 目标代码段描述符的DPL
  • 将控制直接转移到依从代码段
CPL ≥ 目标代码段描述符的DPL
RPL ≥ 目标代码段描述符的DPL
  • 高特权级别的程序可以访问低特权级别的数据段,但低特权级别的程序不能访问高特权级别的数据段
CPL ≤ 目标数据段描述符的DPL
RPL ≤ 目标数据段描述符的DPL
  • 处理器要求,任何时候,栈段的特权级别必须和当前特权级CPL相同
CPL = 目标栈段描述符的DPL
RPL = 目标栈段描述符的DPL
  • 控制通过调用门来转移
需要两个条件
1、当前特权级CPL和请求特权级RPL高于,或者和调用门描述符特权级DPL相同
CPL ≤ 调用门描述符的DPL
RPL ≤ 调用门描述符的DPL
2、当前特权级低于,或者和目标代码段描述符特权级DPL相同,即数值上
CPL ≥ 目标代码段描述符的DPL

14.2 代码清单14-1

14.3 内核程序初始化

本章继续使用上一章的主引导程序,因此内核加载后的GDT布局和上一章一致。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第3张图片

14.3.1 调用门

调用门用于在不同特权级的程序之间进行控制转移。本质上,它只是一个描述符,一个不同于代码段和数据段的描述符,可以安装在GDT或者LDT中。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第4张图片

  • 例程所在代码段的选择子:有了段选择子,就能访问描述符表得到代码段的基地址。之后,对代码段描述符有效性、段界限和特权级进行检查。
  • TYPE字段:标识门的类型,“1100”表示调用门
  • P字段:表示有效位,通常应该为“1”。当它为“0”时,调用这样的门会导致处理器产生异常中断,这是一个故障中断。可以用来统计调用门的使用频率。
  • 参数个数字段:指代压入栈中的参数,寄存器中的参数不计入。
  • DPL字段:调用门的特权级。例如调用门的DPL为2,那么,只有特权级为0、1、2的程序才允许使用该门。
栈切换
  • 通过call far指令通过调用门来实施控制转移时,会改变当前特权级CPL。会导致栈的特权级也会改变,因此需要每个任务除了自己固有的栈之外,还必须额外定义几套栈。
  • 额外创建的栈,其描述符位于任务自己的LDT中,同时,还要在任务的TSS中登记。
14.3.2 调用门的安装和测试

将内核的描述符表中,各个描述符对应的段基地址,修改为调用门选择子。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第5张图片

14.4 加载用户程序并创建任务

14.4.1 任务控制块和TCB链

按处理器的要求标准,要使一个程序成为“任务”,并且能够参与任务切换和调度,必须要有LDT和TSS。
对于操作系统来说,内核应当为每一个任务创建一个内存区域,来记录任务的信息和状态,称为任务控制块(TCB),可能在不同操作系统中有不同的称呼,但是必定是有一个类似的结构的。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第6张图片
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第7张图片

【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第8张图片

14.4.2 用栈传递过程参数

栈的访问有两种

  • 隐式的,由处理器在执行诸如push、pop、call、ret等指令时自动进行。隐式地访问栈需要使用指令指针寄存器ESP。
  • 显式的,不依赖于先进后出的机制,而是把栈看成是一般的数据段,直接访问其中的内容。需要使用到栈基址寄存器EBP。
14.4.3 加载用户程序

从硬盘中将程序加载到内存中,但现在还要分配LDT,同时将程序在内存中的位置记录到TCB中。即,以下3件事:

  • 分配一块内存,作为LDT来用,为创建用户程序各个段的描述符做准备。
  • 将LDT的大小和起始线性地址登记在任务控制块TCB中。
  • 分配内存并加载用户程序,并将它的大小和起始线性地址登记到TCB中。
14.4.4 创建局部描述符

流程和创建GDT一样。

14.4.5 重定位U-SALT表

和13章的流程一样,不同点在于,传给用户程序的选择子,现在变成了调用门选择子。

14.4.6 创建0、1和2特权级的栈

安装到LDT,并将栈信息登记到TCB中。

14.4.7 安装LDT描述符到GDT中

全局描述符表(GDT)是唯一的,整个系统中只有一个,所以只需要用GDTR寄存器存放其线性基地址和段界限即可;但LDT不同,每个任务一个,所以为了追踪它们,处理器要求在GDT中安装每一个LDT描述符。当要使用这些LDT时,可以用它们的选择子来访问GDT,将LDT描述符加载到LDTR寄存器。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第9张图片

  • LDT描述符中的S位固定为0,表示系统的段描述符或者门描述符,以相对于存储器的段描述符(S=1),因为LDT的描述符属于系统的段描述符。
  • S=0的前提下,TYPE字段为0010表明这是一个LDT描述符。
14.4.8 任务状态段TSS格式

【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第10张图片

和LDT一样,必须在全局描述符表(GDT)中创建每个TSS的描述符。

  • SS0、SS1和SS2
    分别是0、1和2特权级的栈段选择子
  • ESP0、ESP1和ESP2
    分别是0、1和2特权级栈的栈顶指针
  • CR3
    和分页内容有关
  • 偏移为32~92区域
    处理器各个寄存器的快照部分,用于在进行任务切换时,保存处理器的状态以便将来恢复现场。
    只要第一次创建时需要填写这些区域, 之后该区域内容由处理器固件进行更改。
  • LDT段选择子
    当前任务的LDT描述符选择子。该信息在任务切换时使用,在任务运行期间保持不变。
  • T
    用于软件调试。在多任务的环境中,如果T位是“1”,每次切换到该任务时,将引发一个调试异常中断。这是有益的,调试程序可以接管该中断以显示任务的状态,并执行一些调试操作。
  • I/O映射基地址
    用于决定当前任务是否可以访问特定的硬件端口。如果该字单元的内容大于或者等于TSS的段界限(在TSS描述符中),则表明没有I/O许可位。
    EFLAGS寄存器中的IOPL位决定了当前任务的I/O特权级别。如果当前特权级CPL低于任务的I/O特权级IOPL,也并不意味着所有的硬件端口都对当前任务关上了大门。事实上,处理器的意思是总体上不允许,但个别端口除外。至于个别端口是哪些端口,要找到当前任务TSS,并检索I/O许可位
    【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第11张图片
    【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第12张图片
14.4.9 创建任务状态段TSS

分配内存,以填入TSS的内容。将TSS的基地址和界限登记到任务控制块(TCB)中,将来创建TSS描述符时用得着。
TSS的界限值必须至少为103,任何小于该值的TSS,在执行任务切换时,都会引发处理器异常中断
TSS中,LDT对任务来说并不是必须的,如果没有LDT,这里应该填0

14.4.10 安装TSS描述符到GDT中

必须在GDT中安装TSS描述符。这样做,一方面是为了对TSS进行段和特权级的检查;另一方面,也是执行任务切换的需要。当call far和jmp far指令的操作数是TSS描述符选择子时,处理器执行任务切换操作。

【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第13张图片

格式和LDT描述符差不多,除了TYPE字段。
TSS描述符中的B位是Busy位。在任务刚刚创建的时候,它应该为二进制的1001,即B位是0,表明任务不忙。当任务开始执行时,或者处于挂起状态(临时被中断执行)时,有处理器固件把B位置1。

14.4.11 带参数的过程返回指令
ret和retf,代操作数的过程返回指令
ret imm16/32
ret imm16/32
;都允许16位和32位的立即数作为操作数,不同之处在于,前者是近返回,后者是远返回。

14.5 用户程序的执行

14.5.1 通过调用门转移控制的完整过程
调用门控制转移流程
  1. 首先,通过调用门实施控制转移,可以使用jmp farcall far指令,跳转遵循下述规则。指令执行时,描述符选择子必须指向调用门,32位偏移量被忽略。
    【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第14张图片

  2. call far指令会触发栈切换;jmp far指令不触发栈切换
    具体的检查流程就不细说了,只说结果,如下所示
    【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第15张图片
    【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第16张图片

控制返回过程

【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第17张图片

14.5.2 进入3特权级的用户程序的执行

任务寄存器TR总是指向当前任务的任务状态段(TSS),而LDTR也总是指向当前任务的LDT。TSS是任务的主要标志,因此要使TR寄存器指向TR;而使用LDTR的原因是可以在任务执行期间加速段的访问。
【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第18张图片

ltr指令和lldt指令
ltr r/m16
lldt r/m16
;操纵数是16位通用寄存器或者一个16位单元的内存地址
;ltr和lldt执行时,处理器首先要检查描述符的有效性,包括审查它是不是TSS或者LDT描述符

【x86汇编语言:从实模式到保护模式笔记】第十四章 任务和特权级保护_第19张图片

本章代码因为只有一个任务,而且是个3特权级的任务,不能用任务切换的方法使它开始运行。即,如何从任务的0特权级全局空间转移到它自己的3特权级空间正常执行?
执行一个远返回指令retf,假装从调用门返回,于是控制转移到用户程序的3特权级代码开始执行。

你可能感兴趣的:(Linux,linux)