x86 架构微处理器
- 微处理器基本结构
- 重要结构
- 总线接口单元(BIU):包括数据、控制、指令总线,连接各个组件,用于传递信息。
- 中央处理单元(CPU):包括指令读取、指令译码、执行单元等。
- 内存管理单元(MMU):包括分段和分页组件,负责内存管理和保护、地址转换等。
- 工作模式
- 实模式:CPU 复位后的默认模式,字长只到 16 位,任何代码都具有完全权限。
- 保护模式:从实模式提示后的模式,字长到达 32 位,支持高级功能,划分特权级。
- 虚拟 8086 模式:保护模式下模拟实模式,具有一定兼容性,但权限受限,特权指令需要模拟。
- 重要结构
- 寄存器
- 通用寄存器
EAX:累加器,主要用于算术运算和数据传输。EBX:保存数据,常用于基址寄存器,即作为一段连续内存区域的起始地址。ECX: 保存数据,常用于循环计数器。EDX:保存数据,在使用乘除法指令时保存数据的专用区域。EBP:保存栈帧基址,启用 FPO 后,无特殊职责,可以用于其他用途。EDI:常用于目的操作数索引。ESI:常用于源操作数索引。ESP:固定用于保存栈顶地址。
- 段寄存器
CS:代码段寄存器。DS:数据段寄存器。SS:堆栈段寄存器。ES:附加数据/扩展段寄存器。FS、GS:无明显的命名含义,常用于线程局部存储。- 所有的段寄存器都是 16 位。
- 实模式下,段寄存器保存段基址的高 16 位。
- 保护模式下,段寄存器保存段选择符,间接表示段基址。
- 标志寄存器
CF、ZF、SF、OF参考计算机系统部分的介绍。PF:即 Parity Flag,若运行结果的最低 8 位为res8,则PF == popcount(res8) % 2。AF:即 Auxiliary Carry Flag,若运行结果的最低 4 位向次低 4 位进位或借位,则为1。TF:即 Trap Enable Flag,为1时每条指令执行后产生中断,用于单步调试。IF:即 Interrupt Enable Flag,为1时允许接收中断。DF:即 Direction Flag,为0时串操作地址自动递增,为1时自动递减。IOPL:表示 IO 操作的所需最低特权级,取值0到3。NT:即 Nested Task Flag,仅保护模式可用,如果任务执行在另外一个任务中,则为1。VM:为1时,处于虚拟 8086 模式。
- 控制寄存器
CR0:包括机器状态。- 第 0 位
PE:即 Protection Mode Enable,设置为1则进入保护模式。 - 第 1 位
MP:即 Monitor Coprocessor Extension,若存在协处理器则为1。 - 第 2 位
EM:即 Emulate Processor Extension,为1时使用软件运算,为0用硬件协处理器运算。 - 第 3 位
TS:即 Task Switched,任务切换后被设为1。 - 第 4 位
ET:即 Extension Type,协处理器选择标志,80386 以后,为1表示系统中存在协处理器。 - 第 31 位
PG:即 Paging Enable,为1时启用分页内存管理。
- 第 0 位
CR1:保留,暂无意义。CR2:页面故障线性地址寄存器,发生缺页中断时,保存要访问的虚拟地址。CR3:页目录基址寄存器(PDBR),保存页目录表首地址,必须按页大小 4 KiB 对齐(低 12 位为0)。- 所有控制寄存器都为 32 位。
- 描述符表寄存器
- 限长:长度减 1,地址偏移量或索引的合法区间。
GDTR:全局描述符表寄存器,表示全局描述符表(GDT)的位置,高 32 位记录基址,低 16 位记录内存大小限长。IDTR:中断描述符表寄存器,表示中断描述符表(IDT)的位置,高 32 位记录基址,低 16 位记录内存大小限长。LDTR:局部描述符表寄存器,16 位,保存 GDT 的选择符,对应局部描述符表(LDT)在 GDT 中的表项。
- 任务管理寄存器
TR:16 位,保存 GDT 的选择符,对应 TSS 描述符在 GDT 中的表项。表示当前任务的上下文保存位置。
- 通用寄存器
- 内存管理
- 实模式段式管理
- 实模式下地址空间 20 位,但是 20 位地址无法直接表示,需要用段寄存器保存高 16 位。
- 段的最大长度为 64 KiB(16 位)。
- 地址用
base:offset来指定,被转换为base * 16 + offset。 - 没有任何保护机制。
- 保护模式段式管理
- 段选择符
- 段寄存器在保护模式下保存段选择符,不直接保存段基址。
- 段选择符共 16 位,包括:
- 第 15~3 位:表示 GDT 中的索引。
- 第 2 位
TI:即 Table Indicator,表示用于在 GDT(TI为0)或 LDT 中查询。 - 第 1~0 位
RPL:即 Requested Privilege Level。
- 地址用
selector:offset来指定,真正的段基址需要通过查询获得。
- 段描述符
- 段描述符共 64 位,包括基址、限长、其他信息位。
- 基址 32 位,限长 16 位。
- 按照从上往下地址增加的顺序表示段描述符结构(内存上的真实结构,无需小端序转换):

