「Linux Boot Process and Shutdown」- Boot loader Stage 1(MBR loading)

  CREATED BY JENKINSBOT

在软盘中,整个引导扇区的 512 字节全为可执行代码(除了引导签名);在磁盘中,主引导记录的 0x0000 – 0x01bd 含有可执行代码,后面为四个分区表,每个分区表十六字节,以及两字节引导签名。

主引导记录代码执行的“环境”

这种早期环境是高度实现定义的(即由特定 BIOS 实现定义的),具有很多不确定性,切勿对寄存器的内容做任何假设:可能被初始化为零,也可能包含伪值。包括 FLGAS Register 与 SP Register,可能也没有有效的栈!唯一能够确定是 DL Register 存有引导代码(来自引导代码加载地方)。

当前处理器处于 Real Mode(除非你使用某些罕见 BIOS 帮你激活 Protected Mode),就是说需要编写代码激活以激活其他硬件的 Protected Mode,还要测试是否已经激活保护模式。这些都是主引导记录代码需要处理的问题。

主引导记录代码的工作任务

这些都是引导代码需要完成的工作,除此之外它还要完成其他工作(少量):
1)决定从哪个分区引导启动,或者显示菜单以让用户选择引导分区
2)确定内核位置以载入内存(要么检测文件系统,要么从固定位置加载)
3)将操作系统内核载入内存(需要磁盘读写)
4)启用保护模式
5)准备内核的运行环境(比如设置栈空间)

虽然不必按以上顺序完成这些任务,但是这些任务是必须完成的(历史遗留问题要求我们完成这些任务)。

完成引导工作任务的几种途径

第一种,极客式,MBR only
在主引导记录中完成所有事情。然而这几乎不可能,并且没有办法处理特殊场景以及显示错误信息(空间无法容纳更多程序)。

第二种,单阶段,MBR => Stub Program + Kernel
编写“桩程序(Stub Program)”,链接到内核镜像前面。引导记录加载内核映像(低于 1MiB 内存标记,因为在实模式下,这是内存上限!),跳转到桩程序,桩程序负责切换到保护模式并进行准备工作,正确跳转到内核。

第三种,两阶段,MBR => Stub Program => Kernel
编写分离的“桩程序(Stub Program)”(即不与内核链接),加载到 1MiB 内存标记以下,然后完成上述工作。

当前实现方案(以 GRUB 为例)

如果实现方式不同,那么 MBR 的工作内容也有所不同。

我们以 GRUB 为例,它提供 主引导记录代码 与 桩程序,主引导记录代码完成以下工作:
TODO 整理 GRUB 主引导记录代码的任务

关于编写引导加载程序

棘手问题:由于 GCC 只能生成 Protected Mode 的可执行代码,所以用于该早期环境的的代码不能使用 C 编写。

在传统上,MBR 会将自己重新定位到 0x0000:0x0600 地址,从分区表上确定活动分区,将该活动分区的第一扇区加载到 0x0000:0x7c00 地址,并跳转到该地址。这被成为 Chain Loading。如果希望自己写的引导记录能够双重引导,例如 Windows,那它应该模仿这种行为。

最简单的实现方式是使用现有的引导程序,比如 GRUB,两阶段引导,不仅提供 Chain Loading 功能,还将环境初始化为良好定义的状态(包括 Protected Mode 与 从 BIOS 读取各种需要信息),可将通用可执行文件当作内核镜像加载(其他引导程序可能需要固定二进制文件),支持可选内核模块,多种文件系统,无盘启动。