启动linux kernel过程:
PC/AT机内存使用区域图:
|-----------------------------------------------------| 0xffffffff(=4GB)
| jmp ROM_BIOS_LOCACTION
|-----------------------------------------------------| 0xfffffff0(=4GB-16B)
| ROM BIOS |
|-----------------------------------------------------| 0xfffeffff(=4GB - 64KB)
|
|-----------------------------------------------------| 16MB
|
|-----------------------------------------------------| 0x100000(=1MB)
| ROM BIOS Map |
|-----------------------------------------------------| 0xf0000
| Other BIOS Map |
|-----------------------------------------------------| 0xe0000
| Other Card ROM BIOS |
|-----------------------------------------------------| 0xc7fff
| VGA ROM BIOS |
|-----------------------------------------------------| 0xc0000(=768KB)
| Video buffer |
|-----------------------------------------------------| 0xa0000(=640KB)(=640 * 1024B)
|
|-----------------------------------------------------| 0x00500
| BIOS Data |
|-----------------------------------------------------| 0x00400
| Interrupt Vector |
|-----------------------------------------------------| 0x00000
Note!!!
1) 为保持计算机在软件上的兼容,PC/AT在1MB以下物理内存使用分配上依然与8086兼容,因为
8086只有20根地址线,只能寻址1MB,只是原来系统ROM中BIOS一直处于CPU所能寻址的内存最
高端位置处,而BIOS原来所在位置(指BIOS在8086机器所能寻址的位置)被用作现在机器BIOS的
Shadow区域,即:把BIOS复制到此位置处.
2) 除了0xA0000~0xFFFFF(total=384KB): 用于I/O设备
0xfffe0000~0xffffffff(total=128KB): 用于BIOS
其余部分都可以作为系统内存
1.当计算机系统上电或者按下复位按钮时:
CPU: cs = 0xF000,ip=0xFFF0, 故,段基地址=0xFFFF0000,段长度=64KB,CPU代码指针指向
0xFFFFFFF0,即:4GB空间最后一个64KB的最后一个16字节处,这里存放一条跳转指令,主机将
该跳转指令安排为BIOS的初始化程序的入口地址.
Note!!!(author: bogdan)
Bochs在BIOS程序的第一条指令处中断
[0xfffffff0]f000:fff0 (unk. ctxt): jmp far f000:e05b ; ea5be000f0
查看CS寄存器的值
cs:s=0xf000, dl=0x0000ffff, dh=0xff0093ff, valid=1
查看ip的值:
rip: 0x00000000:0000fff0
intel处理器,实模式下地址的计算为"段地址*16+偏移地址"即是f000*10+fff0=ffff0 即物理地址为[0x000ffff0];那么为什么会是[0xfffffff0]呢?
解释:
这时因为intel 286后,实模式下地址的计算根本就不是"段地址*16+偏移地址"
而是采用跟保护模式一样的地址计算方式,即采用描述符。也就是说,实模式下
段寄存器的缓冲区也是有用的,段寄存器的缓冲区是“不可见的”,它有8个字节。
cs:s=0xf000, dl=0x0000ffff, dh=0xff0093ff, valid=1,其中dl,dh就是它的缓冲寄存器。
段寄存器缓冲区的值就是描述符的值,我们知道,当寄存器的值不变时(valid=1,描述符有效,
这时,段寄存器的值无关紧要,是什么值应该都可以)是从段寄存器的缓冲区中取基地址的,
从dl=0x0000ffff, dh=0xff0093ff(描述符), 可以看出,段界限=0ffff,基地址=ffff0000,
G=1(段界限粒度为字节,段大小限制为64k),d=0,avl=0,P=1(段已在内存中),DPL=0(ring0,特权级为0),
s=1(存储段描述符),type=3(这里为什么不是大于等于8,我就不清楚了)所以物理地址是ffff0000+0000fff0=fffffff0,
即第一条指令的地址为[0xfffffff0]。接下来,实模式下,当段寄存器的地址发生变化的时候,就会把段寄存器的值左移4位,
放入段寄存器的缓冲器中的基地址部分,这应该是实模式转移指令做的事情。这样,在实模式下看起来,地址的计算方式就是"段地址*16+偏移地址"。
intel 为什么要这样做呢?完全是为了向上兼容处理器惹的祸!我们知道实模式下的地址都是物理地址,我们的内存根本就不能达到[0xfffffff0],
那么这个地址在哪里呢?其实,cpu启动的时候,这个地址应该是采取某种机制把它映射到rom中的(bios),在rom中执行到适当的时候,
会rombois拷贝到内存的1m后64k,并采用某种机制切换到内存中执行,以加快启动的速度。
基于这样的地址计算方式,其实在实模式下,完全可以访问到大于1M的空间,
这样做:合适地初始化各个描述符,做好必要的工作,暂时切换到保护模式,注意要开启A20地址线,然后再切换回实模式,
不要关闭a20,这样地址应该就不会回滚了。注意,进入保护模式不要开启分页,或者开启分页后保证线性地址是和物理地址一样的,
为了防止找不到你的指令,回保护模式的时候也不要改变段寄存器的值,因为改变段寄存器的值就改变段寄存器的缓冲寄存器的值了,
这样就访问不到大于1M的内存了,当然,访问不存在的地址可能会出现问题。
2.BIOS初始化过程包含两部分工作: 系统加电自检和系统自举(OS的装入和引导)
1)检测电脑系统中的内存,显卡等关键设备是否能够正常工作.
2)查找显卡的BIOS,然后调用显卡BIOS的初始化代码,用它完成显卡的初始化工作.
3)查找其他设备的BIOS程序,找到之后同样要调用这些BIOS的内部初始化代码来初始化设备。
4)显示BIOS自己的启动代码.
5)检测CPU的类型和工作频率,并将检测结果显示在屏幕上。
6)开始检测系统中安装的一些标准硬件设备(硬盘, CD-ROM等)
7)标准设备检测完成后开始检测一些即插即用设备
Note!!!
上述步骤是在打开电源开关或按Reset按钮进行冷启动时所要完成的各种初始化工作,如果
按Ctrl+Alt+Del键进行热启动时直接从第三步开始,同时会跳过第五步
8)BIOS在执行了一系列硬件检测和初始化工作之后,就会把与原来PC机兼容的64KB BIOS代码和
数据复制到低端1MB末端64KB处,然后跳转到这个地方并且让CPU进入真正的实模式模式工作.
9)加电自检完成后,按照指定的启动顺序从软盘,硬盘和光驱中寻找启动设备。
Note!!
若硬盘是启动设备:即硬盘的第一个扇区(0磁头,0磁道,1扇区),硬盘最开始的512B,其最后两个字节是0x55AA
这是引导扇区的标志,512个字节不能多一个字节也不能少一个字节.
10)找到启动设备后,将启动设备上的512个字节的OS引导程序加载到内存0x7c00处,并跳转到这个地方继续
执行引导操作.
3.到此开始执行Linux的引导程序boot
Path: boot/bootsect.s.
Func: 其被BIOS读入到内存物理地址0x7c00(31KB)处,当它被执行时就会吧自己移动到内存
物理地址0x90000(576KB)处,并把启动设备后2KB(= 4 sector)字节代码(boot/setup.s)读入到内存0x90200出,而
kernel的其他部分(system模块)则被读入到内存地址0x10000(64KB)开始处