- 第 5 字节第 7 位
P:即 Present,表示此段是否在内存中,与虚拟内存机制相关。 - 第 5 字节第 6~5 位
DPL:描述特权级,即 Descriptor Privilege Level,决定访问此段的权限。 - 第 5 字节第 4 位
S:即 System,粗略划分描述符的类型。S == 0时,描述符为系统描述符或门描述符,这里的其他结构含义可能不再适用。S == 1时,描述符对应为代码段、数据段、堆栈段。
- 第 5 字节第 3~1 位
Type:表示描述符的具体类型信息。S == 1时,第 3 位为E,表示 Executable,第 2~1 位由E决定。E == 0时,Type表示E:ED:W,此段表示非代码段,不可以执行代码。ED:即 Expansion Direction,确定扩展的方向,基本上为0,即向高地址扩展。W:即 Writable,为1时可写。
E == 1时,Type表示E:C:R,此时段表示代码段,可以执行代码,必定只读。C:即 Conforming,一致位。为1时,此段即使是高特权级,也可以被低特权级执行。- 代码段
C == 1时,其他高特权级代码不可以调用此段,低特权级代码调用此段不提升特权级。 R:即 Readable,为1时可读。C == 0时按照R控制权限。
- 第 5 字节第 0 位
A:即 Accessed。 - 第 4 字节第 7 位
G:即 Granularity,表示限长的单位。G == 0时,以字节为单位,有效偏移量最大为长度减1,即等于限长。G == 1时,以页为单位,段的可用页数为限长加1,有效偏移量最大为最后一页的最后地址。
- 第 4 字节第 6 位
D:即 Default Operand Size,D == 1时为 32 位,否则 16 位。 - 段描述符具有专用的缓存,段寄存器不变时,可以加速读取。
- 全局描述符表
- 即 Global Descriptor Table(GDT),主要存放段描述符,也可以存放门描述符。
- GDT 全局只有一个,表基址和限长存放在
GDTR中。 - 从 GDT 访问段内地址:

- 局部描述符表
- 即 Local Descriptor Table(LDT)。LDT 每个任务一个。
- 表基址和限长编码在一个段描述符中,
LDTR存放引用此段描述符的段选择符。 - 查询 LDT 信息:

- 段选择符
- 保护模式页式管理
- 页表描述符
- 页表描述符结构(内存上的真实结构,无需小端序转换):

- 第 0 字节第 6 位
D:即 Dirty,是否向其中写入过内容。 - 第 0 字节第 5 位
A:即 Accessed,是否访问过。 - 第 0 字节第 2 位
U/S:即 User/Supervisor,为0时只有 OS 可以访问。 - 第 0 字节第 1 位
R/W:即 Readonly/Writable,为1时可写。OS 任何时候都可写。 - 第 0 字节第 0 位
P:即 Present,是否存在内存中。 - 第 1 字节第 3~1 位
AVL:即 Available,供 OS 自行决定如何使用。
- 页表结构
- 32 位地址分为 3 部分,第 31~22 位为页目录索引,第 21~12 位为页表索引,剩下为页内偏移。
- 地址转换过程:

- 多级页表中,读写等保护取各级页表的描述符中更严格的一个。
- 页表描述符
- 保护模式段页式管理
- 先段式转换,在页式转换。
- 段式内存管理不再是主要作用,所有进程的使用同样的段,所有段是同样的基址和限长,即平坦模式。
- 进程的地址空间通过页式管理隔离。
- 实模式段式管理
- 任务管理
- 任务环境
- 每个任务有独立的代码段、数据段、4 个堆栈段(对应 4 个特权级)。
- 每个任务都有一个 LDT,构成一个局部地址空间。
- 任务状态段
- 即 Task State Segment,一种 x86 ISA 规定的特殊结构,保存每个任务的上下文。
- TSS 以段的形式存在,前 104 B 为固定格式,剩下部分存放任务的额外信息,包括 IO 许可位图。

