浅谈80368之保护模式

浅谈80368之保护模式

Table of Contents

  • 1 保护模式的历史
  • 2 保护模式的定义和特点
    • 2.1 什么是保护模式?
    • 2.2 特点
  • 3 实模式与保护模式寻址方式对比
    • 3.1 常用的段寄存器介绍
    • 3.2 实模式下的寻址
    • 3.3 保护模式下的寻址

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。那么,你应该知道
      1. 这个地址的RPL=0, ring 0
      2. TI=0, 说明我们应该去查GDT,而不是IDT。关于GDT和IDT的区别以后细说。
      3. Index = 7。 所以base address应该保存在GDT的第7个选择子中。

Date: 2014-07-05 14:01:32

HTML generated by org-mode 6.31a in emacs 23

你可能感兴趣的:(浅谈80368之保护模式)