「x86」- 特权级(Privilege Level)学习笔记

  CREATED BY JENKINSBOT

该笔记是《x86 汇编语言:从实模式到保护模式》的「第 14 章 任务和特权级保护」的学习笔记。

程序,Program,是记录在载体上的指令与数据。

任务,Task,是正在执行的程序副本。

LDT – Local Descriptor Table

为了实现任务的隔离,处理器建议每个任务都应该具有自己的描述符表,局部描述符表(LDT,Local Descriptor Table),并将属于任务自己的段保存到 LDT 中。

与 GDT 不同,LDT 的零号槽位可是可用的。

LDTR – LDT Register

LDT 具有多个,为了追踪,处理器使用 LDTR(LDT Register)来追踪和访问 LDT,LDTR 的结构与 GDTR 相同,包含 32-bit 线性基地址 以及 16-bit 段界限 字段。

正在执行的任务为当前任务(Current Task),LDTR 只有一个,它指向当前正在运行的任务。任务发生切换时,会更新 LDTR 的内容,以指向新任务。

TSS – Task Status Segment

当任务发生切换时,需要保存当前任务的状态(比如各种寄存器),否则下次切换回该任务时无法继续执行。保存任务状态的段,本质上还是内存段,被成为任务状态段(TSS,Task Status Segment)。

该段格式固定,最小 104 字节,处理器固件能够识别 TSS 的每个元素。在任务切换时,处理器会读取其中的信息。

TR – Task Register

任务寄存器,TR,Task Register,用于指向当前任务的 TSS。当任务发生切换时,TR 也会指向新任务的 TSS。

切换过程(简述):处理器将当前任务状态保存到 TR 执行的 TSS 中,然后使 TR 指向新任务的 TSS,并从新的 TSS 中恢复任务状态

特权级别,Privilege Level

特权级别,Privilege Level,是存在于 Descriptor 及 Segment Selector 中一个数值,当这些 Descriptor 或 Segment Selector 要进行某些操作,或者被别的对象访问时,该数值用于控制它们能够进行的操作或者限制它们的可访问性。

Intel Processor 具有 4 个特特权级别(0-3)

描述符特权级(DPL,Descriptor Privilege Level)

每个 Descriptor 都具有描述符特权级(DPL,Descriptor Privilege Level)字段。Descriptor 总是指向它所“描述”的目标对象,代表带对象,因此该字段(DPL)实际上是目标对象的特权级

当前特权级(CPL,Current Privilege Level)

当处理器正在某个代码段中取指令执行时,该代码段的特权级叫做当前特权级(CPL,Current Privilege Level),这就是在段寄存器 CS 中 Segment Selector 的最低两位。

特权指令(Privileged Instructions)

某些指令只有 CPL == 0 时才能执行,这些指令称之为特权指令,比如 hlt 或者 对控制寄存器 CR0 的写操作。

输出特权级(I/O Privilege Level)

除此之外,还有输入输出特权级(I/O Privilege Level),使用 EFLAGS 的 IOPL(13-14)位进行控制。

控制转移的方法

代码段的特权级检查是很严格的,一般来说,控制转移只允许发生在两个特权级相同的代码段间。比如 CPL == 2 可以转移到 DPL == 2 的代码段,但是不能转移到 DPL == 0/1/3 的代码段。

但是为了使特权级低的程序调用特权级高的操作系统例程,处理器提供相应的解决方法:

方法一、将高特权级的代码段定义为依从的

对于高特权级的代码段,将其 TYPE 字段的 C 位设置为 1(即依从的代码段),这样特权级低的程序则可以调用进入高特权级的代码段

但是,特权级的检查依旧非常严格:必须保证 CPL 低于 DPL,即数值上 CPL >= DPL (值越大,特权级越低,所以是 >= 符号,没有写错)。比如说 DPL == 1 则只有 CPL == 1/2/3 可以调用。

另外,特权级不会发生变化,即:对于定义为依从的高特权级的代码段,当从特权级低的程序调用进入后,当前特权级依旧是特权级低的程序的特权级,而不使用高特权级代码段的特权级。比如从 CPL == 3 进入 DPL == 1 代码段,依旧 CPL == 3 而不是 CPL == 1,这也是被称为“依从”的原因。

方法二、使用调用门(Call Gate)

# 门,Gate,也是种描述符,有调用门,陷阱门/中断门,任务们。这里学习的是调用门(Call Gate)

调用门也是 64-bit 的 Descriptor,在 Call Gate Descriptor 中,描述了目标例程的(1)代码段选择子(2)以及段内偏移。

调用 Call Gate 指向的函数,只需要使用调用门选择子作为 CALL FAR / JMP FAR 的操作数。

使用 JMP FAR 不改变特权级,依旧使用 CPL 运行。使用 CALL FAR 将改变特权级(CPL = DPL)。另外,除了从高特权级例程中返回,不允许从高特权级例程进入低特权级历程。

参考文献

阅读《x86 汇编语言:从实模式到保护模式》的「第 14 章 任务和特权级保护」