浅谈80368之保护模式
Table of Contents
1 保护模式的历史
- Intel早期的8086处理器是16位处理器,数据总线16位,地址总线20位,最高寻址空间可达1MB。由于数据总线 的位数少于地址总线,所以采用了分段的方式来进行寻址。此时的处理器的工作模式只有实模式,即8086模式。
- 软件在执行的过程中直接访问物理地址,没有权限保护而且寻址能力也受到段寄存器大小的制约,(16bit)64K段。
- 到后来80286,数据总线仍然是16位,但地址总线增加到了24位,最大寻址16MB。与8086不同,80286引入了 保护模式。程序不再直接访问物理内存,而是需要经过一系列的权限检查和转换,从而大大提高了系统的安全性。但 由于数据位数仍然是16位,寻址能力仍然受到64K段的限制。
- 直到80386出世,数据总线达到32位,地址总线32位,系统可以毫无压力地访问4GB的地址空间。寻址能力不再是 问题。2000年,AMD又推出了x86-64处理器架构。同时支持32位和64位指令集。
2 保护模式的定义和特点
2.1 什么是保护模式?
- 保护模式是一种80286以及之后的x86兼容cpu的操作模式。
2.2 特点
- 内存保护,分页,系统及硬件的虚拟内存支持
3 实模式与保护模式寻址方式对比
3.1 常用的段寄存器介绍
- 要了解寻址方式,首先得熟悉一下常用的段寄存器
CS (Code Segment) 代码段寄存器 DS (Data Segment) 数据段寄存器 ES (Extra Segment) 附加段寄存器 SS (Stack Segment) 堆栈段寄存器
3.2 实模式下的寻址
- 由于处理器必须要考虑向下兼容,所以今天我们的计算机在启动后大多数都是先工作在实横式下面的。在实模式下, 系统采用16位寻址模式,但由于ALU(逻辑运算单元)最高只有16位,但地址却是20位的。为了方便地进行地址 运算,采用了分段的方式来管理。
此时系统地址的表示方式为: 段地址:偏移地址 当需要计算实际的物理地址的时候,可以采用以下方式: 实际的物理地址 = (段地址 << 4) + 偏移地址
- 从上面的公式可以看到,系统的地址空间被分成了一个个64K大小的段,段的最大数目也是64K。此时段寄存器中 存放的地址是实际的物理地址。
3.3 保护模式下的寻址
- 在保护模式下,段寄存器中不再存放实际的物理地址了。它存放的是一个索引,这人索引实际是全局描述符表(GDT) 中的偏移值。
- 保护模式下的寻址可以用以下的方式: xxxx:yyyyyyyy。 其中xxxx就是索引值(16位),yyyyyyyy(32位)是实际的偏移地址。
+--------------------------+ | Linear Addr (0x00000000) | ~ ~ . ... . |--------------------------| | GDT | | GDT | ~ ... ~ +----------------->| GDT |------------+ | | GDT | | seg:offset | GDT | | | |--------------------------| | | ~ ... ~ | | |--------------------------| | | | |<-----------+ | | Data | +------------>| | ~ ... ~ | Data | ~ ... ~ | Linear Addr (0xFFFFFFF) | |--------------------------|
4 如何开启保护模式
4.1 控制寄存器(CRx)
- CR0: 主要保存processor操作模式和系统状态的标志位
- CR1: Reserved
- CR2: 用于保存触发PG fault的线性地址
- CR3: 页目录地址和两个flag位。(PCD和PWT)
- CR4: 架构扩展用的寄存器位
- CR8: 用于读写访问TPR寄存器(Task Priority Register)
4.2 CR0的各位bit含义
|----+----+----+-------+----+-----+----+------+----+----+----+----+----+----| | 31 | 30 | 29 | 28.19 | 18 | 17 | 16 | 15.6 | 5 | 4 | 3 | 2 | 1 | 0 | |----+----+----+-------+----+-----+----+------+----+----+----+----+----+----| | PG | CD | NW | N/A | AM | N/A | WP | N/A | NE | ET | TS | EM | MP | PE | |----+----+----+-------+----+-----+----+------+----+----+----+----+----+----|
- PG: Paging, 启动分页
- CD: Cache disable, 禁止cache
- NW: Not write through, 一般和CD一起用。
- AM: Alignment Mask. 启动自动Alignment。
- WP: Write Protect 写保护
- NE: Numeric error
- ET: Externsion Type
- TS: Task Switched
- EM: Emunation
- MP: Monitor Coprocessor
- PE: Protection Enable, 启动保护模式
4.3 开启保护模式的代码示例
# Switch from real to protected mode, using a bootstrap GDT # and segment translation that makes virtual addresses # identical to their physical addresses, so that the # effective memory map does not change during the switch. lgdt gdtdesc movl %cr0, %eax orl $CR0_PE_ON, %eax movl %eax, %cr0 # Jump to next instruction, but in 32-bit code segment. # Switches processor into 32-bit mode. ljmp $PROT_MODE_CSEG, $protcseg .code32 # Assemble for 32-bit mode protcseg: # Set up the protected-mode data segment registers movw $PROT_MODE_DSEG, %ax # Our data segment selector movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %fs # -> FS movw %ax, %gs # -> GS movw %ax, %ss # -> SS: Stack Segment
- 以上代码是从JOS中取出的, 首先是lgdt把GDT的地址传给gdtr. 然后enable CR0寄存器中的bit0(PE).
- 当PE被置位后,执行一个long jump, 把保护模式下的段选择子(index)load到CS寄存器中,这样系统才真正地进入保 护模式。
- 细心研究可以发现ljmp所跳转的地址实际上正是系统正要执行的下一条语句。但不同的是此时的寻址方式已经变了 系统切换到了32位模式,寻址空间不再有1MB的限制。CS中存放的也不再是段地址,而是GDT的index。
4.4 物理地址,线性地址和逻辑(虚拟)地址
- 这三个概念应该是在学习x86架构的时候最容易混淆的了。
- 物理地址:系统访问内存的最终地址。IA32架构提供了2^32(4GB)的内存寻址空间
- 线性地址:在未开启分页之前,线性地址就是物理地址。当系统开启分页之后,线性地址通过页表转换可以得到物理地址。
- 逻辑地址:逻辑地址可以表示为 段选择子:32位地址偏移(offset)
逻辑地址与线性地址的转换 +----------------+ +----------------+ | Seg Selector | 16bit:32bit | Offset | +----------------+ +----------------+ | +------------+ | | | GDT/LDT | | +--->| |------------>+<---' ~ ... ~ | | | V | GDT/LDT | Linear Address +------------+
4.5 段选择子 (Segment Selector)
- 在开启保护模式之前,CS, DS等段寄存器中存放的都是段地址。然后当保护模式启动后,段寄存器中存放 的不再是段地址。而是段选择子。它不再可以直接与偏移地址进行移位求和运算。它只代表了descriptor table中的一个index。地址变换过程变成了: 逻辑地址=>线性地址=>物理地址
Segment Selector |----------+----+-----| | 15 ... 3 | 2 | 1 0 | |----------+----+-----| | Index | TI | RPL | |----------+----+-----|
- RPL: 请求特权级 (Request Privilege Level), 这个我们在后面要详细介绍。
- TI: Table indicator, 0-GDT, 1-LDT
- Index: 描述符表的索引值。
- 实例分析:
- 如果你在OS读到当前程序运行在一个这样的逻辑地址, 0x38:12345678。那么,你应该知道
- 这个地址的RPL=0, ring 0
- TI=0, 说明我们应该去查GDT,而不是IDT。关于GDT和IDT的区别以后细说。
- Index = 7。 所以base address应该保存在GDT的第7个选择子中。
- 如果你在OS读到当前程序运行在一个这样的逻辑地址, 0x38:12345678。那么,你应该知道
Date: 2014-07-05 14:01:32
HTML generated by org-mode 6.31a in emacs 23