关于从x86实模式到保护模式的关键一跳的指令连续执行问题

问题描述

关于实模式与保护模式的基础可以参考《深入理解linux内核》,或相关博文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.实模式下的寻址方式
cs <<4 + ip
2.保护模式下的寻址方式
base(index(cs)) +eip
3.x86实模式进入保护模式的代码
lgdt GdtPtr //加载 GDTR
cli //关中断
//打开地址线A20等
//下面准备切换到保护模式
mov %cr0,%eax
or 0x1,%eax
mov %eax,%cr0 //将cr0的PE位置1,进入保护模式

jmp dword SelectorSode32:0x0

问题:
当打开cr0的PE位的瞬间,处理器进入保护模式,寻址方式改变。此时cs的值并没有改变,并且打开cr0瞬间处理器对cs的解释方式完全不一样,那么问题来了,如何确保在进入保护模式后下一条指令被顺利执行?

解决过程

你可能会想

  • 1.是不是cs段寄存器的内容足够特别,使得无论此时采用实模式的寻址方式还是保护模式的寻址方式所解释出来的地址相等?然后你会发现其实这个cs目前值没有任何规律,完全不对头。
  • 2.翻一翻x86指令集,发现x86规定,在cpu执行长跳转指令的时候才会自动改变cs寄存器的值。然后你会去找,是不是前后有哪些地方执行过长跳转指令?然后没找到。
  • 3.然后没招的时候你还可能想是不是并不一定就是接着下一条指令执行呢?然后就真的按照当前cs段的值以实模式的方式计算一下下条指令会在那个地方,最后发现根本行不通。

** 图书馆、百度、Google齐上阵后发现**

问题答案

段寄存器后面都有隐藏的高速缓冲寄存器,当cs寄存器值被更新时,这个高速缓冲寄存器的值会根据当时的寻址方式更新,比如实模式下就是cs<<4后放入高速缓冲寄存器,等需要取指令的时候就直接取高速缓冲寄存器的值与eip相加即可,并不会真的再去cs段寄存器找这个值然后做像保护模式下的寻址方式那样推导。这样就解释了上面的情况,虽然寻址方式改变了,但是cs段寄存器的值没变,高速缓冲寄存器的值就不会变,基址仍然是实模式时的值,从而实际上计算出来的还是实模式的地址,保证了指令流的持续执行。内核在等到切换准备就绪的时候就会执行一个长跳转指令来刷新cs段寄存器的值,从而真的跳转导保护模式下了。

 可以参考《x86汇编语言:从实模式到保护模式》,里面也做了相关描述。