Chapter 4.8 - 保护模式编程初始化
Created by : Mr Dk.
2019 / 08 / 01 20:56
Nanjing, Jiangsu, China
4.8 保护模式编程初始化
当机器上电或硬件复位时,CPU 工作在与 8086 兼容的实地址模式下,从物理地址 0xFFFFFFF0
处开始执行软件初始化 (EPROM 中)。初始化代码首先设置基本系统必要的数据结构:实地址 IDT 表 (中断向量表)。
如果 CPU 将要工作在保护模式下,那么 OS 软件必须加载保护模式操作必要的数据结构信息,然后切换到保护模式。
4.8.1 进入保护模式时的初始化操作
在 CPU 能够被切换到保护模式下运行之前,OS 加载和初始化软件 (bootsect.s
、setup.s
、head.s
) 必须在内存中设置好保护模式下使用的数据结构:
- 保护模式下的中断描述符表 IDT
- 全局描述符表 GDT
- 任务状态段 TSS
- 局部描述符表 LDT
- 若使用分页,则起码需要设置一个页目录和一个页表
- CPU 切换到保护模式下运行的代码段
- 含有中断和异常处理程序的代码块
在切换到保护模式之前,还需要设置以下寄存器:
- GDTR
- IDTR
- CR1、CR3
初始化完毕后,设置 CR0 中的 PE 位,切换到保护模式。
4.8.1.1 保护模式系统结构表
平坦的 / 分段的
为实现无分页的平坦内存模型,初始化代码必须设置具有一个代码段和一个数据段的 GDT 表。GDT 的第一项需要放置一个空描述符。堆栈可以放置在普通的可读写数据段中。若要支持分页,还需要至少一个页目录和一个页表。多段模型还需要用于 OS 的其它段,以及用于每个应用程序的段和 LDT 表段。LDT 表的段描述符要求放在 GDT 中。
4.8.1.2 保护模式异常和中断初始化
软件初始化必须设置一个保护模式 IDT,最少需要含有 CPU 可能产生的每个异常向量对应的门描述符,使用 IDT 之前,需要将基地址加载到 IDTR 中。
4.8.1.3 分页机制初始化
软件必须在物理内存中建立至少一个页目录和一个页表。页目录表的物理基地址加载到 CR3 寄存器中。PG 和 PE 可以同时设置。设置 PG 标志的指令随后应当立刻跟随一条 JMP 指令 - 清空 CPU 已经取得或译码的指令。设置 PG 标志到跳转指令 JMP 之间的代码必须来自对等映射的一个页面,即跳转之前的线性地址与开启分页后的物理地址相同。
4.8.1.4 多任务初始化
软件初始化必须至少设置一个 TSS 以及相应的 TSS 段描述符,TSS 段描述符存放在 GDT 中。在 CPU 切换到保护模式之后,使用 LTR 指令将 TSS 段描述符的选择符加载到 TR 中。软件进行第一次任务切换之前必须首先加载 TSS 段选择符。
4.8.2 模式切换
从实地址模式进入保护模式。进入保护模式后,一般不会再需要回到实地址模式。进入虚拟-8086 模式比退回实地址更方便。
4.8.2.1 切换到保护模式
- 关中断 - 禁止可屏蔽中断、禁止不可屏蔽中断;软件应保证模式切换操作期间不产生异常和中断
- 将 GDT 基地址加载进 GDTR
- 执行
MOV CR0
设置 CR0 中的 PE 标志 - 立刻执行一个远跳转 JMP 或远调用 CALL
- 将 LDT 段选择符加载到 LDTR 中
- 用初始化保护模式任务的段选择符或可写内存区域的段选择符加载到 TR;切换后该区域将用于保存 TSS
- 重新加载所有段寄存器
- 将保护模式 IDT 表加载到 IDTR 中
- 开启可屏蔽中断,开启 NMI 中断
MOV CR0
到 JMP
或 CALL
指令之间的代码必须来自对等映射页面。
4.8.2.2 切换回实地址模式
- 关中断
- 如果分页:
- 将程序控制转移到对等映射的线性地址处 (线性地址 = 物理地址)
- 确保 GDT 和 IDT 在对等映射页面上
- 清除 CR0 中的 PG
- CR3 寄存器设置为
0x00
,刷新 TLB 缓冲
- 把程序控制转移到 64KB (
0xFFFF
) 的可读段中 - 重新加载段寄存器
- 使 IDTR 指向实地址范围内的中断向量表
- 清除 CR0 中的 PE,切换到实地址模式
- 跳转到实地址模式程序中 - 刷新 CS 寄存器
- 加载实模式程序使用的段寄存器
- 开中断