前提
本文针对x86架构的处理器。在正式开始之前,首先需要明确一些基本概念。
cs寄存器:代码段寄存器,这个寄存器其实是告诉CPU在这个位置是代码还是数据的。在实模式下,CPU的寻址方式是代码段寄存器左移4位然后加上ip寄存器,作为地址去取内容,写作cs:ip。
reset vector:就是CPU执行的第一条指令的位置
地址构成
其实随着x86的发展,第一条指令的地址并不是一成不变的
8086:CPU reset之后cs寄存器的值是0xFFFF,ip寄存器的值是0x0,所以形成的物理地址是0xFFFF0,这个地址就是1M往下16字节的位置
80286:CPU reset之后cs寄存器的值是0xF000,ip寄存器的值是0xFFF0,所以形成的物理地址也是0xFFFF0
80386:到了386时代一切都变了,此时在CPU reset之后cs寄存器的值仍然是0xF000,但是cs除了段选择子之外还有一个隐藏的基址寄存器,这个寄存器的值是0xFFFF0000,ip寄存器的值仍然是0xFFF0。此时的地址不在是0xF000左移4位加上0xFFF0了,而是0xFFFF0000 + 0xFFF0 = 0xFFFFFFF0。所以第一条指令是在一个很高的地址,是4G往下16字节的位置。
地址布局与映射
经过地址构成这一说,可能大家会有一些疑问。CPU怎么能一上来就去0xFFFFFFF0取指呢?因为此时内存都还没初始化呢,那里什么都没有啊。而且第一条指令毫无疑问是在BIOS里啊,现在用来烧写BIOS的flash芯片有256KB的,1MB的,4MB的,怎么也没有0xFFFFFFF0这个地方吧。
这里就要说一下x86的地址编码方式了。我们平时说的内存,主存都是指的RAM,但这里就有一些问题了,因为RAM是从0开始的一个地址范围,而ROM也是从0开始的一个地址范文,当我们访问一个地址,比如0x100,此时到底是去RAM里找还是ROM里找呢?
x86解决这个问题的方法时统一编码,也可以叫地址映射。也就是说在CPU眼里,它只看到一块大空间,从0开始,32位的话就是4G结束,64位的最大范围就很大了。而这块地址的不同的位置是有不同的作用的,大部分是用来给程序运行的时候当内存的,但还有的其实是映射给了ROM,IO,或者某些设备,这也就是平时我们看到的reserved address了。
现在明白了吧,CPU去0xFFFFFFF0取第一条指令,其实就是去BIOS里取了第一条指令。那么BIOS的地址又是怎么映射到那一整块的地址空间中去的呢?这里我认为不同的CPU其硬件实现是有不同的,仅就我知道的一些情况展开讨论。
在一块1M的flash芯片中,第一条指令一般是在0xFFFF0这个位置,所以我们很简单的想到,其实是把这1M映射到了4G地址中最高的1M。
1M以下地址空间
前文说到BIOS也就是ROM的地址空间被映射到了高地址的1M,但我们平时看到的都是低1M以下空间和BIOS相关。因为在高地址的时候,CPU实际上是在访问ROM,在一段时间以后,CPU会将ROM中的内容复制到RAM中,这个位置就是低1M地址空间。所以当CPU访问低1M地址空间的时候就是真正在访问RAM了,而不是ROM。这样带来两个好处,第一是RAM比ROM更快。第二是这样重新满足了实模式下寻址范围是1M这一特点。CPU会执行一条长跳转,从高地址跳到1M以下地址空间,这会引起cs的改变,然后CPU寻址方式重新回归到cs左移4位 + ip的模式。
但这里有一些疑问:我们一般认为第一条指令就是一条长跳转,那么CPU怎么完成内存初始化并将BIOS相关数据复制到1M以下地址空间呢?这里有几种可能(猜测),第一是第一条指令并不是长跳转,而是先进行了一些基本的初始化,然后才跳转。第二是ROM到地址空间的映射并不是简单得映射到了高地址的1M,而是有更复杂的映射方式,使得长跳转以后实际上CPU还是在访问ROM。第三就是CPU一旦上电就由相关硬件将BIOS内容复制到了对应的RAM中。这里就是我认为不同CPU有不同实现的地方了,只能说具体情况具体分析
小结
本文分析了计算机第一条指令的地址形成,分别介绍了CPU访问的物理地址,以及这第一条指令在1M flash中的位置,介绍了ROM到RAM的地址映射,当然这只是一种可能情况。并介绍了CPU如何从第一条指令开始进行后续的行为。
参考链接
https://en.wikipedia.org/wiki/Reset_vector
http://blog.csdn.net/xuefeng0707/article/details/8526306