MBR的介绍网上很多,没错,就那个最后以0x55AA结尾的512字节的引导块,OpenBSD提供了引导MBR实现;OpenBSD在x86上的引导过程为MBR --> PBR --> boot --> kernel,其整体过程在boot_i386(8) (http://man.openbsd.org/OpenBSD-6.0/man8/i386/boot_i386.8)中有详细讲解,让我们直接上代码吧,MBR的代码在sys/arch/i386/stand/mbr/目录下,主要就是其中的mbr.S
代码是AT&T格式的汇编,但开头的注释已经几乎将其功能介绍完了:
/*
* Memory layout:
*
* 0x07C00 -> 0x07DFF BIOS loads us here (at 31k)
* 0x07E00 -> 0x17BFC our stack (to 95k)
*
* 0x07A00 -> 0x07BFF we relocate to here (at 30k5)
*
* 0x07C00 -> 0x07DFF we load PBR here (at 31k)
*
* The BIOS loads us at physical address 0x07C00. We use a long jmp to
* normalise our address to seg:offset 07C0:0000. We then relocate to
* 0x07A00, seg:offset 07A0:0000.
*
* We use a long jmp to normalise our address to seg:offset 07A0:0000
* We set the stack to start at 07C0:FFFC (grows down on i386)
* The partition boot record (PBR) loads /boot at seg:offset 4000:0000
*/
大概帮翻一下就是BIOS会将mbr.S加载到0x7C00处,然后mbr.S将自己复制并重定性到0x7A00处,然后将PBR加载到自己原来的位置0x7C00处,整个过程使用的栈在0x07E00 ~ 0x17BFC,然后jmp到PBR继续执行,PBR然后加载/boot到4000:0000,mbr.S是实模式程序,当然涉及到x86实模式的寻址方式,整个过程就是这样了。
MBR虽然有510字节,但实际用户代码的只有400+字节,因为MBR里还包含一个分区表,分区在MBR末尾定义:
/* partition table */
/* flag, head, sec, cyl, type, ehead, esect, ecyl, start, len */
. = DOSPARTOFF /* starting address of partition table */
pt:
.byte 0x0,,,,,,,
.long ,
.byte 0x0,,,,,,,
.long ,
.byte 0x0,,,,,,,
.long ,
.byte DOSACTIVE,,,,DOSPTYP_OPENBSD,,,
.long ,0x7FFFFFFF
/* the last bytes in the sector contain the signature */
. = 0x1fe
基本上就是每个分区的开始结束扇区、各种flag等信息,其中一个flag是表示是否为“活动分区”的,mbr.S执行是会检查这个标志,并将活动分区的第一个扇区(512字节)加载到0x7C00(MBR在开始执行时已经将自己从定位到0x7A00处了),然后jmp到0x7C00执行PBR。
加载PBR也就是读磁盘,读磁盘可以使用传统的CHS模式或者LBA模式,通过int 13检测硬件是否支持LBA,否则回退到CHS模式操作,或者可以在启动时按住shift键强制使用CHS模式。
另外还有一点,就是BIOS会在执行MBR前将引导的设备号放到DL寄存器中,第一块硬盘为0x80。
OK,mbr.S的内容基本就这些了,代码还是相当清晰的,这就当个简单的笔记吧,熟悉的同学直接跳过这篇。