码迷,mamicode.com
首页 > 其他好文 > 详细

进入保护模式

时间:2015-02-03 12:38:52      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:

本文为<x86汇编语言:从实模式到保护模式> 第11章笔记

以下图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可以

mov ax, cs  
02.        mov ss, ax  
03.        mov sp, 0x7c00  
04.          
05.        ; 计算GDT所在地址的逻辑段地址  
06.        mov ax, [cs:gdt_base + 0x7c00]  
07.        mov dx, [cs:gdt_base + 2 +0x7c00]  
08.        mov bx, 0x10  
09.        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  

 

进入保护模式

标签:

原文地址:http://www.cnblogs.com/Acg-Check/p/4269462.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!