最近,正在学习 iPXE 的源代码,于是开始各种 Google 查找 iPXE 的资料进行学习。学习中发现需要重点了解计算机的启动方式,现将学习过程中遇到的很多新的知识点整理为此文,作为后续的参考。
一台运行操作系统的设备(PC 或嵌入式 Linux 设备)从上电到出现我们用户可见的操作系统提供的 GUI 或者 CLI 经历了一些列的过程。这个过程在 PC 和 嵌入式设备上稍有不同,但大致的流程仍然基本一致。
引导过程通常会被划分为不同的阶段。其中,固件阶段毫无疑问是引导的第一个阶段,目前几乎所有常见系统级芯片都是这样。固件阶段中的代码通常是出厂时由厂家内置到 ROM 中的,它们通常被统一称为固件(firmware)。当然,在不同的芯片中可能有自己的叫法。
- PC 的 ROM 程序通常是放到主板上的,而嵌入式芯片是放在芯片内部。
- BootLoader 通常又会分为多个子阶段。
BIOS(Basic Input Output System,基本输入输出系统)也称为系统 BIOS、ROM BIOS、BIOS ROM 或 PC BIOS,是一组固化到计算机内主板上一个 ROM 芯片中的程序 ,包含计算机最重要的基本输入输出的程序、开机后自检程序和系统自启动程序,它可从 CMOS 中读写系统设置的具体信息。
现在,BIOS 这个词已经成为 PC 启动阶段的一个标准,是 PC 启动过程中的一个固定阶段。
目前,全球只有四家独立 BIOS 供应商(IBV),曾经的 Award Software 与 General Software、Microid Research 均被凤凰科技收购,SystemSoft 被 Insyde Software 收购。
BIOS 是连接软件与硬件的一座“桥梁”,是计算机的开启时运行的第一个程序,主要功能是为计算机提供最底层的、最直接的硬件设置和控制。因此,硬件系统一上电或重置,处理器要执行第一条指令的地址会被定位到 BIOS 存储器,初始化开始运行,并逐步过渡到操作系统。
最初,BIOS(Legacy BIOS 或 UEFI BIOS) 固件是存储在 PC 主板上的 ROM 芯片中的,无法修改。在后来的计算机系统中,BIOS(Legacy BIOS 或 UEFI BIOS) 固件开始存储在 FLASH 上,因此可以在不从主板上卸下芯片的情况下重写。
- BIOS 芯片通常位于南桥芯片附近,附件还有个纽扣电池。
- 现代主板上一般都是两个 BIOS 芯片,其中一个作为备份。
现代 BIOS 都会提供一个配置界面,用户可以在 PC 启动时通过特定的按键进行访问。在 BIOS 的配置界面,用户可以看到 PC 硬件的基本信息,并对硬件进行配置。BIOS 还支持用户自由配置引导优先级顺序,以使 PC 从各种介质(硬盘、U 盘、光盘等)中启动。
在 PC 机中,硬盘必须要分区之后才能使用。传统的 BIOS 采用的分区方案被称为 MBR(Master Boot Record,主引导记录),而 UEFI 采用的分区方案被称为 GPT( GUID Partition Table,GUID 分区表)。采用分区的原因有如下两点:
现在,一般用户说的 BIOS 往往指的是 PC 上电后可进入的配置界面,这个阶段实际的实现可能是 Legacy BIOS 也可能是 UEFI BIOS。在 UEFI 诞生初期,专业人士通常严格区分 Legacy BIOS 还是 UEFI BIOS。而如今随着 UEFI 的普及,专业人士中说 BIOS 就是指的 Legacy BIOS,UEFI 就是 UEFI(孩子翅膀硬了要独立)。
Legacy BIOS 通常与 MBR 配合使用,PC 上电后,Legacy BIOS 优先被运行,最后会读取硬盘上的 MBR 信息,然后将 CPU 控制权移交到 MBR 中去。MBR 中记录着硬盘本身的相关信息以及硬盘各个分区的大小及位置信息,是数据信息的重要入口。
从软件实现上来说,Legacy BIOS 和 MBR 中的操作系统引导程序是分别独立实现,他们并没有一个统一的标准。这就导致了不同厂商苦于兼容性问题。
BIOS 这个名字源自于 1975 年 IBM PC 上由 Gary Arlen Kildall 创造的 CP/M 操作系统,后来成为了事实上的标准。之所以有 Legacy 这个称呼,是因为旨在代替 BIOS 的 UEFI 出现后人们为了以示区分。
Legacy BIOS 自诞生之初就是一个专有软件,通常用是汇编语言编写的。从 20 世纪 90 年代中期开始,BIOS 提供一个配置界面(通过特定按键访问),但往往都非常简陋(通常是纯文本形式)。大多数 BIOS 实现都是专门设计用于特定的计算机或主板型号。传统的 BIOS 有三个主要功能:
最初的 Intel 8086 是 16 位的,随之诞生的 BIOS 就工作在16 位的运行方式(实模式)。后来,x86 进入了 32 位时代,Intel 仍然保留了 16 位实模式(开机后,CPU 切换为 16 位实模式运行 BIOS)。 BIOS 就以 16 位汇编代码,寄存器参数调用方式,静态链接,以及 1MB 以下内存固定编址的形式存在十几年。
尽管,PnP BIOS、ACPI、传统 USB 设备等许多新元素添加到 BIOS 中,但 BIOS 的根本性质没有得到任何改变。
现在,随着 UEFI 的逐步普及,Legacy BIOS 基本已经没有了,甚至于最初 UEFI 提供的兼容 Legacy BIOS 的模块(Compatibility Support Module ,CSM)也逐渐被众多厂商放弃支持了。MacOS、Windows 11 等系统已经不再支持 Legacy BIOS 启动。
MBR(Master Boot Record,主引导记录)也被称为主引导扇区是分区计算机大容量存储设备(如固定磁盘或可移动驱动器)最开始的一种特殊类型的引导扇区。MBR 的概念于 1983 年随 PC DOS 2.0 一起公开引入,旨在与 IBM PC 兼容系统及其他系统一起使用。并在后续进行了多次扩展(例如,NEWLDR MBR、AAP MBR),目前我们常见的是现代标准 MBR。
UEFI BIOS 通常与 GPT 配合使用(UEFI 也支持 MBR 分区),PC 上电后,UEFI BIOS 优先被运行,执行完相关检测后会读取载存储在 ESP 上的文件,以启动已安装的操作系统和各种实用程序。
从软件实现上来说,UEFI 完整定义了整个开机过程,BIOS 固件和操作系统引导程序(通常是前置阶段)都涵盖在了 UEFI 中。UEFI 作为一个统一的标准,让 PC 的启动阶段更加透明。开源 UEFI 固件开发环境 EDK2。
UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)是一个公开的规范,为个人电脑操作系统和平台固件之间的接口定义了一个新的模型。该接口由数据表组成,其中包含与平台相关的信息,以及操作系统及其加载程序可用的启动和运行时服务调用。它们共同为引导操作系统和运行预引导应用程序提供了一个标准环境。
UEFI 源自于 20 世纪 90 年代中期第一个英特尔–惠普安腾系统的早期开发过程中的 EFI(Extensible Firmware Interface)。2005 年 7 月,英特尔停止了 1.10 版 EFI 规范的开发,并将其贡献给了 UEFI 论坛,该论坛将该规范开发为统一可扩展固件接口 (UEFI)。
UEFI 规范可以从 UEFI Forum 官网下载:https://uefi.org/specifications。
UEFI 本身还包含一些其他规范,通过多种规范,UEFI 统一规范了 PC 启动的过程,原来专用的 BIOS 程序被 UEFI 定义的程序取代(通常被称为 UEFI BIOS),UEFI BIOS 负责加电自检(POST)、联系操作系统以及提供连接操作系统与硬件的接口,旨在取代老旧的 BIOS。
UEFI 规范定义的接口包括平台信息的数据表以及操作系统加载程序和操作系统可用的引导和运行时服务。UEFI 应用程序(UEFI Application)和 UEFI 驱动程序(UEFI driver)是 PE 格式的 .efi 文件,可用 C 语言编写。
UEFI 的行为类似于位于固件和操作系统之间的小型化操作系统。它在启动时执行与 BIOS 相同的诊断,但提供了更大的灵活性。操作系统直接在 UEFI 中引导。一般认为,UEFI 由以下几个部分组成:
Pre-EFI 初始化程序在系统开机的时候最先得到执行,它负责最初的 CPU,芯片组及主存的初始化工作,紧接着加载 UEFI 的驱动程序执行环境(DXE)。当 DXE 被加载运行时,系统便具有了枚举并加载其他 UEFI 驱动程序的能力。DXE 枚举并加载各种总线(包括 PCI、SATA、USB、ISA)及硬件的UEFI驱动程序。
UEFI 代码在 CPU 上以 32 位或 64 位保护模式运行,而不是以 16 位实际模式运行。因此,UEFI 固件区分架构,在 UEFI 引导模式下,通常只能执行特定架构的 UEFI 操作系统和特定架构的 EFI 应用程序(EBC 程序除外)。 比如,采用 64 位 UEFI 固件的 PC,在 UEFI 引导模式下只能执行 64 位操作系统启动程序;而在 Legacy 引导模式(即 BIOS 兼容引导模式)下,既可以执行 16 位的操作系统(如 DOS),也可以执行 32 位操作系统和 64 位操作系统。
UEFI 固件的二进制文件格式采用的是微软为 Windows NT 定制的 PE(Portable Executable) 文件格式。 目前常见的二进制文件格式有:Windows 的 PE、Linux 的 ELF、MacOS 的 Mach-O。
DOS STUB 是可选的,上图不含 DOS STUB。默认情况下,现代 C 编译器会自动添加 DOS STUB。如果直接使用 Win32 汇编,DOS STUB 则根据需要自行添加。
微软二进制:COM 格式 ➜ DOS_MZ ➜ NE(New Executable) ➜ PE(Portable Executable)
GPT( GUID Partition Table,GUID 分区表) 是计算机存储设备(如硬盘驱动器或固态驱动器)的分区表布局的标准,使用通用唯一标识符,这些标识符也称为全局唯一标识符 (Globally Unique Identifier,GUID)。它是统一可扩展固件接口 (UEFI) 标准的一部分,旨在取代 MBR。GPT 也使用逻辑区块位址(LBA)取代了早期的 CHS 寻址方式。
EFI 系统分区(EFI system partition,ESP)是一个 FAT 或 FAT32 格式的磁盘分区(占一个 Partition Table Entry),启动计算机时,UEFI 固件会加载存储在 ESP 上的文件,以启动已安装的操作系统和各种实用程序。
在 Linux 中,ESP 的挂载点通常是 /boot/efi
目录,使用 root 权限就可以查看内容;在 Windows 中,ESP 是个隐藏分区,通过一些磁盘管理软件(例如,DiskGenius)就可以查看其中的内容。Windows 引导管理器就位于 ESP 的 \efi\Microsoft\boot\
子文件夹中;Ubuntu 则是 \efi\ubuntu\boot\
目录中。
在 GPT 方案中,EFI 系统分区的全局唯一标识符(GUID)是 C12A7328-F81F-11D2-BA4B-00A0C93EC93B,而其在 MBR 方案中的标识符是 0xEF。GPT 分区和 MBR 分区的磁盘都可以包含一个EFI 系统分区,因为 UEFI 固件支持这两种分区方案。
在现代 PC 机中,硬盘控制器、显卡、网卡等扩展卡通常会包含被称为 Option ROM 的 BIOS 扩展程序,以此为 BIOS 提供附加的功能。部分内置于主板的设备(如板载 RAID),其 Option ROM 可能包含在主板 BIOS 中。最为常见的 Option ROM:
在即插即用 BIOS 标准的开发和普遍采用之前,一个附加设备,如硬盘控制器或网络适配器卡(NIC),通常需要包括一个 Option ROM 以便启动,因为主板 BIOS 不包括任何对设备的支持,所以不能将它纳入 BIOS 的启动协议。而后产生的 PCI 标准,PnP 标准中均有对 Option ROM 的相关定义。
Option ROM 通过 BIOS 引导规范扩展 BIOS 的启动功能。BIOS 引导规范 (BBS) 由康柏、英特尔和凤凰科技组成的联盟开发,旨在标准化即插即用 (PnP) BIOS Option ROM 和不符合 PnP BIOS 标准的传统 Option ROM 的初始化顺序,以及它们挂接中断的顺序。该标准提出了启动连接向量 (BCV) 表和 BCV 优先级的概念。
详细说明:https://resources.infosecinstitute.com/topic/pci-expansion-rom/
上电自检(POST)完成之后,BIOS 就会依次查找 Option ROM,并执行它们。Option ROM 执行时会完全控制了系统,可以不受限制地执行的操作。而且,只有 Option ROM 执行结束后,主导将 CPU 交还给 BIOS 后,BIOS 才可以继续执行下一个 Option ROM 或其他操作。
Option ROM 分为传统 Option ROM 和 UEFI Option ROM,可以在 PCI Data Structure 的 Class Code 查看(内部偏移 0x0D) 。UEFI Option ROM 和 Legacy Option ROM 的结构是相同的。UEFI Option ROM 利用了之前保留的字节(偏移 0x04 处),用来表明自己的身份。
传统 Option ROM 代码生成文件的大小不能太大,BIOS 文件预留的空间不多(通常是 0xC000 ~ 0xE000)。UEFI Option ROM 的限制则在 UEFI 规范的 4.4.2 PCI Option ROMs
章节有详细说明。
在嵌入式芯片中,往往存在一块固定的区域,存放着厂家提供的一段简单的代码,这段代码通常被称为 ROM CODE。ROM CODE 最基本的功能就是能够实现对芯片内部的编程。大多数嵌入式芯片支持配置启动方式,用户可以选择不从 ROM CODE 启动。
自 P6 微架构以来,英特尔处理器就具有可重新编程的微码。AMD 处理器自 K7 微架构以来就已经有了可重新编程的微码。BIOS 包含处理器微码的补丁,用于修复初始处理器微码中的错误。微码被加载到处理器的 SRAM 中,因此重新编程不是持久的,因此每次系统通电时都会执行微码更新的加载。
引导加载程序(BootLoader)是整个引导过程的最后一个阶段。它会为操作系统内核准备各种环境,然后将操作系统内核加载到内存中,进而将 CPU 的控制权交给内核,加载过程结束,系统内核开始接管 CPU 运行。
BootLoader 其实就是一个微型的系统。现在,几乎所有的 BootLoader 除了提供引导操作系统内核功能外,通常还会提供各种辅助功能。例如,最为常见的就是 BootLoader 会提供一个命令行界面,我们可以通过这个命令行界面进行各种操作。
BootLoader 往往也是被划分多个阶段的,这些阶段在不考虑 ROM 阶段时也被称为第一阶段、第二阶段等。从代码的角度来说,BootLoader 会被分为多个可执行程序,这些可执行程序会依次调用,并最终启动操作系统内核。划分阶段的一个重要原因是,不同阶段能够使用的资源不同。
BootLoader 有很多种实现,多数引导加载程序都支持引导多种操作系统。然而实时上,每个流行的操作系统都有自己固定的默认引导加载程序。例如,Windows 默认使用 BOOTMGR、Linux 默认使用 GRUB2、嵌入式系统常用 U-Boot。
各种 BootLoader 比较:https://en.wikipedia.org/wiki/Comparison_of_bootloaders
GNU GRUB(GNU GRand Unified Bootloader,GRUB)是一个来自 GNU 项目的多操作系统启动程序。GRUB 是对自由软件基金会的多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。GRUB 可用于选择系统分区上的不同内核,也可用于向这些内核传递启动参数。
目前,GRUB 已经被 GRUB 2 取代,原 GRUB 现在被称为 GRUB Legacy,并且已经不再维护。GRUB 2 经过了完全的重构,以使其更健壮、现代、便携,现在的大多数 Linux 发行版都使用的是 GRUB 2,GRUB Legacy 基本已经没有了。
一个名为 boot.img
的程序会被写入 MBR 的主引导代码区域。该程序通过一个 48 比特的 LBA 来定位 core.img
,core.img
加载 /boot/grub/i386-pc/normal.mod
,normal.mod
会解析 /boot/grub/grub.cfg
,可选加载模块,并显示菜单。在 MBR 分区的磁盘上,core.img
存储在 MBR 和第一个分区之间的空扇区中。
UEFI 本身定义了很多标准,在 UEFI 模式下,原来的 BIOS 代码会被一个简单的 UEFI 代码代替,此外,UEFI 还要求必须有一个 ESP 分区,里面存放了启动代码。在 Linux 中,ESP 分区通常会被挂载在 /boot/efi
目录下,切换为 root 用户就可以进行查看。对于 64 位系统,/efi/
会直接用来引导固件(/boot/grub
下的文件)。
NTLDR(Windows NT Loader 的缩写)是 1993 年 Windows NT 操作系统开始采用的引导加载程序,常见与 Windows NT、2000 和 XP 以及 Windows Server 2000 和 Windows Server 2003 中。但是,支持在文件中给定适当的引导扇区,加载非基于 NT 的操作系统。
NTLDR 将其引导配置存储在一个名为 BOOT.INI
的基于文本的简单文件中,该文件存储在活动分区的根目录中(通常为 C:\Boot.ini)。一旦第二阶段引导加载程序加载并执行 NTLDR,它就会执行一个名为 NTDETECT.COM
的帮助程序,该程序可识别硬件并生成有关系统的信息索引。
BOOTMGR 是一个独立的引导加载程序,具有更多选项,专门设计用于与现代操作系统中的新功能兼容,并在设计时考虑了 EFI 和 GPT。Windows Vista 引入了 BOOTMGR (Windows Boot Manager)引导加载程序,目前由 Windows Vista、7、8 和 10 以及 Windows Server 2008 和 2012 使用。
与 NTLDR 不同,BOOTMGR 将其配置存储在一个独立于固件的名为 BCD(Boot Configuration Database)的文件中。BCD 文件是一个二进制数据库,无法手动打开和编辑。必须使用专门设计的命令行工具(如 bcdedit.exe)和更用户友好的 GUI 实用程序(如 EasyBCD) 来读取和修改操作系统列表。
BCD 文件的格式与 Windows 注册表单元相同,并最终挂载在注册表键 [HKEY_LOCAL_MACHINE\BCD00000
下。UEFI 启动时,该文件位于 ESP 中:/EFI/Microsoft/boot/BCD
;对于传统的 BIOS 引导,该文件位于活动分区: /boot/BCD
。
BIOS 在启动时从硬盘驱动器中调用 MBR 引导代码。MBR 引导代码和 VBR 引导代码特定于 OS。在 Microsoft Windows 中,MBR 引导代码试图找到一个活动分区,然后执行活动分区的 VBR 引导代码。VBR 引导代码试图从活动分区查找并执行 bootmgr 文件。
UEFI BIOS 引导 windows 系统时,是通过一个 FAT 格式分区下的 bootmgfw.efi 文件来导入 /ESP/Microsoft/Boot/BCD
文件,然后 BCD 文件根据自身的配置内容加载系统引导文件 C:\Windows\System32\Boot\winload.efi
(操作系统启动加载程序)加载操作系统内核程序C:\Windows\System32\NTOSKRNL.EXE
和核心设备驱动程序。
U-Boot(Universal Boot Loader,通用引导加载程序)是嵌入式设备中使用非常多的使用 GPL 协议开源的引导加载程序。U-Boot 实现了嵌入式基本引导要求(Embedded Base Boot Requirements,EBBR)规范中定义的 UEFI 规范的子集。它可用于多种架构,包括 68k,ARM,Blackfin,MicroBlaze,MIPS,Nios,SuperH,PPC,RISC-V 和 x86。
U-Boot 由系统的 ROM 从支持的启动设备加载。如果存在大小限制,U-Boot 可以分为两个阶段:平台将加载一个小型 SPL (Secondary Program Loader),这是 U-Boot 的精简版本,SPL 将进行一些初始硬件配置,并加载更大的、功能齐全的 U-Boot 版本。
Syslinux(小写)是 Syslinux Project 这个项目的简称。该项目由 H. Peter Anvin 建立的,旨在开发一套用于在计算机上启动 Linux 发行版的引导程序。Syslinux Project 由五种不同的引导加载程序(注意,名字全为大写)组成:
Syslinux 的最新官方版本可以从 https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/ 下载 .tar.gz
、.tar.bz2
、.tar.xz
和 .zip
格式,源代码仓库地址:https://repo.or.cz/syslinux.git。压缩包里包含了适用于大多数用户的源代码和官方预编译二进制文件。