本文主要围绕三个问题展开:
1、为什么要有引导程序?
2、什么是引导程序?
3、Linux 0.11引导程序怎么工作?
一、为什么要有引导程序?
解释这个问题以前,先说一点BIOS的工作。当你按下电源键以后,计算机先进行的工作是加电自检,自检完了然后就到了BIOS大展身手了,它负责“点醒”硬件,然后把引导程序BIOS也不知道自己把什么东西加载进内存。这里要注意BIOS加载的位置:把硬盘上的第一个扇区加载进内存的0x7c00位置。
所以第一个问题就来了,我们知道一个扇区只有512字节,假设我们没有引导程序,BIOS把系统加载进内存,那就意味着你的系统代码只有512字节。你没有看错是512字节,这其中还包括了实模式的跳转代码和你写的注释。
如果你说,我geek一点,我写的系统就是只有512字节。那你 本文对你来说没有任何需要阅读的意义。
所以为了突破这个512字节的限制,我们设计出了引导程序。这就引入了第二个问题,什么是引导程序。
二、什么是引导程序
引导程序顾名思义就是负责把系统引导进内存的程序,刚刚已经解释了这个的重要性。现在来看看引导程序到底实际上需要做什么。
第一点也是最重要的一点,把系统load进内存一个特定的位置。其次,刚开机的时候是不是还是在是模式下,如果可能的话我们希望引导也能帮我跳转到保护模式。我希望我的系统第一个模块就是在32位系统下运行的。
捋一下就是主要有两件事:
1、加载系统
2、跳转到保护模式
是的,就是这么简单,我们下来看看具体的实现。
三、Linux 0.11引导程序怎么工作
Linux0.11boot主要分为三个部分,分别在三个程序里面实现,bootsect.s、head.s和setup.s。这个三个程序运行的顺序是:bootsect–>setup–>head。
bootsec:把自己从0x7c00位置移动到0x90000位置,加载setup程序把系统加载在0x10000位置,
setup:把系统搬移到0x00000位置,读取一个磁盘、显示或者内存的参数并保存在合适的位置,然后跳转到保护模式(保护模式跳转的详细分析见博文http://blog.youkuaiyun.com/sium__/article/details/50082285)
1、bootsect.s
这个程序是最先被执行的,根据Linus的解释说,这个程序会被BIOS启动例程(bios-startup routines)加载到内存的0x7c00处(这个地址是BIOS约定好的,每一个系统的引导都会被从硬盘的第一个扇区读入这个位置。这也就一位这,每一个引导都要在第一个扇区包括自己写的)。但是这里请大家注意,这个地址的表示方式是16位的,表示现在还是在实模式下。然后bootsect.s程序会把自己移动到内存的0x90000处,这个地址是20位的地址了。最后jump到0x90000处。程序加载setup程序到内存的0x90200处,并且此时系统在内存的0x10000处。
下面是程序的详细解释:
<此时在实模式下>
把0x7c00写入ds寄存器
把0x90000写入es寄存器
源变址寄存器和目的变址寄存器清零
循环执行当前代码移动到内存的0x90000
jmp到0x90000处代码的go标签处,这里提一点,jmp指令执行完了以后,CPU会自动修改cs寄存器的内容
把cs寄存器的内容写入ds、es寄存器
定义一个栈,空间大小是0xfeff,当前的栈顶地址是0x9ff00
利用中断int 0x13把磁道号0,扇区号2磁头号0、驱动号0的内容读入内存的0x90200处,读四个扇区
**如果读失败**
重置驱动号重试一次
**如果读成功:**
通过BIOS中断int 0x13 获取磁盘参数(包括磁道号、扇区数、驱动器号、和需要读出的磁头号)
保存磁道号和扇区
重新将0x9000写入es
调用中断0x10(功能号:0x03)读取光标的位置和形状
调用中断0x10(功能号:0x13)输出24个字符(loading system...)
把0x1000写入es寄存器
调用度磁盘过程
调用关闭软驱过程
这里说一点题外话,汇编也是有类似于别的语言的函数,我们称之为过程,过程的调用原理类似于c语言的函数调用,调用完了还是要回到过程被调用的地方来。为了使大家对程序也有更清晰的认识,我们按照程序执行的顺序对程序分层,按照层次讲解。因此这两个过程解析见后文。
取出根设备,
如果根设备号不等于0,跳转至root_defined处
取出扇区数如果扇区数是15,则说明是1.2Mb的驱动器;如果扇区数是18,则说明是1.44Mb的软驱。
根据不同的类别保存不同的根设备号
跳转到内存的0x90200处,执行setup程序
到这里程序的主要部分就结束了,在分析read_it过程之前,先来捋一下。我们可以根据以上的分析得出如下结论:
1、bootsect程序执行的主要工作是把自己从内存的0x7c00处移山填海到0x90000处,把setup程序搬移到0x90200处。实际上还有一个很重要的工作,就是把存在磁盘上的系统读入内存的0x10000处
2、根据这个内存的安排我们可以大胆的认为:bootsect程序没有0x0200字节长(请大家注意这一点)。
接下来我们分析过程read_it:
判断es是不是在64KB的边界上,如果不是就死循环
根据刚才读出来的磁盘参数(磁头号、驱动号、磁道号、扇区号)判断是不是已经读完、是不是要换一个磁道和是不是要换一个扇区,然后调用BIOS中断触发读磁盘操作
这里boot程序执行完了,并把控制权交给了setup程序。
2、setup程序
前面已经大致解释了setup程序完成的功能:调用BIOS中断获取一些系统的参数,依次把它们顺序保存在0x90000位置(覆盖了bootsect程序),然后把系统搬移到0x00000位置,最后跳转至保护模式,把控制权交给head程序。语言看起来清晰明朗,这里不在赘述。
值得一提的是,bootsect程序和setup程序都算是引导程序,为什么把它们写了两个程序,这个原因跟为什么要引入引导程序一样,因为512字节写不下。
head是系统的第一个模块,运行在保护模式下。