* 把core.img第一个扇区从内存0x70000位置拷贝到0x8000位置 */
==================================================== 学习: 0x7000:[0x0000]表示什么意思
====================================================
物理地址就是地址总线上提供的20位地址信息,8086 CPU 能提供20位的地址信息,可直接对1M个存储单元进行访问,而CPU内部可用来提供地址信息的寄存器都是16位。 那怎样用16位寄存器来实现20位地址寻址呢。答案就是使用了下面的:
段地址:[段内偏移地址]
那么我们就使用之前说20位的地址信息可以对1M个内存单元进行访问,就是说编址00000H~FFFFFH,而段寄器CS,DS,SS,ES即存放了这些地址的高4位,如比如我们的上面所说的地址0x70000:
ds便会存储0x7000高16bit信息,这即为段地址。 si存储段内偏移0x0000低16bit信息。
知道了段地址与段内偏移,那么我们可以计算实际地址: 物 理地址=段地址*10H+段内偏移地址。
段地址乘以10H是因为段地址当时是取高四位得到的,所以还原后要让段地址左移4位(10H = 10000B),例如(ds)= 0x7000,(si)= 0x0000,则物理地址为0x7000*0x10+0x0000 = 0x70000。
==================================================== 为什么要分两步拷贝
====================================================
也许有人会问,为什么在读硬盘的时候不直接读到0x8000位置,要先读到0x70000位置,然后再从0x70000拷贝到0x8000位置? 这个问题读者可以自行思考一下,请参考:
http://stackoverflow.com/questions/21339410/why-doesnt-grub2-load-the-diskboot-img-to-address-0x8000-directly
有时候会觉得答案很吃惊噢
7.2.6. Step6 执行core.img第一条语句
/* 跳转下一步,准备执行core.img第一个语句:也就是diskboot.img第一个 内存0x7c5a处存储的值为 00 80, 也就是地址0x8000*/
00007D61 FF265A7C jmp word [0x7c5a]
7.2.7. Step7 boot.img总结
boot.img的功能是是将硬盘LBA-1(0柱面,0磁道,2扇区)的512字节搬移到0x8000处。 这部分代码就是core.img的第一个扇区,一般是GRUB2的diskboot.img
有关boot.img其它的代码,在这里不再详解,大伙可以自行研究一下。
7.3. GRUB2中core.img
下面进入GRUB2最主要的部分,也就是core.img, 当然core.img我们了解到包含了diskboot.img, kernel.img以及必须的*.mod. 下面我们先来看看diskboot.img
7.3.1. diskboot.img
diskboot一般是core.img的第一个代码,位于第一个扇区,当如如果启动的不是硬盘,那么core.img第一段代码可能是cdboot.img(如果你重光盘启动)。本文我们主要介绍diskboot.img
整个diskboot的作用很简单,是从LBA-2(0柱面 0磁道 3扇区) 开始拷贝若干个扇区到内存。起始扇区号,扇区个数 内存目的地址都在diskboot中定义。
==================================================== 三个diskboot
====================================================
diskboot是被整合到core.img,所有在/boot/grub2/i386-pc/目录下并没有diskboot.img,但是你可以去/usr/lib/grub/i386-pc目录下有diskboot.img,我们可以作一个对比:
1) /usr/lib/grub/i386-pc/diskboot.img
2) /boot/grub2/i386-pc/core.img 最开始512byte 3) 硬盘LBA-1的512byte (lba1.bin)
下面我们导出硬盘LBA-1的512byte,并hexdump
[root@controller image]# dd if=/dev/sda of=lba1.bin bs=512 skip=1 count=1
上述命令跳过LBA-0导出LBA-1的512字节
[root@controller image]# hexdump -Cv lba1.bin >lba1.hex
Hexdump diskboot.img
[root@controller image]# cd /usr/lib/grub/i386-pc
[root@controller i386-pc]# hexdump -Cv diskboot.img > /home/lanzhou/image/diskboot.hex
Hexdump core.img
[root@controller i386-pc]# cd /boot/grub2/i386-pc
[root@controller i386-pc]# hexdump -Cv core.img > /home/lanzhou/image/core.hex
比较上面三个
除了0x01fc这个Byte不一样,其余完全一样
[注意,0x01fc这个地方是被grub2-install所修改,指示core.img剩余还有多少扇区]
====================================================
反汇编
====================================================
diskboot.img是core.img第一个扇区代码,在被boot.img加载到0x8000位置并被执行,下面开始讲解diskboot.img 同样进行反汇编
[root@controller image]# ndisasm -o 0x8000 lba1.bin > lba1.asm
这里以附件形式给出所有代码(lba1.asm是反编译后的,diskboot.S是官方源码)
lba1.asmdiskboot.S
7.3.1.1. Step1 初始化
/*
* 我们在这里继续使用boot.img的stack
* 并且认为一些寄存器已经在boot.img中设定了正确的值 */
/* save drive reference first thing! */
00008000 52 push dx
/* this sets up for the first run through \
00008001 BFF481 mov di,0x81f4
/* di为0x81f4,那个位置的值为0x02,那么把该值拷贝到ebp * ebp中现在存储的是core.img的第二个扇区的LBA地址
* 注意,core.img的第一个扇区就是本代码diskboot.img */
00008004 668B2D mov ebp,[di]
/* 内存[di+8] 0x81fc存储的是有core.img剩下有多少扇区将要被读入 * 内存0x81fc的值为0x0067,表明core.img后面还有67个扇区需要被载入 * 代码在这里判断一下是否为0,如果为0表示不需要继续载入core.img * 直接跳转到到0x80f1处Step4-bootit) 不用加载硬盘*/
00008007 837D0800 cmp word [di+0x8],byte +0x0 0000800B 0F84E200 jz word 0x80f1
7.3.1.2. Step2) 从硬盘读取core.img第二个扇区开始的若干扇区
/* 首先检查硬盘是LBA模式还是CHS模式*/
0000800F 807CFF00 cmp byte [si-0x1],0x0
/* 如果是CHS模式,直接挑转 0x805b,这里不在介绍CHS模式*/
00008013 7446 jz 0x805b
/* 转载硬盘起始地址,共64bit, [di]内存为0x81f4.其值为2,硬盘地址为LBA-2 */
00008015 668B1D mov ebx,[di] 00008018 668B4D04 mov ecx,[di+0x4]
/* LBA读取最大扇区数为0x7f,这是因为Phoenix EDD限制 * 这里我没有去查什么是Phoenix EDD*/
0000801C 6631C0 xor eax,eax 0000801F B07F mov al,0x7f
/* 检查下我们需要拷贝读取的硬盘扇区数是否超过0x7f * 也就是BIOS INT13 ah=0x42一次组多读取0x7F扇区 * 但是GRUB不会超过这个数,当前我用来讲解的版本是67 */
00008021 394508 cmp [di+0x8],ax 00008024 7F03 jg 0x8029
/* 如果没有超过了,我们就把总扇区数保存起来,AX存储当前读写的扇区数*/
00008026 8B4508 mov ax,[di+0x8]
/* [di+0x8] 存储总的扇区数 * AX存储当前这次需要读取扇区数 *
* 运行该条指令后, [di+0x8] 存储剩余的扇区数,还需要继续读取的 * * 一般来说只需要读取一次,这条指令后,[di+0x8] 将存储为 0*/
00008029 294508 sub [di+0x8],ax
/* [di]为内存0x81f4,该地存储已经读取的扇区数, 也就是下次需要读取的起始地址 */
0000802C 660105 add [di],eax
0000802F 6683550400 adc dword [di+0x4],byte +0x0