进入保护模式

来源:互联网 发布:全国地区信息 数据库 编辑:程序博客网 时间:2024/05/19 02:25

以下图2, 图4和图5截自Intel手册


全局描述符表

  • 全局描述符表中存放着段描述符, 每个段描述符8个字节.
  • 为了跟踪全局描述符表, 处理器内部有一个48位寄存器, 叫做全局描述符表寄存器(GDTR), GDTR分为两部分,分别为32位的线性地址和16的边界, 32位线性基地址部分保存的是全局描述符表在内存中的起始线性地址, 16位边界部分是全局描述符表的边界(界限), 其值等于表的大小(总字节数) - 1, 因为从偏移0开始. 如果界限值是0, 表示表的大小是1字节.
  • 因为GDT的界限是16位的, 故该表最大是2 ^ 16字节, 也就是65536字节(64KB), 又因为一个描述符8字节, 所以该表最多定义8192个描述符.在进入保护模式之后, 处理器立即要按新的内存访问模式工作, 所以, 必须在进入保护模式之前定义GDT, 但是, 由于实模式下只能访问1MB的内存, 故GDT通常多定义在1MB以下的内存范围中, 当然, 允许在进入保护模式后换个位置重新定义GDT


存储器段描述符

每个描述符占8字节, 下图中, 上面位高32位, 下面为低32位


  • 段描述符中, 指定了32位的段起始地址和20位的段边界, 在实模式下, 段地址并非真实的物理地址, 计算物理地址时需要左移4位. 和实模式不同, 32位保护模式下, 段地址是32位的,如果未开启分页功能, 该线性地址就是物理地址. 段基地址可以是0~4GB范围内的任意地址, 段界限用来限制段的扩展范围, 因为访问内存的方法是用段基地址加上偏移量, 所以, 对于向上扩展的段, 如代码段和数据段来说, 偏移量从0开始递增, 段界限决定了偏移量的最大值; 对于向下扩展的段,如栈段来说, 段界限决定了偏移量的最小值.
  • G位是粒度位, 用于解释段界限的含义, G = 0时, 段界限以字节为单位, 此时段的扩展范围是从1字节到1MB, 因为段界限是20位的, 当G = 1时, 段界限以4KB位单位, 这样, 段的扩展范围是从4KB到4GB.
  • S位用于指定描述符的类型, S = 0时, 表示是一个系统段, S = 1时, 表示是一个代码段或数据段(栈也属于特殊数据段)
  • DPL表示描述符的特权级, 特权级分别是0, 1, 2, 3, 其中0是最高特权级, 3是最低特权级, 刚进保护模式时执行的代码具有0特权级(可以看成是从处理器那里继承来的), 在这里, 描述符的特权级用于指定要访问该段做必须具有的特权级, 如果这里的数值是2, 那么, 只有特权级0, 1和2的程序能够访问该段, 特权级3访问时, 处理器会予以阻止.
  • P是段存在位, P位用于指示描述符所对应的段是否存在, 一般来说, 描述符所指示的段都位于内存中. 但是, 当内存紧张时, 有可能只是建立了描述符, 对应的内存空间并不存在, 这时, 就应当把描述符P位清0, 表示段并不存在. 另外, 同样是在内存紧张的情况下, 会把很少用到的段换出到硬盘, 腾出空间给急需内存的程序使用(当前正在执行的), 这时, 同样要把P位清0, 当再次轮到它执行时, 再装入内存, 然后将P位置1. P位是由处理器负责检查的, 每当通过描述符访问内存中的段时, 如果P位是0, 处理器就产生一个中断, 通常该中断处理程序是由操作系统提供, 该处理过程的任务是负责将该段从硬盘换回内存, 并将P位置1, 在多用户, 多任务的系统中, 这时一种常用的虚拟内存调度策略.
  • D/B位是默认的操作数大小或默认的栈指针大小, 该标志对不同的段有不同的效果, 对于代码段, 此位称作D位, 用于指示指令中默认的偏移地址和操作数大小, D = 0表示是16位的, D = 1, 指示是32位的.  如果D = 0, 处理器在这个段执行时, 将使用16位指令指针寄存器IP, 否者使用32位的EIP.    对于栈段来说, 该位就做B位, 用于在进行隐式的栈操作时, 是使用SP还是使用ESP, 隐式的栈指令包括 push, pop和call等, 如果该位位0, 则使用SP, 栈段的上部边界位0xffff,  否者使用ESP, 上部边界为0xffffffff.
  • L位是64位代码位, 保留此位给64位处理器使用
  • TYPE共4位, 指示描述符的子类型, 或者说是类别, 对数据段来说, 分别是X, E, W, A位, 对代码段来说是X, C, R, A位                                          上表中, A位是已访问位, 每当该段被访问时, 处理器自动将该位置1, 对该位清0是由软件(操作系统)负责的, 通过定期监视该位的状态, 可以统计出该段的使用频率. X代表执行, E位指示扩展方向, W位指示可写, C位指示段是否为特权级依从的, C = 0表示非依从的代码段, 这样的代码段只能供同特权级的程序使用, 或者通过门调用, C = 1则表示这样的代码段为依从代码段, 可以从特权级比它低的程序调用并进入. 代码段总是可以执行的, 但是为了防止程序被破坏, 它是不能写入的. 它的R位不是用来限制处理器是否可读的, 而是用来限制程序的, 一个典型的例子是使用段超越前缀CS:来访问代码段中的内存.
  • AVL位是软件可以使用的位, 通常由操作系统来使用他, 处理器不使用它.