- IO 许可位图偏移量从 TSS 起始开始计算。
- TSS 在 GDT 中存有对应的 TSS 描述符,属于系统描述符(
S == 0)。 - TR 存有当前任务的选择符,可以引用到 GDT 中的描述符。
- 直接任务切换
- 若目标任务的 TSS 对应的选择符为
selector,则通过CALL selector:offset实现切换。 offset无实际作用,真正的目标代码位置由目标任务 TSS 保存的 IP 确定。
- 若目标任务的 TSS 对应的选择符为
- 门
- 门描述符
- 属于系统描述符(
S == 0),具体有 4 种类型,通过系统描述符的Type区分。 
- 门包括调用门、任务门、中断门、陷阱门,都是实现了某种调用或跳转。
- 门描述符的段选择符和偏移用于指定跳转的目标。
- 属于系统描述符(
- 调用门
- 若门描述符对应的选择符为
selector,则通过CALL selector:offset可以调用指定的门。 - 通过门调用时,
offset无实际作用,真正的跳转目标在门描述符种。 - 通过调用门可以暂时提升特权级执行目标代码,返回后特权级还是低的。
- 调用的参数需要复制到目标堆栈,参数个数记录在门描述符中,参数占用大小为个数乘字长。

- 若门描述符对应的选择符为
- 任务门
- 门描述符中,段选择符部分引用某个 TSS 段描述符,偏移不使用(与直接任务切换相同)。
- 通过
CALL或JMP,实现间接任务切换(与调用门类似)。 
- 中断门
- 门描述符指向中断处理程序,位于 IDT。
- 陷阱门
- 门描述符指向故障/陷阱等异常处理程序,位于 IDT。
- 门描述符
- 任务环境
- 保护机制
- 特权级
- Current Privilege Level:当前正在执行代码的特权级,当前
CS中的最低 2 位。 - Descriptor Privilege Level:描述符指向的代码或数据需要的最低特权级,包括在描述符中。
- Requested Privilege Level:转换的特权级,可以用于降低权限,在段选择符最低 2 位。
- Current Privilege Level:当前正在执行代码的特权级,当前
- 数据保护
- 段访问操作:
- 检查段选择符是否符合描述符表的限长。
- 检查段描述符的
P,确认段是否存在。 - 按照段描述符的
R、W等控制是否可读、可写。 - 特权级检查,
DPL >= max(CPL, RPL)。
- 段寄存器操作:
- 对段描述符的
DPL进行检查,DPL >= max(CPL, RPL)。 - 装入 CS 时,检查对应的段描述符是否
E == 1。 - 装入 DS 等段时,检查对应的段描述符是否
E == 0。 - 装入 SS 时,额外检查
RPL == CPL。
- 对段描述符的
- 段访问操作:
- 程序保护
- 段内直接转移:特权级不改变,只检查限长、描述符是否存在。
- 段间直接转移:
- 目标段
C == 0时,额外检查DPL == CPL,不改变特权级,RPL无用。 - 目标段
C == 1时,额外检查DPL >= max(CPL, RPL),不改变特权级。
- 目标段
- 段间间接
CALL转移:通过门实现。- 目标段
C == 0时,额外检查DPL_gate >= max(CPL, RPL)、DPL_code <= CPL,改变特权级为DPL_code。 - 目标段
C == 1时,额外检查DPL_gate >= max(CPL, RPL)、DPL_code <= CPL,不改变特权级。
- 目标段
- 段间间接
JMP转移:通过门实现。- 目标段
C == 0时,额外检查DPL_gate >= max(CPL, RPL)、DPL_code == CPL,不改变特权级,相比CALL不允许提权。 - 目标段
C == 1时,额外检查DPL_gate >= max(CPL, RPL)、DPL_code <= CPL,不改变特权级。
- 目标段
- 按
C分类:C == 0:特权级为目标段的权限,但实际上只有段间间接CALL转移允许有更高的目标段权限,其他均不变。C == 1:特权级保持不变。
- 只有通过段间间接
CALL转移调用C == 0段的代码,才可以提权。 - 所有的间接转移都不允许执行
DPL_code特权级更低的代码,JMP也不允许更高权限。- 防止权限泄露,执行不可信代码。
- IO 保护
- 当
IOPL >= CPL时,任务允许进行任何 IO。 - 每个任务包含一个 IO 许可位图,最长 8 KiB,对应 65536 个端口。
- IO 许可位图的第
i位为1时,允许任务对端口i读写,不受 IOPL 限制。 - 如果 IO 指令同时访问多个端口,则只有所有端口都有权限时,指令才能成功执行。
- TSS 中 IO 许可位图的偏移量必须大于等于 104 B(前面是上下文的固定区域),小于 56 KiB(段最长 64 KiB)。
- 当
- 特权级