本文首发于我的博客
书接上回,我们继续来看ucore操作系统的启动部分。
上一部分结束时,程序已经从最开始的bootasm跳转到了bootmain函数。
1. 读取磁盘
由于BIOS只会把第一个扇区加载到磁盘上,而我们的操作系统的大小肯定不止512KB,所以要在boot程序中把操作系统其余的部分加载到内存上。
总体来看,这个函数的意思就是将磁盘上从1开始的8个扇区加载到ELFHDR(0x1_0000)的位置上。由于第0个扇区(就是我们现在正在执行的代码)已经被BIOS加载上了,所以从第一个扇区开始加载就行了。
至于为什么是这样组织磁盘的,就要看Makefile里的描述了
这里的意思是,先制作一个大小为1000个block(512Byte)的img文件,然后把 bootblock,就是bootasm + bootmain放在第零个块中,再从第一个块开始,放入kernel
注意在制作bootblock时会用sign.c(tool目录下的)将其填充为510byte并在最后加上55AA使其成为一个合法的主引导扇区
构造完成后各文件大小如上图
再回到读取扇区的代码上来
这段代码就是循环的调用readsect函数,把count个byte的数据读取到va开头的地址上。
readsect函数用于读取一个扇区到指定位置,关于读取扇区的描述,可以看我之前写的博客。这里看反汇编出来的代码会更详细一些。
总而言之,就是与0x1F0到0x1F7这八个端口交互数据。
2.解析ELF
和bootblock不一样,kernel是ELF格式的,所以我们需要先解析处kernel中的一些信息。关于ELF,可以参考
文档
和
《程序员的自我修养:链接,装载与库》一书
上图是ELF的两种视图,这里我们应该按照Execution View分析
我们可以使用readelf命令先分析一下kernel的组成
关键的信息有:
1.小端序,是可执行文件
2.系统架构是 80386
3.程序入口地址是0x10_0000(比加载kernel到内存的位置多了一个0!)
4.有三个程序头
再来看一下程序头表的内容
关键信息
1.text段的地址 0x10_0000,和程序的入口地址对应,这也是我们要跳转的地址。
2.data段的地址为0x11_0000
知道了这些,再来看代码
第一步,验证是否是有效的ELF,即开头是否是MAGIC NUMBER
然后把所有的程序段加载到对应的内存位置。
最后,跳转到程序入口(0x10_0000)执行代码
3.内存视图
1.ELF文件在0x10000处
2.kernel内存位置为0x100000
再综合一下之前的内存位置
4.总结
至此,boot部分就已经完成,下面就是真正的操作系统接手控制权了。作者对于ELF的理解也不是很深入,如果有错误,欢迎大家指出