/* 参照下表设置
SI在上面7.2.3中被设置为0x7c05,根据下表设置
****************************************************************** 偏移 地址
大小
位数
描述
值 0x10 0x00 0x67
00h 0x7c05 BYTE 8 01h 0x7c06 BYTE 8 02h 0x7c07 WORD 16 04h 0x7c09 DWORD 32 08h 0x7c0d QWORD 64 /* 大小和保留 设置为0x0010
00008034 C7041000 mov word [si],0x10
数据块的大小 (10h or 18h) 保留,必须为0
传输数据块数,传输完成后保存传输的块数 传输时的数据缓存地址
0x7000:[0x0000]
起始绝对扇区号(即起始扇区的LBA号码) 0x01
******************************************************************/
/* 传输的数据块数放ax中,在这个例子中为0x67 */
00008038 894402 mov [si+0x2],ax
/* 硬盘拷贝的起始扇区为LBA-2 ebx, ecx在前面已经设置过*/
0000803B 66895C08 mov [si+0x8],ebx 0000803F 66894C0C mov [si+0xc],ecx
/* 依然拷贝到内存0x7000:[0x0000]处地址*/
00008043 C744060070 mov word [si+0x6],0x7000 00008048 50 push ax /*保存ax=0x67 */ 00008049 C744040000 mov word [si+0x4],0x0
/*
* BIOS call \ * Call with %ah = 0x42 * * * */
0000804E B442 mov ah,0x42 00008050 CD13 int 0x13
%dl = drive number
%ds:%si = segment:offset of disk address packet %al = 0x0 on success; err code on failure
* Return:
/* 如果读取错误,跳转到0x8105进行处理,在这里就不做介绍 */
00008052 0F82AF00 jc word 0x8105
/* 准备拷贝buffer */
00008056 BB0070 mov bx,0x7000 00008059 EB66 jmp short 0x80c1
/* 注意,也许一次没有从硬盘读取完,还需要继续读取,但是下次读取硬盘必须等待buffer 拷贝结束*/
7.3.1.3. Step3) 拷贝缓冲区
/* 注意,从硬盘读取的数据放在0x70000位置, 我们需要拷贝到一个区域*/ /* load addresses for copy from disk buffer to destination * [di+0xa]=0x81f4+0xa=0x81fe初始化内存存放为 20 08 * es 寄存器第一次值为0x0820 /
000080C1 8E450A mov es,[di+0xa]
/* 恢复 %ax =0x67*/
000080C4 58 pop ax
/* 计算下一个可能目标地址 (presuming512 byte sectors!) */
000080C5 C1E005 shl ax,byte 0x5/* shift %ax five bits to the left 0x67 */ 000080C8 01450A add [di+0xa],ax /* 更新[di+0xa]位0x8200 */
/* 保存地址寄存器 */
000080CB 60 pushaw 000080CC 1E push ds
/* 计算拷贝的长度 */
000080CD C1E003 shl ax,byte 0x3 /* ax X 8*/ 000080D0 89C1 mov cx,ax
000080D2 31FF xor di,di /* 目的地址置0 */ 000080D4 31F6 xor si,si /* 源地址置0 */
000080D6 8EDB mov ds,bx /* 恢复原段地址: 0x7000 */
/* 开始拷贝从 ds:[si] 0x7000:[0x0000])处拷贝到es:[di]0x0820:[0x0000]处 起始物力地址为0x70000,目的物理地址为0x8200
注意: es=0x0820为段地址, 转化为物理地址需要左移4bit*/
000080D8 FC cld 000080D9 F3A5 rep movsw
/* restore addressing regs and print a dot with correct DS
(MSG modifies SI, which is saved, and unused AX and BX) */
000080DB 1F pop ds
000080DC BE1B81 mov si,0x811b /*打印message*/ 000080DF E85700 call word 0x8139 000080E2 61 popaw
/* 检查是否还有剩余的硬盘扇区需要读写不,如果需要,返回Step2)继续读取 * step2) 的运行地址为0x800f */
000080E3 837D0800 cmp word [di+0x8],byte +0x0
000080E7 0F8524FF jnz word 0x800f
/* update position to load from 并跳转到bootloop
* 在step1) 初始化中我们继续判断 是否还需要读取剩余的扇区,如果没有那么进入下一步/
000080EB 83EF0C sub di,byte +0xc 000080EE E916FF jmp word 0x8007
7.3.1.4. Step4) Bootit: core.img
000080F1 BE1D81 mov si,0x811d /*打印message*/ 000080F4 E84200 call word 0x8139
/*跳转到 8200并开始执行, 8200地址为core.img的运行地址*/
000080F7 5A pop dx
000080F8 EA00820000 jmp word 0x0:0x8200
7.3.1.5. 总结
diskboot.img把LBA2到LBA67的core.img全部读入到内存0x8200位置, 这部分LBA2-LBA67内容分为两个内容:
1) 开始部分是start_raw.S的代码
2) 后一部分是压缩的GRUB2核心代码 后面进入GRUB2 startup部分
7.3.2. GRUB2 core Startup
在diskboot.img完成任务后,后面的工作又core.img中的Startup接手,startup分为两个部分:
Startup_raw Startup
为了节约时间,这部分代码我们不再进行反汇编讲解,而是直接从官方网站直接取得代码。 大伙可以登录官方网站 http://www.gnu.org/software/grub/ 并可以直接使用git下载代码
git clone git://git.savannah.gnu.org/grub.git
7.3.2.1. Startup_raw
这段代码位置:grub-core/boot/i386/pc/Startup_raw.S
依然为汇编语言,官方代码采用AT&T风格的汇编语言编写
/*
* 这段代码Startup_raw.S必须加载到0x0:0x8200. */
7.3.2.1.1. Step1) 初始化
/*首先需要 设置数据段、堆栈段和扩展段寄存器,以及栈指针。*/
cli
/* we're not safe here! */
/* set up %ds, %ss, and %es */ xorw %ax, %ax movw %ax, %ds movw %ax, %ss movw
%ax, %es
/* set up the real mode/BIOS stack */ movl $GRUB_MEMORY_MACHINE_REAL_STACK, ?p movl
?p, %esp
sti
/* we're safe again */
7.3.2.1.2. Step2) 进入保护模式
/*进行一些准备工作后开始进入保护模式
* real_to_prot 这个函数的代码在 grub-core/kern/i386/realmode .S中 。
/* save the boot drive */
*/
movb %dl, LOCAL(boot_drive)
/* reset disk system (%ah = 0) */ int $0x13
/* transition to protected mode */ calll real_to_prot
7.3.2.1.3. Step3) 解压核心代码
#ifdef ENABLE_LZMA
/* GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR 定义为0x100000. 在memory.h中*/
/* 因为core.img有 32K限制,对core.img进行压缩是非常有必要的 解压后的核心最开始放在0x00100000处,esi寄存器的值就是核心代码的地址 */
movl movl movl
$GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR, íi $decompressor_end, %esi $LOCAL(decompressor_end), %esi
#ifdef __APPLE__ #else #endif
pushl íi movl leal
LOCAL (uncompressed_size), ìx (íi, ìx), ?x
/* Don't remove this push: it's an argument. */ push ìx call
_LzmaDecodeA
pop ìx
/* _LzmaDecodeA clears DF, so no need to run cld */ popl
%esi
#endif
7.3.2.1.4. Step3) 跳转核心代码
在上面的指令中:
esi寄存器的值就是核心代码的地址:0x00100000 Edx:启动设备 movl movl movl movl
LOCAL(boot_dev), íx $prot_to_real, íi $real_to_prot, ìx $LOCAL(realidt), êx
jmp *%esi