段选择子

  • 3~15是段描述符在描述符中的索引, TI = 0时表示描述符在GDT中, TI = 1时, 描述符在LDT中, RPL是请求特权级, 表示给出当前选择子的那个程序的特权级, 正是该程序要求访问这个内存段.

段描述符高速缓存器

  • 如上图所示, 在32位处理器上运行时, 每个段寄存器还包括一个不可见的部分, 称为描述符高速缓存器, 用来存放段的线性基地址, 段界限和段属性, 既然不可见, 那就是处理器不希望我们访问它. 事实上, 我们也没有任何办法来访问这个不可见部分, 它是由处理器内部使用的.
  • 当处理器在执行任何改变段选择器(段寄存去CS, DS等)的指令时(比如pop, jmp, call, ret等), 就将指令中提供的索引号(就是选择子中的索引)乘以8(一个描述符8字节)作为偏移地址, 同GDTR中提供的线性基地址相加, 以访问GDT, 如果没有发现什么问题(比如超出了GDT的界限), 就自动将找到的描述符加载到不可见的描述符高速缓存部分. 加载的部分包括段的线性基地址, 段界限和段的访问属性. 此后, 每当有访问内存时, 就不再访问GDT中的描述符, 直接用当前段寄存器描述符高速缓存器提供的线性基地址.

进入保护模式

  • 从0x92端口读取的数据, 第2位为开A20, 第一位 = 1为重启
  • 在保护模式下, 不允许使用mov指令改变段寄存器CS的内容, 比如: mov cs, ax, 企图这样做将导致处理器产生一个无效操作码的异常中断.

下面是一段主引导区代码, 实验环境书中也有说, 我的资源中也有配书资源下载, 里面有说如何配置VirtualBox和Bochs, 这里说一下配置Bochs, 配书资源中关于配置Bochs中的

Disk&Boot --> ATA channel 0 --> First HD/CD on channel 0 --> Type of disk image这个选项是vpc, 我用2.6.2配置时选vpc无法启动, 得选flat才可以, 2.6.0貌似选vpc可以

