原文:https://www.cnblogs.com/mahocon/p/5691348.html
本文翻译自:https://www.happyassassin.net/2014/01/25/uefi-boot-how-does-that-actually-work-then/
术语
首先,我们了解一些术语。BIOS 和 UEFI 都是计算机的 固件
类型。BIOS 固件(主要)用于 IBM PC 兼容计算机。UEFI 的通用性更强,可用在非“IBM PC 兼容”系列的计算机上。
不存在“UEFI BIOS”。没有任何一台计算机会有“UEFI BIOS”。BIOS 不是所有 PC 固件的通用术语,它只是 PC 固件的一种特定类型。计算机中包含固件。如果你有一台 IBM PC 兼容计算机,那么固件几乎肯定就是 BIOS 或 UEFI。如果你运行的是 Coreboot
,那么恭喜,你是个例外,引以为傲吧。
安全启动 (Secure Boot) 与 UEFI 不是同一个概念。请不要将这些术语混淆使用。安全启动 (Secure Boot) 实际上是 UEFI 规范的一项可选功能,于 UEFI 规范版本 2.2 引入。我们稍后会详细讨论安全启动 (Secure Boot) 到底是什么,但是目前而言,只需要记住它和 UEFI 不同即可。
UEFI 不是由微软开发的,也从来不受微软控制。它的前身和基础——EFI,是由 Intel 开发和发布的。UEFI 由 UEFI 论坛 进行管理。微软是 UEFI 论坛的成员之一。
UEFI 启动:背景
我们现在来看看 UEFI 计算机上的启动原理。即使未掌握本文的细节,也请记住这一点:UEFI 与 BIOS 完全不同。UEFI 启动原理与 BIOS 绝对不同。你不能把 BIOS 启动的原理直接套用到原生 UEFI 启动上。你不能把专为 BIOS 启动设计的工具应用到原生 UEFI 启动的系统上。
还需要了解一个重点:许多 UEFI 固件实现了某种 BIOS 兼容模式(有时候称为 CSM
)。许多 UEFI 固件可以像 BIOS 固件一样启动系统,它们可以查找磁盘上的 MBR,然后从 MBR 中执行启动装载程序,接着将后续工作完全交给启动装载程序。有时候,其他人误将此功能称为“禁用 UEFI”,从语言学角度而言,这种说法是荒谬的。他们讨论的是通过 UEFI 固件的一项功能,以“BIOS 风格”启动系统,而不是采用原生 UEFI 方式启动系统。
我想解释一下原生 UEFI 启动。如果你有一台基于 UEFI 的计算机,其固件具有 BIOS 兼容功能,并且你打算一直使用这项兼容功能,在启动过程中,你的计算机看起来就是基于 BIOS 的。你只需要像 BIOS 启动一样进行所需操作即可。如果你确实有此打算,那么就不要中途变卦。对于你日常使用的操作系统,强烈建议不要混合使用原生 UEFI 启动和 BIOS 兼容启动,尤其不要在同一块磁盘上混用。这么做的话,你会痛不欲生。如果你决定混合使用原生 UEFI 启动和 BIOS 兼容启动,到时候就别找我哭诉。
为了理清头绪,我将假设磁盘采用 GPT ,并且包含用于 EFI 的 FAT32 EFI 系统分区(ESP)
。根据你对这些知识的深入程度,你可能发现,在进行原生 UEFI 启动时,GPT 磁盘和 EFI FAT32 ESP 并不是必要条件。但是 UEFI 规范和 GPT 磁盘以及 EFI FAT32 ESP
的联系程度相当密切。在99%的情况下,你要处理的也正是这样的组合。除非你在使用 Mac(老实说,Mac 混乱不堪)。
UEFI 原生启动:实际工作原理
言归正传。本节将解释原生 UEFI 启动的实际工作原理。如果已掌握一定程度的背景知识,可能更容易深入理解本节内容。
在固件层,UEFI 的基础架构更丰富,可用于处理系统启动。UEFI 远不像 BIOS 那么简单。与 BIOS 不同,UEFI 确实可以(不同程度上)理解“磁盘分区”、“启动装载程序”以及“操作系统”的概念。你可以稍微看看 BIOS 启动过程,然后再看看 UEFI 启动过程,了解 UEFI 启动过程如何采用多种措施来解决特定问题。
在思考启动过程时,你会发现 BIOS/MBR 查找启动装载程序的方法实在不怎么样。BIOS/MBR 非常奇葩:位于磁盘起始位置的这一小段空间包含 神奇代码 (magic code)
,而这段神奇代码只作用于系统固件和写入此神奇代码的工具。这种方法有许多问题。
处理不便——你需要特殊工具来写入 MBR,如果要查看 MBR 中包含的内容,唯一的方法几乎就是把 MBR
dd
出来,然后进行检查。如上所述,MBR 本身不足以容纳许多现代启动装载程序。这些启动装载程序会将自身的一小部分安装在 MBR 中,而将其他部分安装到磁盘上的可用空间中。这段可用空间位于常规 MBR 末尾和第一个分区的起始位置之间。这就会造成很大的问题(其实这整个设计就是个大问题,不过无所谓)。对于第一个分区的起始位置,并没有成文的可靠规定,因此难以确保空间足够。只有一件事情是肯定的:这段空间不足以容纳某些启动装载程序的配置。
如果要选择其他启动目标(除磁盘以外),这种设计没有提供任何标准化层或标准化机制,但是用户希望选择除磁盘以外的启动目标。也就是说,他们希望实现多重可启动对象——通常是操作系统。在 BIOS/MBR 组合中,实现这种目的的唯一方法是由启动装载程序进行处理;至于如何实现,并没有进行获得广泛认可的规定。虽然实现的方法非常多,但是它们无法彼此协作,而且也都不是获得广泛认可的标准或规定。而在操作系统/操作系统安装层编写工具的难度很大,无法干净利落地处理多重启动。因此这种设计非常混乱。
这种设计没有提供标准方法,让用户可以从除磁盘以外的目标进行启动。本文不会就此问题进行详细讨论,但是请注意,UEFI 启动的另一优势为:它提供了进行启动(例如,从远程服务器进行启动)的标准方法。
固件层以上的其他层无法配置固件的启动行为,BIOS 没有提供相应机制。
可以想象,在 UEFI 设计之初,开发人员思考过这些问题,并最终提出解决方案。UEFI 固件并不仅仅可以识别磁盘,它也知道启动装载程序代码在每个磁盘上所处的位置,而且在固件层,UEFI 的基础架构更丰富,可用于处理启动装载。接下来,我们讨论下 UEFI 规范中定义的相关内容。
EFI 可执行文件
UEFI 规范定义了一种可执行文件格式,并要求所有 UEFI 固件能够执行此格式的代码。当开发人员为原生 UEFI 编写启动装载程序时,就必须按照这种格式编写。这种设计非常简洁直观,也无需进一步解释:对于固件可以执行的代码,固件规范真正定义了其通用格式,这是件好事。
GPT(GUID 分区表)格式
GUID 分区表格式与 UEFI 规范具有密切联系,而且,它并不特别复杂,无需多加解释。GPT 是 UEFI 规范提供的良好基础架构之一。GPT 仅仅是分区表的一种标准——磁盘起始位置的信息定义了磁盘所包含的分区。相比 MBR/MS-DOS 分区表,这种分区表对分区的定义要好得多,并且 UEFI 规范要求 UEFI 兼容固件必须能识别 GPT(也要求固件能识别 MBR,以保证向后兼容)。所有这些规范都是相当实用的基础架构:UEFI 规范正建立某些功能,固件层上的一切都可依靠固件本身来实现这些功能。
EFI 系统分区
在修订本文时,我才真正思考 EFI 系统分区的概念,让我有如醍醐灌顶。实际上,“EFI 系统分区”的概念可以解决“奇葩”的 MBR 空间所产生的问题。在磁盘起始位置留出自由空间,用于存放启动装载程序代码,但又不定义其容量,这种设计糟糕透顶。这一点在上文已经讨论过了。EFI 系统分区是 UEFI 用于解决这种问题的解决方案。
具体的解决方案如下:我们要求固件层能够读取某些特定的文件系统类型。UEFI 规范要求兼容固件必须能读取 FAT 格式的变种(包括 FAT12
、FAT16
和 FAT32
)。UEFI 规范实际扮演的角色就是编纂整理 FAT 文件系统格式的现有解释,确保在采用 UEFI 时可以使用那些格式,并规定 UEFI 兼容固件必须能够读取那些格式。UEFI 规范针对这方面的具体规定如下:
可扩展固件接口 (EFI) 支持的文件系统基于 FAT 文件系统。EFI 定义了可以明确记录和测试的具体 FAT 版本。FAT 的唯一定义必须符合 EFI 规范及关联参考文档,对 FAT 唯一定义的实现必须支持 EFI。为区分 EFI 文件系统与纯 FAT,定义了新的分区文件系统类型。
EFI 系统分区 是采用 FAT 变种(UEFI 规范定义的变种之一)格式化的任意分区,该分区被赋予特定 GPT 分区类型,以帮助固件识别该分区。此分区的目的如上所述:固件层确实可以读取“普通”磁盘分区中的数据。希望我已明确解释为何这种设计更佳:操作系统可以创建、格式化和挂载分区(采用广泛理解的格式),并将启动装载程序的代码和固件可能需要读取的所有其他内容放到这个分区中,而不用像 MBR 磁盘一样,将启动装载程序的代码写入磁盘的起始位置空间。
刚开始的时候,对我而言,整个 ESP 的设计看起来有点匪夷所思且令人困惑,因此我希望本节可以解释为何 ESP 实际上是非常优秀的设计——真正匪夷所思和令人困惑的设计是 BIOS/MBR。若要从操作系统层写入某些内容,唯一的方法是将这些内容写入磁盘起始位置的某部分(但不知道是多少)空间,而并没有具体规定其中的具体实现。如果回过头再看,这种设计并不明智,且难以理解。
正如我们稍后会强调的那样,UEFI 规范试图采用更直观严格的方法——它很少禁止固件执行其他操作。UEFI 规范并不反对编写固件,用于执行以其他格式写成的代码、读取其他类型的分区表,以及读取用 UEFI 变种文件系统(非 FAT)格式化的分区。但是 UEFI 兼容固件必须至少能够实现执行 EFI 可执行文件、读取 GPT 分区表、以及读取 ESP,因此如果你正编写操作系统或其他东西,并且想要在 UEFI 兼容固件上运行的话,你也得遵循 UEFI 规范,这就是 EFI 系统分区的概念非常重要的原因:它允许(至少理论上)将 EFI 可执行文件放在以 UEFI FAT 格式化且 GPT 分区类型正确无误的分区上,另外,系统固件要能够读取该分区。这种机制非常严谨,等价于 BIOS 中的“固件能够执行放置在 MBR 空间中的启动装载程序代码”。
UEFI 规范为我们提供了三大重要基础,这些重要基础是上层架构正常运行的立足之本:
读取分区表
访问某些特定文件系统中的文件
执行特定格式的代码
相比 BIOS 固件所提供的功能,UEFI 的功能要丰富得多。但是,为了完成固件层可以处理多重目标(而不仅仅是磁盘)启动的愿景,我们需要其他基础:需要建立一种机制,通过这种机制,固件可以查找各种可能的启动目标,并提供相应的配置方法。
UEFI 启动管理器
UEFI 规范定义了名为 UEFI 启动管理器的一项功能(Linux 发行版包含名为 efibootmgr
的工具,可用于更改 UEFI 启动管理器的配置)。如果你确实阅读过 UEFI 规范,那么就会发现,UEFI 规范对 UEFI 启动管理器作出了如下规定:
UEFI 启动管理器是一种固件策略引擎,可通过修改固件架构中定义的全局 NVRAM 变量来进行配置。启动管理器将尝试按全局 NVRAM 变量定义的顺序依次加载 UEFI 驱动和 UEFI 应用程序(包括 UEFI 操作系统启动装载程序)。
好,既然已经明确了这一概念,那我们就继续吧。不,先等等。我来先把那一项规定解释清楚,便于理解。简单来说,你可以把 UEFI 启动管理器视为启动菜单。在 BIOS 固件上,固件层的“启动菜单”(当然)是,启动时连接到计算机的各个磁盘——不多不少。但是对于 UEFI 固件而言,情况有所不同。
UEFI 启动管理器可以进行配置——简言之,你可以向“启动菜单”添加项或者从中删除项。固件也可以(事实上, UEFI 规范也有此要求)根据连接到计算机的磁盘或根据某些固件配置,在此启动菜单中“生成”有效项。你也可以检查启动菜单,确保正确无误。
UEFI 提供了一种非常优秀的机制,可以从上层架构执行此操作:你可以从已启动的操作系统中配置系统启动行为。如果已通过 UEFI 启动 Linux,就可以使用 efibootmgr
工具来完成所有这些操作。Windows 也有相应的工具,但是我对 Windows 下的工具非常不熟悉。我们不妨看一些典型的 efibootmgr 输出,这些是我从 Fedora 论坛转过来的,稍微进行了调整:
1 | [root@system directory]# efibootmgr -v |
这个示例非常清晰。我们可以从中观察细节。
第一行表示,目前你从“启动菜单”的哪个项进行了启动。第二行非常明显(如果固件的 UEFI 启动管理器显示了类似启动菜单的界面,那么这一行表示继续启动默认项之前的超时)。BootOrder 是列表中启动项的尝试顺序。其余输出显示了实际的启动项。我们稍后会说明每一个启动项具体作用。
如果完全正常启动 UEFI 固件,而不进行任何调整(我们稍后会讨论),UEFI 固件将按照BootOrder 中列出的顺序,尝试从“启动菜单”中的每个“项”进行启动。因此,在这台计算机上,UEFI 固件将尝试启动名为“opensuse”的项,如果启动失败,然后再尝试启动名为“Fedora”的项,然后再是“CD/DVD Drive”,接着是第二项“Hard Drive”。
UEFI原生启动:启动管理器项
那么,这些项的实际含义是什么?实际上,UEFI 规范之所以显得复杂,很大程度上是因为其中的不确定因素太多。如果你正在阅读 UEFI 规范,那么先做好心理准备,然后前往 EFI_DEVICE_PATH_PROTOCOL 一节。但是请注意,这个协议是通用的,虽然这个协议不涉及启动过程,但是有其他作用——这实际上就是 UEFI 官方的设备标识方法,这种标识方法可用于启动管理器项以及各种其他用途。出于各种原因,并不是每一种潜在的 EFI 设备都像 UEFI 启动管理器项一样起作用(如果你想从视频适配器启动,很可能不会成功)。但是启动菜单中显然可以包含指向 PXE 服务器(而不是磁盘分区)的项。UEFI 规范进行了多项规定,可以向 UEFI 启动管理器配置中添加除磁盘以外的启动目标。
但是对我们而言,只需要考虑连接到计算机的一般磁盘即可。既然这样,我们来讨论下可能遇到的三种启动项类型。
BIOS 兼容启动项
在本示例中,Boot0000 和 Boot0004 实际上是 BIOS 兼容模式启动项,而不是原生 UEFI 启动项。这些启动项不是通过外部工具添加到 UEFI 启动管理器配置中的,而是由固件本身生成的——这也是 UEFI 固件实现 BIOS 兼容启动的常见方式,通过生成 UEFI 启动管理器项,可触发指定设备的 BIOS 启动。至于 UEFI 启动管理器如何呈现给用户,这是另一个问题,我们稍后讨论。根据具体固件及其配置,其中有些项可能无法显示。每一项只会具有一个名称(“CD/DVD Drive”、“Hard Drive”),这表示“如果选中此项,那么就以 BIOS 兼容模式启动本磁盘”(其中,对于 Boot0000,“本磁盘”为 3,0,00,对于 Boot0004,“本磁盘”为 2,0,00)。
回退路径 (Fallback path) UEFI 原生启动项
Boot0001 项(我虚构的,实际操作中可能不存在,这里只是为了举例说明)用于通知固件尝试从特定磁盘启动(以 UEFI 模式而不是 BIOS 兼容模式),但是并没有向固件提供其他信息。它没有指定磁盘上的具体启动目标,而只是让固件启动磁盘。
UEFI 规范定义了一种“回退”路径 (Fallback path),用于启动此类启动管理器项,其工作原理类似于 BIOS 驱动器启动:它会在标准位置查找某些启动装载程序代码。但是其中的细节和 BIOS 不同。
当尝试以这种方式启动时,固件真正执行的操作相当简单。固件会遍历磁盘上的每个 EFI 系统分区(按照磁盘上的分区顺序)。在 ESP 内,固件将查找位于特定位置的具有特定名称的文件。在 x86-64 PC 上,固件会查找文件 \EFI\BOOT\BOOTx64.EFI
。固件实际查找的是 \EFI\BOOT\BOOT{计算机类型简称}.EFI
,其中,“x64”是 x86-64 PC 的“计算机类型简称”。文件名还有可能是 BOOTIA32.EFI
(x86-32)、BOOTIA64.EFI
(Itanium)、BOOTARM.EFI
(AArch32,即32位ARM)和 BOOTAA64.EFI
(AArch64,即64位ARM)。然后,固件将执行找到的第一个有效文件(当然,文件需要符合UEFI规范中定义的可执行格式)。
这种机制的设计目的不在于启动日常使用的操作系统。它的设计目的更像是为了启动可热插拔、与设备无关的介质(如 Live 映像和操作系统介质)。这也是这种机制的常见用途。如果查看 Linux 或其他操作系统的 UEFI 兼容 Live 或安装介质,你会发现其中包含 GPT,以及位于(或靠近)设备起始位置的 FAT 分区,该分区的 GPT 分区类型标识为 EFI 系统分区。在那个分区中,会有一个 \EFI\BOOT
目录,目录中至少包含上述特殊命名的文件之一。当以原生 UEFI 模式启动 Fedora Live 或安装介质时,就会采用这种机制。BOOTx64.EFI(或其他)文件将处理剩余启动过程,从而启动介质上包含的真正操作系统。
完全原生 UEFI 启动项
Boot0002 和 Boot0003 是存储设备上所安装操作系统的“典型”项。这些项显示了 UEFI 启动机制的全部优势,不仅仅是“从此磁盘启动”,而是“启动此特定磁盘上此特定位置中的这一特定启动装载程序”。
Boot0002 是由原生 UEFI Fedora 安装生成的启动项。Boot0003 是由原生 UEFI OpenSUSE安装生成的启动项。按照字面意思,这些启动项表示“从此分区加载这一文件”。分区指的是 HD(1,800,61800,6d98f360-cb3e-4727-8fed-5ce0c040365d) 这个东西:表示某一特定分区(使用 EFI_DEVICE_PATH_PROTOCOL,我不打算对此进行详细介绍。如果你通过固件界面和 efibootmgr 与启动管理器进行交互,你也不需要知道其中的细节)。文件指的是 (\EFI\opensuse\grubx64.efi) 这个东西:它仅表示“加载所述分区上此位置中的文件”。这里所指的分区基本上始终指的就是充当 EFI 系统分区的那个分区,因此:可以放心地让固件访问 EFI 系统分区。
UEFI 规范提供这一机制,以便操作系统可启动:操作系统将启动装载程序(作用为加载操作系统内核等)安装到 EFI 系统分区中,并使用某一名称(显然,这一名称通常来源于操作系统名称)以及启动装载程序(EFI 可执行格式,用于加载操作系统)的位置向 UEFI 启动管理器配置中添加启动项。
Linux 发行版使用 efibootmgr
工具处理 UEFI 启动管理器。进行原生 UEFI 安装时,有关启动装载方面,Linux 发行版实际进行的操作相当简单:它会创建一个 EFI 系统分区(如果不存在此分区),使用相应配置将 EFI 启动装载程序(通常为 grub2-efi
,但是也有例外)安装到 EFI 系统分区中的正确路径下,然后调用 efibootmgr 添加相应的 UEFI 启动管理器项(指向其启动装载程序)。如果已存在 EFI 系统分区,大部分发行版会使用现有分区(尽管完全可以创建新的 EFI 系统分区并使用这个新分区):我们已经提到过,UEFI 是一种宽松规范,只要在逻辑上遵循其设计,那么有多少个 EFI 系统分区都没问题。
配置启动过程(固件 UI)
上文描述了 UEFI 规范定义的基本机制,用于管理 UEFI 启动过程。固件用户界面可能不会明确遵循这一机制,了解这一点非常重要。不幸的是,UEFI 规范有意未限制启动过程的呈现方式或用户配置启动过程的方式,这表示——由于我们也从事 固件工程——每个固件会有不同的实现方法,并且其中某些固件的实现方法较疯狂。
许多固件的启动配置界面较直观。优秀的固件设计至少会显示启动顺序以及其中的各个启动项,允许用户添加/删除项、更改启动顺序或在某次特定启动中忽略原有启动顺序(仅针对那次启动生效,或直接让固件启动特定菜单项,甚至可以选择让固件以 BIOS 兼容模式
或 UEFI“回退 (Fallback)”模式
“启动这块磁盘”,我的固件就可以这么操作)。此类界面通常可以仅按名称显示完整的原生 UEFI 启动项(例如我们上文提到的 Fedora 和 OpenSUSE 示例);你需要检查 efibootmgr -v 的输出,以详细了解在调用这些项时,它们具体会尝试并执行哪些操作。
某些固件会尝试对配置进行抽象和简化,最终结果良莠不齐。例如,如果可以选择“启用或禁用”BIOS 兼容模式,固件很有可能会为已连接驱动器的 UEFI 启动管理器配置添加或删除 BIOS 兼容项。如果可以选择“启用或禁用”原生 UEFI 启动,那么在用户“禁用”原生 UEFI 启动时,固件很有可能更改 UEFI 启动管理器配置,从 BootOrder 中删除所有原生UEFI启动项。
请谨记,固件界面中的所有配置选项所执行的操作就是在后台配置 UEFI 启动管理器的行为。如果你能理解以上所有内容,那么当你更改固件界面中的选项时,你会更容易理解其背后的本质。
在 BIOS 中,系统不会始终尝试优先从可移动驱动器(CD、USB)进行启动,然后再从驱动器启动。根据实际情况,结果可能有所不同。有些 BIOS 固件会优先尝试从 CD 启动,然后再尝试从硬盘启动(而不是 USB)。试图安装新的操作系统时,用户已习惯于时常检查 BIOS 配置,以确保启动顺序“正确无误”。
UEFI 也是如此,但是由于 UEFI 启动管理器机制的灵活性/复杂性,这一过程看起来可能显得陌生而可怕。
在系统尝试启动固定启动项之前,如果想要确保系统使用“回退(Fallback)”机制优先从可移动设备启动(例如,在安装 Fedora 时),需要将可移动设备作为固件的默认启动项,或需要相应设置固件。根据具体固件界面,可能发现每个连接的可移动设备都有对应的“菜单项”,你只需要调整启动顺序,把你想要的可移动设备放在首位即可,有时候你也会发现可以直接请求“对此特定磁盘进行 UEFI 恢复启动”,另外你还可能发现固件会尝试将配置进行抽象。我们不知道具体的固件界面是什么样,因此难以编写说明。但是既然你已了解背后的工作原理,那么就可能更容易理解固件用户界面配置的含义。
配置启动过程(通过操作系统)
如上所述,与 BIOS 机制不同,你可以从操作系统层面配置 UEFI 启动过程。如果你的固件比较令人恶心,你可能需要执行此操作才能达成目的。
你可以使用之前提过的 efibootmgr 工具来添加、删除和修改 UEFI 启动管理器配置中的项,这一工具也具有其他丰富功能。你可以更改启动顺序。你可以更改下次启动时的首要启动项,而不需要使用 BootOrder 列表(如果你或其他某些工具已经进行过配置,efibootmgr -v 的输出将包括 BootNext 项,说明下一次启动将加载的菜单项)。Windows 下也有类似的工具。因此如果你难以从固件界面配置 UEFI 启动,但是你可以启动某种原生 UEFI 操作系统,那么你可以考虑从操作系统(而不是固件 UI)进行启动配置。
结论:
UEFI 固件包含某些非常类似于启动菜单的内容。
可以使用
efibootmgr -v
从任何原生 UEFI 启动的 Linux 操作系统中查询 UEFI 启动配置,也可以使用 efibootmgr 更改配置(有关详细信息,请参阅 man 页面)。“启动菜单”可以包含表示“以 BIOS 兼容模式启动此磁盘”,“通过回退路径 (Fallback path) 以原生 UEFI 模式启动此磁盘”(将使用上文所述的“寻找 BOOT(某字符串).EFI”方式),或“启动此特定位置(几乎始终为 EFI 系统分区)中的特定 EFI 格式的可执行文件”等含义的项。
UEFI 规范尝试一种优秀、简洁的设计,让所有操作系统都将其自身的启动装载程序安装到 EFI 系统分区中,然后在“启动菜单”中添加指向这些启动装载程序的项,同时不得干涉其他目标的启动过程。
你的固件 UI 可以自由实现此机制,虽然具体的实现结果良莠不齐。
在 UEFI 计算机上安装操作系统
我们快速浏览下上文中与在 UEFI 计算机上安装操作系统相关的具体结果。
原生 UEFI 启动和 BIOS 兼容启动
用户有时会忽略以下事项:
如果以“原生 UEFI”模式启动安装介质,安装介质将以原生 UEFI 模式安装操作系统:它将尝试向 EFI 系统分区写入 EFI 格式的启动装载程序,并尝试向 UEFI 启动管理器的“启动菜单”中添加启动项,用于启动该启动装载程序。
如果以“BIOS 兼容”模式启动安装介质,安装介质将以 BIOS 兼容模式安装操作系统:它将尝试向磁盘上的 MBR 空间写入 MBR 类型的启动装载程序。
这适用于(现在暂时忽略其中的无关警告)我接触过的所有操作系统。因此你可能确实想了解,如何在固件层选择以原生 UEFI 模式启动可移动设备,以及如何在固件层选择以 BIOS 兼容模式启动可移动设备,确保在安装时可以随意选择需要使用的模式。
如果以 BIOS 兼容模式启动安装介质,那么你绝对无法成功进行操作系统的原生 UEFI 安装,因为安装程序无法配置 UEFI 启动管理器(除非以原生 UEFI 模式启动安装介质)。
理论上,在以原生 UEFI 模式启动之后,操作系统的安装程序可通过 BIOS 模式安装该操作系统,即,将启动装载程序写入磁盘 MBR,但是大部分安装程序无法执行此操作,这种做法比较可取。
确定启动模式
有时候,在启动操作系统安装程序之后,你不确定启动模式为原生 UEFI 模式还是 BIOS 兼容模式。别担心。有几种简单方法可以确定启动模式。最简单的方法之一是尝试读取 UEFI 启动管理器。如果你启动了 Linux 安装程序或环境,并且可以运行 shell(例如,在 Fedora 安装程序中是 Ctrl-Alt-F2),请运行 efibootmgr -v
。如果你启动的是原生 UEFI 模式,那么就可以看到上文所示的 UEFI启动管理器配置。如果你启动的是 BIOS 兼容模式,那么会看到类似以下内容:
1 | Fatal: Couldn't open either sysfs or procfs directories for accessing EFI variables. |
如果启动了其他操作系统,你可以尝试运行该操作系统的内置实用程序,读取 UEFI 启动管理器,并查看是否显示了明确输出或类似错误。或者你可以检查系统日志并搜索“efi”和/或“uefi”,从中可能发现蛛丝马迹。
启用原生 UEFI 启动
若要启用原生 UEFI 模式的启动,那么操作系统安装介质必须明确符合我们刚刚说明的所有规范:具有 GUID 分区表,和 EFI 系统分区,启动装载程序位于正确的“回退”路径 (Fallback path) 中——\EFI\BOOT\BOOTx64.EFI(其他平台可能会有其他名称)。如果无法以原生 UEFI 模式启动安装介质,并且无法查出原因,那么请检查安装介质是否满足上述条件。显然,当使用 livecd-iso-to-disk 工具将 Fedora 映像写入 USB 存储器时,你必须传递 --efi
参数,才能将存储器配置为可用 UEFI 模式启动。
强制使用 BIOS 兼容启动
如果你的固件难以通过 BIOS 兼容模式从可移动介质启动,但是你又确实想通过这种方式启动,那么可以使用一些小把戏:完全禁用该介质的原生 UEFI 启动模式。可以通过清除所有 EFI 系统分区来轻松执行此操作(或者,如果使用 livecd-iso-to-disk 从 Fedora 映像创建 USB存储器,那么只需去掉 --efi
参数,存储器就会变为不可通过 UEFI 模式启动)。如果执行完此操作以后,你的固件仍然无法以 BIOS 兼容模式启动介质,那么就去吐槽你的固件供应商吧(如果还没吐槽过)。
磁盘格式(MBR vs. GPT)
其他注意事项如下:
如果想执行“BIOS 兼容”类型的安装,那么需要安装到 MBR 格式的磁盘。
如果想执行原生 UEFI 安装,那么需要安装到 GPT 格式的磁盘。
当然,为了给用户找不自在,许多固件可以通过 BIOS 模式从 GPT 格式的磁盘启动。事实上,从技术层面而言,也要求 UEFI 固件能从 MBR 格式的磁盘以 UEFI 模式启动(虽然无法保证)。但是你应当尽可能避免这种情况。这些注意事项非常重要,因为许多用户都曾深受其害。例如,以原生 UEFI 模式启动操作系统安装程序,然后试图直接安装到 MBR 格式的磁盘是非常不明智的。很有可能失败。多数现代操作系统安装程序将把磁盘自动重新格式化为正确格式(如果你允许安装程序彻底清除磁盘数据),但是,如果你尝试让安装程序“对此 MBR 格式的磁盘执行原生 UEFI 安装,并且不要重新格式化这块磁盘,因为上面有重要数据”,那么就很有可能失败,尽管技术层面而言,UEFI 规范提到了这种配置。具体而言,至少 Windows 和 Fedora 会明确禁止这种配置。
检查磁盘格式
你可以使用 parted 实用程序检查给定磁盘的格式:
1 | [adamw@adam Downloads]$ sudo parted /dev/sda |
注意到 Partition table: msdos
那一行了吗?这是一块 MBR/MS-DOS 格式的磁盘。如果是 GPT 格式的磁盘,会显示 gpt。你可以从 parted 中通过执行 mklabel gpt 或 mklabel msdos 将磁盘重新格式化为其他类型分区表。这会破坏磁盘内容。
对于多数操作系统的安装程序而言,如果你采用的磁盘配置会清空目标磁盘的所有内容,那么根据执行的安装类型,安装程序就会自动使用最合适的配置重新格式化磁盘。但是如果你想使用现有磁盘而不格式化,那么你需要检查该磁盘的格式并三思而后行。
执行手动分区时处理 EFI 系统分区
我只能针对 Fedora 给出权威建议,但是其中的主要内容可能也适用于其他发行版/操作系统。
执行原生 UEFI 安装,并且采用 GPT 格式的磁盘时,或者允许 Fedora 重新格式化磁盘(通过删除所有现有分区)时,如果允许 Fedora 自动处理分区,那么 Fedora 就会自动处理 EFI 系统分区。
但是,如果使用自定义分区,Fedora 会要求指定 EFI 系统分区,以供安装程序使用。如果不执行此步骤,安装程序会报错(错误消息的含义不明)并拒绝启动安装。
因此,如果执行原生 UEFI 安装并使用自定义分区,需要确保类型为“EFI 系统分区”的分区已挂载到 /boot/efi(这是 Fedora 查找 EFI 系统分区的路径)。如果系统上存在现有 EFI 系统分区,那么仅需将其挂载点设置为 /boot/efi 即可。如果还没有 EFI 系统分区,那么请创建一个分区,将其类型设置为 EFI 系统分区,大小至少为 200MB
(建议 500MB
),然后将其挂载点设置为 /boot/efi
。
缺陷
上文解释了 UEFI 的启动原理(至少解释得差不多了)。我这种描述方法应该还可以吧?
但是,UEFI 并不完美,也有许多问题。
细心的读者可能已经留意,我曾经提到过,UEFI 规范提供了一种机制。这种说法很严谨,也很重要。由于 UEFI 规范是一种“广泛共识”,因此其主要缺点之一(就特定方面而言)是并未提供具体实现。
如果仔细阅读 UEFI 规范,就会发现 UEFI 规范的基本方式是定义 UEFI 兼容固件必须支持的一系列功能。但是 UEFI 规范并没有严格规定这些功能的具体实现方法。
因此,UEFI 规范只要求系统固件必须遵循其中描述的所有内容,以便满足 UEFI 兼容固件的要求。但是,规范本身未规定操作系统“应该”或“必须”怎么做,并且 UEFI 规范也没有规定固件不得支持(或者不期望支持)的功能。换言之,在制定 UEFI 固件时,需要支持 GPT 格式的磁盘和 FAT 格式的 EFI 系统分区,并且必须以标准格式读取 UEFI 启动管理器项等等——但是也可以随意添加其他未规定的功能。
从 UEFI 规范中不难发现其中的隐喻——UEFI 规范仔细设置了一种良好机制,用于在固件层处理操作系统(或其他启动项)选择。但是 UEFI 规范并不要求一定要这么做,其他广受赞誉的规范也没有类似规定。
因此,在实际使用时,我们可能遇到各种复杂情况。例如,Apple Mac 的 HFS+ 分区中随附了某些启动装载程序。UEFI 规范提到,UEFI 兼容固件必须支持特定 GPT 分区类型的 UEFI FAT 分区(标识为“EFI 系统分区”),但是 UEFI 规范并没有提到固件不能识别其他文件系统类型并从中加载启动装载程序。(此类分区是否应视为“EFI 系统分区”,这很难回答,在此不做探讨。)
要是所有厂商都能按照 UEFI 规范严格使用 EFI 系统分区,那就不会有这么多问题了。但是 Apple 毕竟是 Apple,它的产品设计领先于其他厂商,率先设计出了可以从 HFS+ 分区读取和加载代码的固件,导致现在其他厂商不得不紧随 Apple 的脚步,除非他们不打算支持 Mac。在启动过程设计中,Apple 进行的工作远超出 UEFI 规范的范围,因此,如果你想让其他操作系统以美观的图标或其他形式显示在 Mac 的图形启动菜单上,你所要做的操作将超出 UEFI 规范的建议范围。
安全启动 (Secure Boot)
我们最后要介绍的,就是安全启动 (Secure Boot)。
安全启动 (Secure Boot) 并不神奇,也不复杂。才怪。安全启动 (Secure Boot) 复杂得要命,但是其理论并不复杂。安全启动 (Secure Boot) 本身也并不邪恶。事实就是如此,你也应当认同这一事实,除非你认为GPG也有恶意。
在 UEFI 规范(2.4A 版本)的第 28 章对安全启动 (Secure Boot) 进行了定义。这种机制事实上非常明智。但是其原理却非常简单。UEFI 规范规定固件可以包含一系列签名,并拒绝运行未签名或签名与固件中包含的签名不一致的 EFI 可执行文件
。
实际使用安全启动 (Secure Boot)
有关安全启动 (Secure Boot) 的所有不满并不针对安全启动 (Secure Boot) 机制本身——虽然发出这些不满的人可能不这么认为——而是针对安全启动 (Secure Boot) 在实际操作中的特定实现方式。
我们唯一在意的是,对于预装 Windows 8 或更高版本 Windows 的 PC 而言,安全启动 (Secure Boot) 是默认开启的。
微软将这些称为“Windows 硬件认证要求”。这些要求并不是什么绝密内容,所有人都可以在互联网上阅读。
你最好读一遍,但是我对其内容进行了总结。
符合微软认证要求的计算机必须满足以下条件:
默认启用安全启动 (Secure Boot)(服务器除外)
在其信任密钥列表中包含微软的密钥
启用安全启动 (Secure Boot) 时,必须禁用 BIOS 兼容模式(如果没记错的话,UEFI 规范也有此要求)
支持签名黑名单
符合微软认证要求的 x86 计算机 还必须满足以下附加条件:
允许 自然人禁用安全启动 (Secure Boot)
允许 自然人启用自定义模式,以及修改固件的信任密钥列表
符合微软认证要求的 ARM 计算机 还必须满足以下附加条件:
不允许 自然人禁用安全启动 (Secure Boot)
不允许 自然人启用自定义模式,以及修改固件的信任密钥列表
建议
以下内容是我在管理系统启动方面的一般建议,不保证其准确性、可靠性或安全性。
如果可以的话,每台计算机只安装一个操作系统。如果你需要一个以上操作系统,那就多买几台计算机,或使用虚拟机。这么做的话,事情就简单多了,而且无论你的固件是 BIOS 或 UEFI,或在 UEFI 系统上使用 BIOS 兼容启动,都没什么关系了。你在使用计算机时也会轻松许多。
如果你确实需要在每台计算机上安装多个操作系统,那么请在每块磁盘上至少安装一个操作系统。如果你比较熟悉 BIOS 启动,而且也不需要安全启动 (Secure Boot) 功能,在这种情况下,对于 UEFI 系统,请优先使用 BIOS 兼容启动。这样一来,可能不会有那么多麻烦,也不会造成数据丢失。如果每块磁盘只安装一个操作系统,那么你也可以混合使用原生 UEFI 和 BIOS 兼容模式。
如果你坚持要在每块磁盘上安装多个操作系统,那么请先理解本文所写内容。这么做无异于自作孽,不可活,出了问题可别责怪操作系统供应商。另外,在这种情况下,也不要混用原生 UEFI 和 BIOS 兼容模式,否则就是雪上加霜。
如果你在使用 UEFI 原生启动,并且不打算自己编译内核/内核模块或在 Linux 上使用 NVIDIA/ATI 私有驱动程序,那么最好启用安全启动 (Secure Boot)。这不会有什么副作用,反而可以带来额外的安全性,用以应对某些卑鄙的攻击类型(尽管目前很少被利用)。
如果打算编译内核/内核模块或使用 NVIDIA/ATI 私有驱动程序,那就最好禁用安全启动 (Secure Boot)。或者你可以启用安全启动 (Secure Boot),然后阅读有关配置信任链和对内核/内核模块签名的说明。但是这一过程至少需要好几天。
不要在 MBR 格式的磁盘上进行原生 UEFI 安装,也不要在 GPT 格式的磁盘上进行 BIOS 兼容安装(如果没记错的话,除非你的磁盘大小大于 2.2TB,因为 MBR 格式无法识别那么大的磁盘。如果想在那么大的磁盘上进行 BIOS 兼容安装,那么你可能会卡在 BIOS+GPT 的组合上。虽然这种组合可以正常运行,但是有点不靠谱,而且会牵涉到臭名昭著的“BIOS Boot 分区”)。