[练习4]分析bootloader加载ELF格式的OS的过程
通过阅读bootmain.c,了解bootloader如何加载ELF文件。通过分析源代码和通过qemu来运行并调试bootloader&OS,理解:
- 1.bootloader如何读取硬盘扇区的?
- 2.bootloader是如何加载ELF格式的OS?
首先要介绍一下 对于bootloader访问硬盘时都是LBA模式的PIO方式,也就是说所有的I/O操作都是通过CPU访问硬盘的I/O地址寄存器完成。操作系统位于第一个硬盘上,而访问第一个硬盘的扇区可以设置I/O端口0x1f0~0x1f7来改变地址寄存器实现。下述表格所显示的即为0x1f0~0x1f7所对应的功能:
I/o地址 | 功能 |
---|---|
0x1f0 | 读数据,当0x1f7不为忙状态时,可以读 |
0x1f1 | 可获得详细的错误信息 |
0x1f2 | 与读写的扇区数量,每次读写前,都需要表明要读写几个扇区 |
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端口读数据 |
问题1:bootloader如何读取硬盘扇区
分析原理
阅读材料其实已经给出了读一个扇区的大致流程:
- 1.等待磁盘准备好
- 2.发出读取扇区的命令
- 3.等待磁盘准备好
- 4.把磁盘扇区数据读到指定内存
实际操作中,需要知道怎样与硬盘交互。阅读材料中同样给出了答案:所有的IO操作是通过CPU访问硬盘的IO地址寄存器完成。硬盘共有8个IO地址寄存器,其中第1个存储数据,第8个存储状态和命令,第3个存储要读写的扇区数,第4~7个存储要读写的起始扇区的编号(共28位)。了解这些信息,就不难编程实现啦。
分析代码
bootloader读取扇区的功能是在boot/bootmain.c的readsect函数中实现的,先贴代码:
static void
waitdisk(void) {
//如果0x1F7的最高2位是01,跳出循环
while ((inb(0x1F7) & 0xC0) != 0x40)
/* do nothing */;
}
/* 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); //读取一个扇区
outb(0x1F3, secno & 0xFF); //要读取的扇区编号
outb(0x1F4, (secno >> 8)&0xFF);//用来存放读写柱面的低8位字节
outb(0x1F5, (secno >> 16)&0xFF);