在开机的一瞬间,也就是上电的一瞬间,CPU 的 CS:IP 寄存器被硬件强制初始化为 0xF000:0xFFF0。
CS:IP 就是 PC 指针,也就是 CPU 下一条要执行的指令的地址。
我们可以使用 qemu 模拟器进行验证:
$ qemu-system-i386 -S -s
输入 info registers,就可以看到 CPU 上电时寄存器状态:
由于开机时处于实模式,段基址(CS)要左移 4 位,于是 0xF000:0xFFF0 的等效地址是 0xFFFF0。此地址便是 BIOS 的入口地址。
不过,0xffff0 到地址最高处 0xfffff 只有 16 字节的空间,BIOS 又要检测硬件,做各种初始化工作,还要建立中断向量表,显然 16 字节干不了这么多活。这说明 BIOS 真正的代码不在这,那此处的代码只能是个跳转指令。上表也有介绍,将会跳转到 f000:e05b。
接下来 BIOS 便马不停蹄地检测内存、显卡等外设信息,当检测通过,并初始化好硬件后,开始在内存中 0x000~0x3FF 处建立中断向量表 IVT 并填写中断例程。
接着 BIOS 检查第一块硬盘的 0 盘 0 道 1 扇区,如果该扇区的最后两个字节是 0x55 和 0xAA,就说明这个扇区是 MBR,可以从这里引导。BIOS 便将这 512 字节从硬盘拷贝到内存的 0x7c00 处,并跳转到 0x7c00,控制权便交给了 MBR。
计算机执行到这份上,BIOS 也即将完成自己的历史使命了。
一直到这里,我们程序员都无法干预,从这里开始,程序员才能插手。
如下图,虚线左侧部分属于硬件部分(BIOS 固件也理解为硬件),我们无法干预。从虚线右侧开始,我们才能干预。
我们从 MBR 处开始接手 CPU 控制权。我们写的第一个程序就是 MBR(我将其称之为 boot)。
由于 MBR 比较小,只有 512 字节,能完成的任务有限。所以,在 MBR 代码里我们将 loader 加载到内存,然后跳转过去。
loader 最终将 kernel 加载到内存,然后跳转过去。
所以,接下来我们就一步一步编写 MBR、loader 和 kernel。