深入理解 GNU GRUB - 03 diskboot.S 3.1 diskboot.S执行时的环境 & 3.2 diskboot.S代码结构

来源:互联网 发布:java最大公约数怎么求 编辑:程序博客网 时间:2024/05/22 06:37

转载注明出处(cppgp: http://blog.csdn.net/cppgp)

 

diskboot.S位于目录boot/i386/pc/,最终生成diskboot.img。这部分指令被加载到0x8000~0x81FF。diskboot.img加载GRUB内核到0x8200开始的内存位置,并将系统控制权交给GRUB内核。用到的BIOS例程和boot.S中相同。因此本章只描述如下内容:
  1)    diskboot.S执行时的环境
  2)    diskboot.S代码结构
  3)    diskboot.S详细注释
  4)    diskboot.S模拟实现


本章最后也模拟一个diskboot的实现。

 

3.1 diskboot.S执行时的环境
boot.img跳转到bootdisk.img之前,已经配置好堆栈和一些寄存器及参数值。因此diskboot假设如下条件已经是满足的:
  1)    堆栈。SS和SP已配置好,有可用的堆栈。
  2)    寄存器DL。DL中保存正确的引导驱动器。
  3)    寄存器SI。SI中保存DAP地址。
  4)    寄存器DS。设置有正确的数据段DS。
事实上,在跳转到diskboot.img之前,boot.img确实做好了配置。堆栈SS:SP=0x0000:0x2000;DL中包含正确的引导驱动器;SI指向DAP地址,因此-1(%si)确定磁盘读取模式(LBA为1,CHS为0),如果是CHS读取,磁盘CHS参数含有正确值;数据段寄存器DS=0。

 

3.2 diskboot.S代码结构
diskboot.S生成512字节机器码,其中0x0~0x147共328字节是指令,0x148~0x1FF共184字节用来保存数据集,每个数据集占用12字节,可以保存15个数据集。对于每个数据集,其中0~7字节表示起始扇区数,在安装时候指定;8~9字节表示扇区数,10~11字节表示目的段地址,都在生成镜像(grub-mkimage)时确定。必须存在一个扇区数为0的数据集表示数据集读取结束,因此可以存在14个有效数据集。当前只使用了一个。我反解了一个安装后的diskboot.img,显示只用了一个数据集,其值如下:
  起始扇区LBA地址: 0x0000 0000 0000 0002
  扇区数: 0x002e
  目的段地址: 0x0820
可知,grub内核的起始扇区是2扇区,扇区数46(0x2E==46),加载到0x8200起始内存处。这个grub内核的大小为46*512=23KiB。
diskboot.S压栈首先保存驱动器。输出提示信息”loading”,设置第一个数据集的地址,然后进入循环读取数据。
根据boot.img中设置的读取模式(LBA/CHS)和数据集中的起始扇区、扇区数、目的段地址,读取数据并保存到内存中(偏移量为0,因此目的段地址唯一确定内存地址)。每调用一次读中断,都提示一个”.”到终端,对于慢速存储设备,用户可以快速得到反馈,以免在加载数据期间,用户误以为死机而进行强制性关机措施等。
在一个数据集内循环读取时,循环标签是LOCAL(setup_sectors),一个数据集完成后,设置处理上一数据集,并跳转到LOCAL(bootloop)开始处理,如果这个数据集的扇区数为0,则跳转到LOCAL(bootit),否则继续数据集内的循环LOCAL(setup_sectors)。
数据读取完毕后,执行LOCAL(bootit)处代码,这里将输出提示信息”/r/n”,还原DX寄存器(保存引导驱动器),并以CS:IP=0x0000:0x8200跳转执行,这里正是kernel.img所在内存地址。
BIOS读中断过程(LBA/CHS)参考boot.S中的注释,此处不再重复。