[plain] view plain copy print?在CODE上查看代码片派生到我的代码片
  1.         mov ax, cs  
  2.         mov ss, ax  
  3.         mov sp, 0x7c00  
  4.           
  5.         ; 计算GDT所在地址的逻辑段地址  
  6.         mov ax, [cs:gdt_base + 0x7c00]  
  7.         mov dx, [cs:gdt_base + 2 +0x7c00]  
  8.         mov bx, 0x10  
  9.         div bx  
  10.         mov ds, ax              ; 商为段地址  
  11.         mov bx, dx              ; 余数为偏移地址  
  12.           
  13.         ; 全局描述符#0, 第一项必须为0  
  14.         mov dword [bx + 0x00], 0x00000000  
  15.         mov dword [bx + 0x04], 0x00000000  
  16.           
  17.         ; 全局描述符#1, 代码段描述符  
  18.         ; 线性基地址: 0x00007c00, 段界限: 0x01ff, 段界限在数值上等于段的长度 - 1, 因此该段的长度是0x200, 即512字节  
  19.         ; 粒度位字节(G = 0), 属于存储器段(S = 1)  
  20.         ; 32位的段(D/B = 1), 位于内存当中(P = 1)  
  21.         ; 段的特权级位0(DPL = 00)  
  22.         ; 只执行(TYPE = 1000)  
  23.         mov dword [bx + 0x08], 0x7c0001ff         
  24.         mov dword [bx + 0x0c], 0x00409800  
  25.           
  26.         ; 全局描述符#2, 数据段描述符  
  27.         ; 线性基地址: 0x000b8000, 段界限: 0xffff  
  28.         ; 粒度位字节(G = 0), 属于存储器段(S = 1)  
  29.         ; 32位的段(D/B = 1), 位于内存当中(P = 1)  
  30.         ; 段的特权级位0(DPL = 00)  
  31.         ; 可读可写, 向上扩展的数据段(TYPE = 0010)  
  32.         mov dword [bx + 0x10], 0x8000ffff  
  33.         mov dword [bx + 0x14], 0x0040920b  
  34.           
  35.         ; 全局描述符#3, 栈段描述符  
  36.         ; 线性基地址: 0x00000000, 段界限: 0x07a00  
  37.         ; 粒度位字节(G = 0), 属于存储器段(S = 1)  
  38.         ; 32位的段(D/B = 1), 位于内存当中(P = 1)  
  39.         ; 段的特权级位0(DPL = 00)  
  40.         ; 可读可写, 向下扩展的数据段, 在这里是栈段(TYPE = 0110)  
  41.         mov dword [bx + 0x18], 0x00007a00  
  42.         mov dword [bx + 0x1c], 0x00409600  
  43.           
  44.         ; 初始化全局描述符表  
  45.         mov word [cs:gdt_size + 0x7c00], 31     ; 全局描述符表界限(总字节数 - 1)  
  46.         lgdt [cs:gdt_size + 0x7c00]             ; 加载全局描述符表  
  47.           
  48.         ; 开A20  
  49.         in al, 0x92  
  50.         or al, 0x02  
  51.         out 0x92, al  
  52.           
  53.         cli                                     ; 保护模式下中断机制尚未建立, 关中断  
  54.           
  55.         ; 打开保护模式, cr0寄存器0位置1  
  56.         mov eax, cr0  
  57.         or eax, 1  
  58.         mov cr0, eax  
  59.           
  60.         ; 进入保护模式, 详情参见本书P199 11.7 清空流水线并串行化处理器  
  61.         ; 注意, 不管你用的是16位远转移, 还是32位远转移  
  62.         ; 因为现在已经处于保护模式下, 处理器都将把第一个参数0x0008视为选择子  
  63.         ; 而不是实模式下的逻辑段地址  
  64.         jmp dword 0x0008:_ProtectMode  
  65.           
  66.         [bits 32]  
  67.           
  68. _ProtectMode:  
  69.         ; 选择子00000000000_10_000B, 索引为2的描述符  
  70.         mov ax, 0x10  
  71.         mov ds, ax  
  72.           
  73.         mov byte [0x00], 'H'  
  74.         mov byte [0x02], 'e'  
  75.         mov byte [0x04], 'l'  
  76.         mov byte [0x06], 'l'  
  77.         mov byte [0x08], '0'  
  78.         mov byte [0x0a], ' '  
  79.         mov byte [0x0c], 'O'  
  80.         mov byte [0x0e], 'n'  
  81.         mov byte [0x10], 'z'  
  82.         mov byte [0x12], '.'  
  83.         mov byte [0x14], '.'  
  84.         mov byte [0x16], '.'  
  85.           
  86.         hlt                   ; 因为关中断, 所以处理器不会被唤醒  
  87.           
  88. gdt_size    dw 0  
  89. gdt_base    dd 0x7e00  
  90.   
  91. times       510 - ($ - $$) db 0  
  92.             dw 0xaa55 
0 0
原创粉丝点击