保护模式和实模式之间的切换

492 阅读4分钟

实模式和保护模式是32位CPU的两种工作模式。32位CPU工作在实模式下的时候,并没有变成16位CPU,就像中学生做小学题目的时候并没有变成小学生一样。现代CPU是64位的,工作模式叫做长模式。

从实模式进入保护模式

流程如下:

  1. 创建好GDT。
  2. 创建好GdtPtr,保存GDT的物理地址和GDT中的字节限制(初始值是0,最大值是GDT的长度减1)。
  3. 把GdtPtr放入专用寄存器gdtrlgdt [GdtPtr]
  4. 关闭中断,cli
  5. 打开A20
  6. 打开cr0的PE位。
  7. 跳入保护模式,jmp dword 32位段代码段选择子:偏移量

执行jmp dword 32位段代码段选择子:偏移量时,已经是在保护模式下,但代码是在16位模式下编译的,使用dword使得操作数大于16位时不会被截取为16位。

dword会使jmp dword 32位段代码段选择子:偏移量这条语句在16位代码下加上反转前缀0x66。有了这个前缀,操作数偏移量会被扩展为32位。这是作者的猜测。未经证实。

从实模式进入保护模式的流程,是一个比较固定的模板,除了那句跳转语句,作者也没有其他疑问。没有什么好纠结的。

从保护模式切换到实模式

流程如下:

  1. 先从保护模式下的32位代码进入保护模式下的16位代码。
  2. 在保护模式下的16位代码段设置ds、es、fs、gs、ss的值是normalSelector选择子(一个符合实模式要求的选择子)。
  3. 关闭cr0的pe位。
  4. 跳回实模式下的16位代码段。

两个问题

为什么不能在保护模式下的32位代码段返回实模式下的16位代码段?

为什么在保护模式下的32位代码段返回实模式下的16位代码段必须通过一个中间代码段(保护模式下的16位代码段)?

上面两个问题实际是一个问题。

存储段描述符的寄存器,例如ds、cs、es、fs、ss,存在对应的高速缓冲寄存器,称这批高速缓冲寄存器为R。实模式下,R有特定值。实模式和保护模式下,R的值不相同。从保护模式切换到实模式,必须先修改R中的值,把R的值从保护模式下的值修改成实模式下的值。

从保护模式切换到实模式的临界操作是设置cr0的PE位的值为0,称呼这个操作是O。由于实模式对R的要求,需要在O之前修改R的值,让R的值符合实模式的要求。R中除cs对应的高速缓冲寄存器(称这个寄存器是R-CS)外,都能用mov修改。修改R-CS只能用jmpcalliretf。当前,最可能使用的是jmp。称这个修改操作是C。C的目的是,把R-CS修改为实模式的要求:16位。

分两种情况。

第一种,C在O之前,即C在保护模式下。执行C后,进入保护模式下的16位代码。此时,执行O,是在保护模式下的16位代码,即,在16位代码下切换到实模式。

第二种,C在O之后,即C在实模式下。会报错。因为,R-CS的值仍是保护模式下32位代码的。

对上面两种情况的分析,当然是正确的,可是,这根本不是在回答开头的问题啊。

再论证,为什么在保护模式的32位代码段切换到实模式下的16位代码段必须经过一个中间的保护模式下的16位代码段。

只有两种情况,经过中间代码段,不经过中间代码段。

分析不经过中间代码段这种情况。

在保护模式下的32位代码,执行O操作。此时,R-CS的值仍然是保护模式下的值,不符合实模式的要求,进入实模式失败。

那么,在执行O操作前,先执行C修改R-CS的值,那么,进入了保护模式下的16位代码段。此时,实质仍然是,经过了中间代码段。

证明了”不经过中间代码段“行不通,那么,自然只剩下”经过中间代码段“这种情况了。

开头的两个问题,已经被回答了。