分析 bootloader 加载 ELF 格式的 OS 的过程。
通过阅读 bootmain.c,了解 bootloader 如何加载 ELF 文件。通过分析源代码和通过 qemu 来运行并调试 bootloader&OS
- bootloader 如何读取硬盘扇区的?
所有的IO操作是通过CPU访问硬盘的IO地址寄存器完成的。
IO地址 | 功能 |
---|---|
0x1f0 | 读数据,当0x1f7不为忙状态时,可以读。 |
0x1f2 | 要读写的扇区数,每次读写前,你需要表明你要读写几个扇区。最小是1个扇区 |
0x1f3 | 如果是LBA模式,就是LBA参数的0-7位 |
0x1f4 | 如果是LBA模式,就是LBA参数的8-15位 |
0x1f5 | 如果是LBA模式,就是LBA参数的16-23位 |
0x1f6 | 第0~3位:如果是LBA模式就是24-27位 第4位:为0主盘;为1从盘 |
0x1f7 | 状态和命令寄存器。操作时先给命令,再读取,如果不是忙状态就从0x1f0端口读数据 |
读一个扇区的流程(boot/bootmain.c readsect函数):
- 等待磁盘准备好
- 发出读取扇区命令
- 等待磁盘准备好
- 把磁盘扇区数据读到指定内存
- bootloader 是如何加载 ELF 格式的 OS?
ELF文件格式是Linux系统下的一种常用目标文件格式,有三种主要类型:
+ 用于执行的可执行文件(executable file),用于提供程序的进程映像,加载的内存执行。 这也是本实验的 OS 文件类型。
+ 用于连接的可重定位文件(relocatable file),可与其它目标文件一起创建可执行文件和共享目标文件。
+ 共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。
/* readsect - read a single sector at @secno into @dst */
static void
readsect(void *dst, uint32_t secno) {
// wait for disk to be ready
waitdisk();
outb(0x1F2, 1); // count = 1
outb(0x1F3, secno & 0xFF);
outb(0x1F4, (secno >> 8) & 0xFF);
outb(0x1F5, (secno >> 16) & 0xFF);
outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
// wait for disk to be ready
waitdisk();
// read a sector
insl(0x1F0, dst, SECTSIZE / 4);
}
在bootmain中判断读入的扇区的数据是否位ELF文件。读取ELF的header-》判断一个成员变量是都等于一个特定的值,是的话就是一个合法的ELF文件
最后一条决定调到什么地方去,将控制权交给ucore
通过 qemu 来运行并调试 bootloader&OS: