在操作系统启动的初期,首先将PC指向磁盘的第一个块。这个区域大小为512字节,被称作MBR。这512字节组织性很好,包含了一些磁盘的基本参数和一些用于启动的代码。当然启动初期和文件系统是毫不相干的,因为文件系统和启动存在一个鸡生蛋和蛋生鸡的问题。举个很简单的例子,系统启动需要通过磁盘读取相应的执行文件,假如需要通文件系统来读取系统加载文件的话,那么先要找到文件系统以及文件系统依赖项。然而要找到文件系统必须足够搭建一个文件系统可以运行的系统环境。这就形成一个死结,因此在传统BIOS中,系统启动都是按照滚雪球的方式逐步建立起来的。首先通过MBR找到可以加载系统的文件,然后利用这些文件搭建一个足以运行文件系统的系统环境,最后文件系统加载文件逐步形成一个可以运行的系统。因此关键是怎样找到MBR,以及怎样找到系统文件。其实答案很简单——绕开文件管理系统,利用磁盘提供的绝对地址进行读写就可以实现。这也是为什们MBR存在于磁盘的第一个块中,因为这是一个强制规定的绝对地址。下面是grub 0.97中的stage1中的汇编文件,这个汇编文件编译之后可以写到软盘或者U盘,光盘来启动。在Linux系统下面命令为dd或者grub-install。
#define ABS(x) (x-_start+0x7c00)
#define MSG(x) movw $ABS(x), %si; call message
#define MOV_MEM_TO_AL(x) .byte 0xa0; .word x
文件的开始部分为三个宏,第一个宏是相对地址到绝对地址的转化过程。由于磁盘的第一个块被复制到内存的0x7C00处,因此需要经过这一步做磁盘相对位置到内存绝对位置的转换。第二个宏,是一个字符显示函数,这个宏实际包含两条汇编指令,第一个是一个mov指令,第二条是函数调用指令。注意,在GCC的汇编语言中和VS的汇编语言是不一样的。在VS中分号是一个注释符号,而在GCC中分号仅仅是一个分割符号,用于表明一个指令的结束。第三个宏,是一个机器码,汇编指令为movb x,%al;表明将一个内存中的数值放到al寄存器中。
.globl _start; _start:
jmp after_BPB
nop
after_BPB:
cli
boot_drive_check:
jmp 1f
testb $0x80, %dl
jnz 1f
movb $0x80, %dl
1:
上面这一部分代码需要注意的部分有三处,第一个是jmp后面的nop,由于jmp指令只需要3个字节,所以需要另外加一个字节来进行对齐。然后是after_BPB节点处的清中断,由于到目前位置还没有堆栈。所以中断需要被屏蔽。下面的验证,并非是必须的。这里直接跳过去了,这就是需要注意的第三处。另外在after_BPB和nop指令之间其实存在一系列的BPB的参数,这些参数用于保存磁盘的基本信息。标号1是一个代码连结的符号。
1:
ljmp $0, $ABS(real_start)
real_start:
xorw %ax, %ax
movw %ax, %ds
movw %ax, %ss
movw $STAGE1_STACKSEG, %sp
sti
MOV_MEM_TO_AL(ABS(boot_drive))
cmpb $GRUB_INVALID_DRIVE, %al
je 1f
movb %al, %dl
1:
代码运行到这里之后,第一件事就是设置