* 注意,BIOS在把控制权交给MBR前,会在DL中设置正确的Bit位,表明MBR来源 * DL = 00h 1st floppy disk(“drive A:”) * DL = 01h 2nd floppy disk(“drive B:”) * DL = 80h 1st hard disk * DL = 81h 2nd hard disk
* 因为我们是磁盘加载的MBR,所以我们dl里面存的是0x80。 * 下面这段应为来自官方源代码注释
* This is a workaround for buggy BIOSes which don't pass boot * drive correctly. If GRUB is installed into a HDD, check if * DL is masked correctly. If not, assume that the BIOS passed * a bogus value and set DL to 0x80, since this is the only * possible boot drive. If GRUB is installed into a floppy, * this does nothing (only jump).
* 意思是有些BIOS类型并没有正确的设置DL寄存器,我们检测是否DL置了0x80bit位 * 如过不是,我们手工设置为DL为0x80
* 注意test 相当于用and(位与)判断,实际上是测试Bit位是否被置上 */
00007C68 F6C280 test dl,0x80 00007C6B 7405 jz 0x7c72
/* 如果DL不在区间0-0x0f and 0x80-0x8f. 那么需要设置DL为0x80 * 如果DL在区间0x10-0x7f,那么不需要设置DL为0x80 */
00007C6D F6C270 test dl,0x70 00007C70 7402 jz 0x7c74 00007C72 B280 mov dl,0x80 00007C74 EA797C0000 jmp word 0x0:0x7c79
/* ax清零,ds赋值0,ss赋值0 */
00007C79 31C0 xor ax,ax 00007C7B 8ED8 mov ds,ax 00007C7D 8ED0 mov ss,ax
/*设置堆栈:设置了实模式下的堆栈段地址(栈顶位置) 0x2000*/
00007C7F BC0020 mov sp,0x2000
/*恢复中断 再次安全*/
00007C82 FB sti
/* 从内存0x7c64读入AL, 在0x7c64,我们值为0xFF, 比较两个数为相等 * 那么不执行0x7c8a
* 这里只是检查我们是否有forced disk reference */
00007C83 A0647C mov al,[0x7c64] 00007C86 3CFF cmp al,0xff
00007C88 7402 jz 0x7c8c 00007C8A 88C2 mov dl,al
7.2.3. Step3 判断硬盘是否支持LBA还是只支持CHS
/*在前面我们介绍了CHS及LBA, 这两种对硬盘读寻址有很大不同
*在加载后续GRUB2 core.img的前,我们必须判断硬盘是否支持LBA还是只支持CHS*/
/* save drive reference first thing! */
00007C8C 52 push dx
/* set %si to the disk address packet */
00007C8D BE057C mov si,0x7c05
/* 调用BIOS INT13功能,检查硬盘是否支持LBA
* 通过 BIOS 调用 INT 0x13 来确定是否支持扩展,LBA 扩展功能分两个子集 , 如下 : 第一个子集提供了访问大硬盘所必须的功能 , 包括:
**************************************************************** 1.检查扩展是否存在 : ah = 41h , bx = 0x55aa , dl = drive( 0×80 ~ 0xff ) 2.扩展读 : ah = 42h 3.扩展写 : ah = 43h 4.校验扇区 : ah = 44h 5.扩展定位 : ah = 47h 6.取得驱动器参数 : ah = 48h
**************************************************************** 第二个子集提供了对软件控制驱动器锁定和弹出的支持 ,包括:
**************************************************************** 1.检查扩展 : ah = 41h 2.锁定/解锁驱动器 : ah = 45h 3.弹出驱动器 : ah = 46h 4.取得驱动器参数 : ah = 48h 5.取得扩展驱动器改变状态: ah = 49h
****************************************************************
我们采用的是ah=41h,bx=0x55aa,dl=0x80,所以是检查扩展是否存在。这个操作会改变 CF标志位的值。如果支持LBA,那么CF=0,否则CF=1。 */
00007C90 B441 mov ah,0x41 00007C92 BBAA55 mov bx,0x55aa 00007C95 CD13 int 0x13
/*
* %dl may have been clobbered by INT 13, AH=41H. * This happens, for example, with AST BIOS 1.04. */
00007C97 5A pop dx 00007C98 52 push dx
/*
* 下面的三个跳转语句都到0x7cd8位置,那个位置是CHS部分 * 有下面几种情况
* 1) 探查结果 CF=1(0x7c99 jc 0x7cd8),二话不说,跳转到CHS模式
* 2) CF=0是否就采用LBA呢?也不一定,还需要判断bx==0x55aa,如果不等则CHS模式 * 3)有一个FORCE_LBA Byte(INT13返回寄存器cx中),如果这个位是1,那么直接采用LBA MODE */
00007C99 723D jc 0x7cd8 00007C9B 81FB55AA cmp bx,0xaa55 00007C9F 7537 jnz 0x7cd8 00007CA1 83E101 and cx,byte +0x1 00007CA4 7432 jz 0x7cd8
/* 由于现在硬盘都是LBA模式,这里我们就不在对CHS模式介绍了,直接进入LBA*/
7.2.4. Step4 采用LBA加载core.img第一个扇区
/* 对于BIOS LBA读来说,最重要的是设置SI所在内存块的值
由si及其偏移量指向的内存保存着磁盘参数块
SI在上面7.2.3中被设置为0x7c05,根据下面的偏移量来设置 如下:
****************************************************************** 偏移 地址
大小
位数
描述
值 0x10 0x00 0x01
00h 0x7c05 BYTE 8 01h 0x7c06 BYTE 8 02h 0x7c07 WORD 16 04h 0x7c09 DWORD 32 08h 0x7c0d QWORD 64
数据块的大小 (10h or 18h) 保留,必须为0
传输数据块数,传输完成后保存传输的块数 传输时的数据缓存地址
0x7000:[0x0000]
起始绝对扇区号(即起始扇区的LBA号码) 0x01
****************************************************************** 下面的很多代码都是在初始化SI 所指向的内存
注意[si+0x4]表示的是 segment:offset pointer to the memory buffer to which sectors will be transferred (note that x86 is little-endian: if declaring the segment and offset separately, the offset must be declared before the segment) */
/* 下面这个首先把内存中si+0x4的位置,传输数据时的数据缓存地址 * 也就是0x7c09~0x7c0a 16bit置为0 */
00007CA6 31C0 xor ax,ax 00007CA8 894404 mov [si+0x4],ax
/* AX现在值为0x0001*/
00007CAB 40 inc ax
/* set the mode to non-zero 也就是把内存[si-1]0x7c04的值置为1 * (也就是mode被置1,表示LBA扩展读;如果是0,就是CHS寻址读) */
00007CAC 8844FF mov [si-0x1],al
/* 把内存0x7c07-0x7c08 16bit 的值置为1
* 也就是需要传输数据块数,这里我们将要传输core.img中第一个扇区 */
00007CAF 894402 mov [si+0x2],ax
/* the size and the reserved byte * 把内存0x7c05的值置为0x10 (固定) * 把内存0x7c06的值置为0x00 (固定)*/
00007CB2 C7041000 mov word [si],0x10
/* the absolute address 起始绝对扇区号(即起始扇区的LBA-1号码) 0x7c0d ~ 0x7c11 32bit: 内存0x7c5c值为01 00 00 00 0x7c12 ~ 0x7c15 32bit: 内存0x7c60 值为 00 00 00 00
*最后就是制定LBA地址为LBA1,也就是第二个扇区,在前面我么说了这个/
00007CB6 668B1E5C7C mov ebx,[0x7c5c] 00007CBB 66895C08 mov [si+0x8],ebx 00007CBF 668B1E607C mov ebx,[0x7c60] 00007CC4 66895C0C mov [si+0xc],ebx
/* the segment of buffer address 传输时的数据缓存地址 * 也就是0x7c0b~0x7c0c 16bit置为0x7000 * 0x7c09~0x7c0a 16bit置为在前面已经被置为0 * 数据缓存地址为 :0x7000:[0x0000]
* 注意0x7000为段地址,偏移为0x0000,实际物力地址将是0x70000 */
00007CC8 C744060070 mov word [si+0x6],0x7000
/*
* 调用BIOS功能\ * 从硬盘读指定扇区到内存中
* 参数如下 %ah = 0x42 (在上面一节中2.扩展读: ah = 42h) * * * * */
%dl = drive number (在7.2.2中已经被置为 0x80) %ds:%si = segment:offset of disk address packet (DS在7.2.2中已经被初始化为0, SI在上面被置为0x7c05) %al = 0x0 成功; err code on failure
* 返回:
00007CCD B442 mov ah,0x42 00007CCF CD13 int 0x13
/* LBA 扩展读失败,可能不支持LBA,跳转采用CHS方式 */
00007CD1 7205 jc 0x7cd8
/* 进入下一步,准备拷贝刚读入的core.img第一个扇区数据到指定位置 */
00007CD3 BB0070 mov bx,0x7000 00007CD6 EB76 jmp short 0x7d4e
/* 总结,上面这段这么多的汇编代码就是完成一个功能:
调用BIOS INT14 ah=0x42 把硬盘LBA-1的512字节传输到内存0x7000:[0x0000]位置 注意:0x7000:[0x0000]实际上是段地址:[段内偏移]实际地址为 0x70000 */
7.2.5. Step5 拷贝core.img第一个扇区到内存指定位置
/*
* We need to save %cx and %si because the startup code in * kernel uses them without initializing them. */
00007D4E 60 pushaw 00007D4F 1E push ds
/* 循环256次:0x100*/
00007D50 B90001 mov cx,0x100
/* bx 在上面被置为0x7000, 也就是从硬盘拷贝过来的地址*/
00007D53 8EDB mov ds,bx 00007D55 31F6 xor si,si
/* di 是我们目标地址0x8000, 也就是core.img要从这个位置开始执行*/
00007D57 BF0080 mov di,0x8000
/* 初始化es为0*/
00007D5A 8EC6 mov es,si
/* 重复前固定操作,清除方向标志位*/
00007D5C FC cld
/* 开始循环 move string word
ds:[si] (0x7000:[0x0000])移到es:[di](0x0000:[0x8000]) 一次一个word,也就是2 byte,循环256次共512byte*/
00007D5D F3A5 rep movsw
00007D5F 1F pop ds 00007D60 61 popaw
/* 总结,上面这段这么多的汇编代码就是完成一个功能: