x86 架构微处理器

  • 微处理器基本结构
    • 重要结构
      • 总线接口单元(BIU):包括数据、控制、指令总线,连接各个组件,用于传递信息。
      • 中央处理单元(CPU):包括指令读取、指令译码、执行单元等。
      • 内存管理单元(MMU):包括分段和分页组件,负责内存管理和保护、地址转换等。
    • 工作模式
      • 实模式:CPU 复位后的默认模式,字长只到 16 位,任何代码都具有完全权限。
      • 保护模式:从实模式提示后的模式,字长到达 32 位,支持高级功能,划分特权级。
      • 虚拟 8086 模式:保护模式下模拟实模式,具有一定兼容性,但权限受限,特权指令需要模拟。
  • 寄存器
    • 通用寄存器
      • EAX:累加器,主要用于算术运算和数据传输。
      • EBX:保存数据,常用于基址寄存器,即作为一段连续内存区域的起始地址。
      • ECX: 保存数据,常用于循环计数器。
      • EDX:保存数据,在使用乘除法指令时保存数据的专用区域。
      • EBP:保存栈帧基址,启用 FPO 后,无特殊职责,可以用于其他用途。
      • EDI:常用于目的操作数索引。
      • ESI:常用于源操作数索引。
      • ESP:固定用于保存栈顶地址。
    • 段寄存器
      • CS:代码段寄存器。
      • DS:数据段寄存器。
      • SS:堆栈段寄存器。
      • ES:附加数据/扩展段寄存器。
      • FSGS:无明显的命名含义,常用于线程局部存储。
      • 所有的段寄存器都是 16 位。
      • 实模式下,段寄存器保存段基址的高 16 位。
      • 保护模式下,段寄存器保存段选择符,间接表示段基址。
    • 标志寄存器
      • CFZFSFOF 参考计算机系统部分的介绍
      • 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 操作的所需最低特权级,取值 03
      • 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 时启用分页内存管理。
      • 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(TI0)或 LDT 中查询。
          • 第 1~0 位 RPL:即 Requested Privilege Level。
        • 地址用 selector:offset 来指定,真正的段基址需要通过查询获得。
      • 段描述符
        • 段描述符共 64 位,包括基址、限长、其他信息位。
        • 基址 32 位,限长 16 位。
        • 按照从上往下地址增加的顺序表示段描述符结构(内存上的真实结构,无需小端序转换):
        • Segment Descriptor
        • 第 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 访问段内地址:
        • Segment Access Using GDT
      • 局部描述符表
        • 即 Local Descriptor Table(LDT)。LDT 每个任务一个。
        • 表基址和限长编码在一个段描述符中,LDTR 存放引用此段描述符的段选择符。
        • 查询 LDT 信息:
        • Lookup LDT
    • 保护模式页式管理
      • 页表描述符
        • 页表描述符结构(内存上的真实结构,无需小端序转换):
        • Page Table Entry
        • 第 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 位为页表索引,剩下为页内偏移。
        • 地址转换过程:
        • Paging Address Translation
        • 多级页表中,读写等保护取各级页表的描述符中更严格的一个。
    • 保护模式段页式管理
      • 先段式转换,在页式转换。
      • 段式内存管理不再是主要作用,所有进程的使用同样的段,所有段是同样的基址和限长,即平坦模式。
      • 进程的地址空间通过页式管理隔离。
  • 任务管理
    • 任务环境
      • 每个任务有独立的代码段、数据段、4 个堆栈段(对应 4 个特权级)。
      • 每个任务都有一个 LDT,构成一个局部地址空间。
    • 任务状态段
      • 即 Task State Segment,一种 x86 ISA 规定的特殊结构,保存每个任务的上下文。
      • TSS 以段的形式存在,前 104 B 为固定格式,剩下部分存放任务的额外信息,包括 IO 许可位图。
      • TSS
      • IO 许可位图偏移量从 TSS 起始开始计算。
      • TSS 在 GDT 中存有对应的 TSS 描述符,属于系统描述符(S == 0)。
      • TR 存有当前任务的选择符,可以引用到 GDT 中的描述符。
    • 直接任务切换
      • 若目标任务的 TSS 对应的选择符为 selector,则通过 CALL selector:offset 实现切换。
      • offset 无实际作用,真正的目标代码位置由目标任务 TSS 保存的 IP 确定。
      • Direct Task Switch
      • 门描述符
        • 属于系统描述符(S == 0),具体有 4 种类型,通过系统描述符的 Type 区分。
        • Gate Descriptor
        • 门包括调用门、任务门、中断门、陷阱门,都是实现了某种调用或跳转。
        • 门描述符的段选择符和偏移用于指定跳转的目标。
      • 调用门
        • 若门描述符对应的选择符为 selector,则通过 CALL selector:offset 可以调用指定的门。
        • 通过门调用时,offset 无实际作用,真正的跳转目标在门描述符种。
        • 通过调用门可以暂时提升特权级执行目标代码,返回后特权级还是低的。
        • 调用的参数需要复制到目标堆栈,参数个数记录在门描述符中,参数占用大小为个数乘字长。
        • Call Gate
      • 任务门
        • 门描述符中,段选择符部分引用某个 TSS 段描述符,偏移不使用(与直接任务切换相同)。
        • 通过 CALLJMP,实现间接任务切换(与调用门类似)。
        • Task Gate
      • 中断门
        • 门描述符指向中断处理程序,位于 IDT。
      • 陷阱门
        • 门描述符指向故障/陷阱等异常处理程序,位于 IDT。
  • 保护机制
    • 特权级
      • Current Privilege Level:当前正在执行代码的特权级,当前 CS 中的最低 2 位。
      • Descriptor Privilege Level:描述符指向的代码或数据需要的最低特权级,包括在描述符中。
      • Requested Privilege Level:转换的特权级,可以用于降低权限,在段选择符最低 2 位。
    • 数据保护
      • 段访问操作:
        • 检查段选择符是否符合描述符表的限长。
        • 检查段描述符的 P,确认段是否存在。
        • 按照段描述符的 RW 等控制是否可读、可写。
        • 特权级检查,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)。