文章目录
- 前言
- 一、UEFI
- 二、Disk device compatibility
- 2.1 GPT 磁盘分区表
- 2.1.1 简介
- 2.1.2 Linux
- 2.2 ESP(EFI) 文件系统
- 2.2.1 简介
- 2.2.2 Linux
- Linux Kernel EFI Boot Stub
- 三、UEFI + GPT + grub2
- 3.1 简介
- 3.2 引导方式
- 3.3 BOOTX64.EFI
- 3.4 shimx64.efi
- 3.5 grubx64.efi
- 四、安全启动
- 4.1 简介
- 4.2 Linux
- 参考资料
前言
本文以 centos 7 (3.10.0 ),x86_64平台为例。
固件保存在主板上的 ROM 中,是计算机上电后执行的第一段程序。固件启动阶段的核心功能包括硬件初始化、硬件自检和加载 bootloader,最终会把控制权转交给给 bootloader。
固件启动阶段主要有两种启动方式:Legacy BIOS 和 UEFI。
Linux系统是使用UEFI还是BIOS启动,查看/sys/firmware/efi是否存在:
# ls /sys/firmware/efi
config_table efivars esrt fw_platform_size fw_vendor runtime runtime-map systab vars
一、UEFI
Unified Extensible Firmware Interface(UEFI)统一可扩展固件接口是一种规范,定义了用于启动计算机硬件的平台固件的架构,以及其与操作系统进行交互的接口。
UEFI取代了BIOS,后者存在于所有兼容IBM PC的个人计算机的引导ROM中,尽管它可以通过CSM引导提供与BIOS的向后兼容性。
与其前身BIOS相反,BIOS最初由IBM作为专有软件创建的事实上的标准,UEFI是由一个行业联盟维护的开放标准。
由EFI规范定义的接口包括包含平台信息的数据表,以及可供操作系统加载程序和操作系统使用的引导和运行时服务。UEFI固件相对于BIOS提供了几个技术优势:
(1)能够引导包含大容量分区(超过2 TB)的磁盘,使用GUID分区表(GPT - GUID Partition Table)进行分区。
(2)灵活的预操作系统环境,包括网络功能、图形用户界面(GUI)和多语言支持。
(3)支持32位(例如IA-32、ARM32)或64位(例如x64、AArch64)的预操作系统环境。
(4)使用C语言进行编程。
(5)使用Python解释器进行UEFI Shell的Python编程。
(6)模块化设计,允许固件和驱动程序的灵活组合和替换。
(7)向后和向前的兼容性,可以与旧版本的EFI固件和新版本的UEFI固件兼容。
(8)安全性:BIOS不包含内置的安全措施,例如安全启动(Secure Boot)。EFI支持安全启动,可以防止未经授权的操作系统或恶意软件篡改启动过程。
二、Disk device compatibility
2.1 GPT 磁盘分区表
2.1.1 简介
除了使用主引导记录(MBR)的标准PC磁盘分区方案外,UEFI还与GUID分区表(GPT)分区方案配合使用,GPT分区方案摆脱了MBR的许多限制。特别是,MBR对磁盘分区数量和大小的限制(每个磁盘最多四个主分区,每个磁盘最大容量为2 TB,即2 × 240字节)得到放宽。更具体地说,GPT允许最大磁盘和分区大小达到8 ZiB(8 × 270字节)。
GPT分区方案相比MBR具有更大的灵活性和扩展性,可以支持更大容量的磁盘和更多的分区。它还提供了更好的数据完整性和可靠性,支持磁盘自我修复和冗余存储。因此,在使用UEFI的系统中,可以利用GPT分区方案来充分利用大容量磁盘和更多的分区布局选项。
GUID Partition Table(GPT)是一种用于物理计算机存储设备(如硬盘驱动器或固态硬盘)分区表布局的标准,它使用全局唯一标识符(UUID)或全局唯一标识符(GUID)来标识分区。作为统一可扩展固件接口(UEFI)标准的一部分(作为PC BIOS的替代方案由统一EFI论坛提议),GPT也被一些BIOS使用,这是由于主引导记录(MBR)分区表的限制,MBR分区表使用32位用于传统512字节磁盘扇区的逻辑块寻址(LBA)。
所有现代个人计算机操作系统都支持GPT。其中一些,包括基于x86架构的macOS和微软Windows,仅支持在具有EFI固件的系统上从GPT分区引导,但FreeBSD和大多数Linux发行版可以在具有BIOS或EFI固件接口的系统上从GPT分区引导。
如下图所示:
与MBR类似,GPT使用逻辑块寻址(LBA)代替历史上的柱面-磁头-扇区(CHS)寻址。保护性MBR存储在LBA 0处,GPT头部存储在LBA 1处,并在最后一个LBA处备份GPT头部。GPT头部包含对分区表(分区项数组)的指针,通常位于LBA 2处。每个分区表上的条目大小为128字节。UEFI规范规定,无论扇区大小如何,分区项数组都分配了至少16,384字节的空间。因此,在具有512字节扇区的磁盘上,至少使用32个扇区用于分区项数组,而第一个可用块位于LBA 34或更高位置;而在具有4,096字节扇区的磁盘上,至少使用4个扇区用于分区项数组,而第一个可用块位于LBA 6或更高位置。
2.1.2 Linux
在Linux中启用对GPT的支持需要在内核配置期间打开CONFIG_EFI_PARTITION选项(EFI GUID分区支持)。此选项允许Linux在系统固件将系统控制权交给Linux后,识别和使用GPT磁盘。
# cat /boot/config-3.10.0-693.el7.x86_64 | grep CONFIG_EFI_PARTITION
CONFIG_EFI_PARTITION=y
为了向后兼容,Linux可以在基于BIOS的系统中使用GPT磁盘进行数据存储和引导,因为GRUB 2和Linux都支持GPT。这样的设置通常被称为BIOS-GPT。由于GPT包含保护性MBR,基于BIOS的计算机可以使用存储在保护性MBR引导代码区域中的GPT感知引导加载程序从GPT磁盘引导。对于GRUB而言,这样的配置需要一个BIOS引导分区,以便GRUB嵌入其二级代码,因为GPT分区磁盘中不存在MBR之后的空隙(该空隙被GPT的主头和主分区表占用)。这个分区通常为1 MB大小,在GPT方案中,该分区的全局唯一标识符(GUID)为21686148-6449-6E6F-744E-656564454649,仅在BIOS-GPT设置中由GRUB使用。从GRUB的角度来看,在MBR分区方案中不存在这种分区类型。如果系统是基于UEFI的,则不需要此分区,因为在这种情况下不需要嵌入第二阶段代码。
UEFI系统可以访问GPT磁盘并直接从中引导,这允许Linux使用UEFI引导方法。在UEFI系统上从GPT磁盘引导Linux涉及创建EFI系统分区(ESP),其中包含UEFI应用程序,如引导加载程序、操作系统内核和实用工具。这样的设置通常被称为UEFI-GPT,建议ESP的大小至少为512 MB,并且使用FAT32文件系统进行格式化以实现最大的兼容性。
为了向后兼容,一些UEFI实现还通过兼容性支持模块(CSM)支持从MBR分区的磁盘引导,该模块提供了传统BIOS兼容性。在这种情况下,在UEFI系统上引导Linux与在传统基于BIOS的系统上引导相同。
2.2 ESP(EFI) 文件系统
2.2.1 简介
EFI(可扩展固件接口)系统分区或ESP是存储设备(通常是硬盘驱动器或固态硬盘)上的一个分区,用于具有统一可扩展固件接口(UEFI)的计算机。当计算机启动时,UEFI固件加载存储在ESP上的文件以启动操作系统和各种实用程序。
ESP包含已安装操作系统的引导加载程序、引导管理器或内核映像(通常包含在其他分区中),用于在启动时由固件使用的硬件设备的设备驱动程序文件,旨在在引导操作系统之前运行的系统实用程序,以及数据文件,如错误日志。
EFI系统分区使用基于FAT文件系统的文件系统进行格式化,其规范基于UEFI规范并作为其一部分进行维护;因此,文件系统规范独立于原始的FAT规范。在GUID分区表(GPT)方案中,EFI系统分区的全局唯一标识符(GUID)是C12A7328-F81F-11D2-BA4B-00A0C93EC93B,而在主引导记录(MBR)分区表方案中,其标识符为0xEF。无论是GPT还是MBR分区的磁盘都可以包含EFI系统分区,因为UEFI固件需要支持这两种分区方案。此外,还支持用于CD-ROM和DVD的El Torito引导格式。
UEFI通过将分区的第一个块(扇区)保留为兼容性代码来提供与传统系统的向后兼容性,实际上创建了一个传统的引导扇区。在传统基于BIOS的系统中,将分区的第一个扇区加载到内存中,并将执行权转移到该代码上。UEFI固件不会执行MBR中的代码,除非通过兼容性支持模块(CSM)以传统BIOS模式引导。
UEFI规范要求完全支持MBR分区表。然而,某些UEFI实现在检测到引导磁盘上的某些类型的分区表时立即切换到基于BIOS的CSM引导,从而有效地阻止了从包含在MBR分区磁盘上的EFI系统分区进行UEFI引导。
UEFI固件支持从可移动存储设备(如USB闪存驱动器)引导。为此,可移动设备使用FAT12、FAT16或FAT32文件系统进行格式化,同时需要根据标准ESP文件层次结构存储引导加载程序,或者通过向系统的引导管理器提供引导加载程序的完整路径。另一方面,固定驱动器上始终期望FAT32文件系统。
2.2.2 Linux
GRUB 2、elilo和systemd-boot是传统的、功能齐全的独立UEFI引导管理器(也称为引导加载管理器),用于Linux。一旦被UEFI固件加载,它们可以访问和引导来自它们所支持的所有设备、分区和文件系统的内核映像,而不仅限于EFI系统分区。
EFI系统分区的挂载点取决于所使用的引导加载器。较旧的引导加载器如GRUB 2和lilo/elilo默认为/boot/efi。或者,systemd-boot更喜欢/efi或/boot而不是/boot/efi,这是因为嵌套的autofs挂载可能导致潜在的复杂问题。无论挂载点路径如何,其内容在Linux引导后是可访问的。
对于GRUB2:
tree /boot/efi/
/boot/efi/
└── EFI├── BOOT│ ├── BOOTX64.EFI│ ├── fallback.efi│ └── fbx64.efi└── centos├── BOOT.CSV├── BOOTX64.CSV├── fonts│ └── unicode.pf2├── fw├── fwupia32.efi├── fwupx64.efi├── grub.cfg├── grubenv├── grubx64.efi├── mmx64.efi├── MokManager.efi├── shim.efi├── shimx64-centos.efi└── shimx64.efi
Linux Kernel EFI Boot Stub
EFI Boot Stub使得在不使用传统UEFI引导加载器的情况下,可以引导Linux内核映像。通过伪装成PE/COFF可执行映像,并对固件呈现为UEFI应用程序,启用EFI Boot Stub的Linux内核映像可以直接由UEFI固件加载和执行。这样的内核映像仍然可以由基于BIOS的引导加载器加载和运行;因此,EFI Boot Stub允许单个内核映像在任何引导环境中工作。
Linux内核对EFI Boot Stub的支持通过在内核配置中打开CONFIG_EFI_STUB(EFI stub支持)选项来启用。它被合并到Linux内核主线版本的3.3版中,于2012年3月18日发布。
# cat /boot/config-3.10.0-693.el7.x86_64 | grep CONFIG_EFI_STUB
CONFIG_EFI_STUB=y
Systemd-boot是一个简单的UEFI引导管理器,它加载和运行配置的EFI映像,仅访问EFI系统分区。配置文件片段、内核映像和initrd映像需要驻留在EFI系统分区上,因为systemd-boot不提供访问其他分区或文件系统上的文件的支持。Linux内核需要使用CONFIG_EFI_STUB=y进行构建,以便可以直接作为UEFI映像执行。
通过EFI Stub,Linux内核可以被直接被编译成UEFI的app,可以直接被UEFI固件识别和启动,完全不需要借助第三方bootloader了。
在x86和ARM平台上,内核的zImage/bzImage可以伪装成PE/COFF映像,从而使EFI固件加载程序将其作为EFI可执行文件加载。修改bzImage头部的代码以及固件加载程序跳转到的EFI特定入口点共同被称为"EFI boot stub",它们分别位于arch/x86/boot/header.S和drivers/firmware/efi/libstub/x86-stub.c。对于ARM架构,EFI stub实现在arch/arm/boot/compressed/efi-header.S和drivers/firmware/efi/libstub/arm32-stub.c中。在不同架构之间共享的EFI stub代码位于drivers/firmware/efi/libstub目录下。
对于arm64架构,没有压缩内核的支持,因此Image本身伪装成PE/COFF映像,并且EFI stub链接到内核中。arm64的EFI stub位于drivers/firmware/efi/libstub/arm64.c和drivers/firmware/efi/libstub/arm64-stub.c中。
通过使用EFI boot stub,可以在没有传统EFI引导加载程序(如grub或elilo)的情况下引导Linux内核。由于EFI boot stub执行了引导加载程序的任务,在某种意义上它就是引导加载程序。
启用EFI boot stub需要使用CONFIG_EFI_STUB内核选项。
请参考:
https://www.kernel.org/doc/html/latest/admin-guide/efi-stub.html
https://zhuanlan.zhihu.com/p/28708585
三、UEFI + GPT + grub2
3.1 简介
bootloader 启动阶段的核心功能是加载内核和 initramfs,并将控制权交给内核。
Centos7 的 bootloader 为 GRUB 2,根据启动方式的不同,可分成两种引导方式:BIOS+MBR/GPT+grub2 和 UEFI+GPT+grub2。
从GPT分区磁盘引导UEFI系统通常被称为UEFI-GPT引导。
这里主要描述UEFI+GPT+grub2引导方式。
这种引导方式,固件 UEFI 从 ESP 分区文件系统下读取 bootloader。Centos ESP 分区文件系统采用 vfat,其目录结构如下:
# tree /boot/efi/EFI/
/boot/efi/EFI/
├── BOOT
│ ├── BOOTX64.EFI
│ ├── fallback.efi
│ └── fbx64.efi
└── centos├── BOOT.CSV├── BOOTX64.CSV├── fonts│ └── unicode.pf2├── fw├── fwupia32.efi├── fwupx64.efi├── grub.cfg├── grubenv├── grubx64.efi├── mmx64.efi├── MokManager.efi├── shim.efi├── shimx64-centos.efi└── shimx64.efi
与传统的PC BIOS不同,UEFI不依赖于引导扇区,而是在UEFI规范中定义了一个引导管理器。当计算机启动时,引导管理器会检查引导配置,并根据其设置执行指定的操作系统引导加载程序或操作系统内核(通常是引导加载程序)。引导配置由存储在NVRAM中的变量定义,包括指示操作系统加载程序或操作系统内核的文件系统路径的变量。
UEFI可以自动检测操作系统引导加载程序,这使得从可移动设备(如USB闪存驱动器)进行简单的引导成为可能。这种自动检测依赖于标准化的操作系统引导加载程序的文件路径,路径的格式取决于计算机架构。例如,在x86-64系统上,操作系统加载程序的文件路径为 /boot/efi/EFI/BOOT/BOOTX64.EFI,而在ARM64架构上为/boot/efi/EFI/BOOT/BOOTAA64.EFI。
# file /boot/efi/EFI/BOOT/BOOTX64.EFI
/boot/efi/EFI/BOOT/BOOTX64.EFI: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
# file /boot/efi/EFI/BOOT/BOOTAA64.EFI
/boot/efi/EFI/BOOT/BOOTAA64.EFI: PE32+ executable (EFI application) Aarch64 (stripped to external PDB), for MS Windows
3.2 引导方式
# tree /boot/efi/EFI/
/boot/efi/EFI/
├── BOOT
│ ├── BOOTX64.EFI
│ ├── fallback.efi
│ └── fbx64.efi
└── centos├── BOOT.CSV├── BOOTX64.CSV├── fonts│ └── unicode.pf2├── fw├── fwupia32.efi├── fwupx64.efi├── grub.cfg├── grubenv├── grubx64.efi├── mmx64.efi├── MokManager.efi├── shim.efi├── shimx64-centos.efi└── shimx64.efi
引导方式如下:
x86_64: uefi ==> shimx64.efi ==> grubx64.efi ==> /boot/efi/EFI/centos/grub.cfg ==> vmlinuz & initramfs
x86 架构系统中,shimx64.efi 和 grubx64.efi 是 UEFI 启动方式下的 bootloader,他们以文件的形式存在于磁盘的 /boot/efi/EFI/centos 目录下,UEFI 可以直接识别文件系统,并获取 shimx64.efi 加载到内存运行,然后 shimx64.efi 获取 grubx64.efi,grubx64.efi 则通过 grub.cfg 寻找 vmlinuz 和 initfamfs,并将其加载到内存中。
Centos 中 BOOTX64.EFI 是 shimx64.efi 的复制。第一次启动时,固件中无引导条目,默认会加载 ESP 分区下 EFI/BOOT/BOOTX64.EFI,BOOTX64.EFI 加载 fbx64.efi,fbx64.efi 根据 EFI/centos/BOOTX64.CSV 文件向固件注册引导项 Centos,并将引导程序指向 /EFI/centos/shimx64.efi,跳转到 shimx64.efi,shimx64.efi最终会加载 grubx64.efi,grubx64.efi根据 grub.cfg 读取并加载内核和 initramfs,并将控制权交给内核。 从第二次启动开始,因为固件中已注册了 Centos引导项,所以直接从 shimx64.efi 启动。
首次启动:
UEFI --> BOOTX64.EFI --> fbx64.efi --> shimx64.efi --> grubx64.efi --> grub.cfg --> vmlinuz & initramfs
非首次启动:
UEFI --> shimx64.efi --> grubx64.efi --> grub.cfg --> vmlinuz & initramfs
shim 的作用主要是安全启动,grubx64.efi 完成实际的引导。
在大多数Linux发行版中,用于UEFI启动的引导加载程序通常是shim.efi或者grubx64.efi。shim.efi是一个轻量级的引导加载程序,主要用于支持安全启动(Secure Boot)功能,并随后加载grubx64.efi(GRUB2),GRUB2是更为复杂的引导加载器,它可以加载Linux内核和初始化RAM磁盘(initrd/initramfs),进而启动操作系统。
3.3 BOOTX64.EFI
BOOTX64.EFI 是一个通用的引导加载程序文件,具有较高的权限,并可以在各种环境下启动计算机。无论计算机是否安装了操作系统,如果存在 BOOTX64.EFI 文件,UEFI 固件将使用它作为默认的引导加载程序。
BOOTX64.EFI 具有广泛的应用场景,可以用于各种目的,包括维护、安装计算机系统以及启动不同的操作系统。通过使用 BOOTX64.EFI,您可以进入各种模式,如 EFI Shell、ISO、Windows、Linux 等,从而在不同的环境下启动计算机。
BOOTX64.EFI 是一种UEFI引导加载程序文件,用于在64位x86架构的计算机上启动操作系统。它是与UEFI固件兼容的引导加载程序,负责加载操作系统内核并引导系统启动。
在UEFI系统中,BOOTX64.EFI通常被用作默认的引导加载程序。当计算机启动时,UEFI固件会查找并执行BOOTX64.EFI文件,从而启动操作系统。
BOOTX64.EFI通常与特定的操作系统或引导管理器相关联。例如,在Windows系统中,UEFI固件会加载BOOTX64.EFI来引导Windows操作系统。类似地,在某些Linux发行版中,UEFI固件也会加载BOOTX64.EFI来引导该发行版的内核和引导程序。
需要注意的是,BOOTX64.EFI的文件名可能因操作系统和计算机制造商而有所不同。不同的操作系统和引导环境可能使用不同的文件名来执行相同的功能。例如,某些Linux发行版可能使用grubx64.efi或shimx64.efi作为默认的引导加载程序文件。
BOOTX64.EFI文件是UEFI系统的启动管理器,它会在计算机启动时被加载到内存中,然后启动操作系统的安装程序或引导管理器。这个文件通常位于Linux ISO image的EFI目录下,它可以被用来启动UEFI系统安装程序或启动其他操作系统的引导管理器。
总结来说,BOOTX64.EFI是一种UEFI引导加载程序文件,用于在64位x86计算机上启动操作系统。它是与UEFI固件兼容的引导加载程序,负责加载操作系统内核并引导系统启动。具体的文件名可能因操作系统和计算机制造商而有所不同。
3.4 shimx64.efi
shimx64.efi 是一个用于 UEFI Secure Boot 的特殊引导加载程序。它在 Linux 系统中的 BOOT 阶段用于启动操作系统,并提供了 UEFI Secure Boot 的支持。
在启用 UEFI Secure Boot 的系统中,引导加载程序必须具有受信任的数字签名才能被固件信任并加载。然而,某些 Linux 发行版的引导加载程序(如 GRUB)可能没有预先被签名,因而无法通过 Secure Boot 的验证。
为了解决这个问题,UEFI 固件允许使用一个称为 “shim” 的中间引导加载程序。shimx64.efi 就是 shim 的一个典型实现,适用于 x86-64 架构的系统。
shimx64.efi 本身是由 UEFI 固件预先信任的,因此可以通过 Secure Boot 的验证。它的主要作用是加载并启动 GRUB 或其他 Linux 发行版的引导加载程序。由于 shimx64.efi 是经过签名并可信任的,它可以作为信任链的一部分,允许其他未签名的引导加载程序或内核模块被加载,并确保系统的启动过程是安全的。
因此,当使用 UEFI Secure Boot 启动 Linux 系统时,shimx64.efi 是作为引导加载程序使用的,它负责加载并启动其他组件,如 GRUB 或操作系统内核,以确保在启动过程中的安全性和完整性。
3.5 grubx64.efi
(1)
grubx64.efi 是 GRUB 引导加载程序的一个文件,用于在 UEFI 系统中启动操作系统。它是 GRUB 的 UEFI 版本,专门设计用于 x86-64 架构的计算机。
grubx64.efi是GNU GRUB引导管理器的UEFI版本,它是一种常用的引导程序,被广泛应用于Linux系统中。当计算机使用UEFI启动时,UEFI固件会查找EFI目录下的grubx64.efi文件,并将其加载到内存中。然后,grubx64.efi将会显示一个菜单,列出可用的操作系统和内核,允许用户选择要启动的操作系统或内核。在Linux ISO image中,grubx64.efi文件通常被用作引导管理器,用于启动Linux操作系统的安装程序。
在 UEFI 系统中,grubx64.efi 被用作主引导加载程序,负责加载操作系统内核和初始化 RAM 文件系统(initramfs)。它是由 GRUB 提供的一个可信的引导加载程序,通常与 shimx64.efi 结合使用,以支持 UEFI Secure Boot。
grubx64.efi 的配置文件通常位于 /boot/grub/grub.cfg 或 /etc/grub.d/ 目录中。在配置文件中,可以定义引导菜单,包括可供选择的操作系统、启动参数和其他设置。
GRUB 具有强大的功能和灵活性,可以处理多个操作系统的引导和配置。它支持多个文件系统,可以通过配置文件进行自定义和扩展。通过编辑和配置 grubx64.efi 的配置文件,可以修改引导菜单、添加新的启动选项、设置默认启动项等。
对于Linux发行版,grubx64.efi是GRUB2在UEFI环境下的引导加载程序。虽然Grub是开源软件,理论上可以编译和定制,但通常情况下,个人用户不是通过直接编辑.efi文件来配置GRUB,而是通过编辑其配置文件(如/boot/grub/grub.cfg)来更改引导菜单、内核选项等。如果确实需要修改源代码,那也是先修改源代码然后重新编译生成新的.efi文件。
总结来说,grubx64.efi 是 GRUB 引导加载程序的 UEFI 版本,用于在 UEFI 系统中启动操作系统。它与 shimx64.efi 结合使用,提供了强大的引导和配置功能,使用户能够灵活地管理和启动多个操作系统。
(2)
grubx64.efi文件是怎么找到grub.cfg文件的:
在UEFI系统中,grubx64.efi引导管理器启动后,它会搜索EFI系统分区中的特定目录和文件,以找到grub.cfg配置文件。具体来说,grubx64.efi通常会按照以下顺序搜索grub.cfg文件:
首先,grubx64.efi会在EFI目录下搜索grub.cfg文件。如果在EFI目录下找到了grub.cfg文件,它会直接加载并执行该文件。
如果在EFI目录下没有找到grub.cfg文件,grubx64.efi会继续搜索EFI目录下的/boot/grub目录。在该目录下,它会尝试查找grub.cfg文件,并加载执行该文件。
如果在/boot/grub目录下也没有找到grub.cfg文件,grubx64.efi会继续搜索EFI系统分区中的其他目录,包括/efi/{distro}/、/efi/boot/等,以查找grub.cfg文件。
一旦grubx64.efi找到了grub.cfg文件,它就会将文件加载到内存中,并根据文件中的配置信息启动相应的操作系统或内核。注意,grub.cfg文件的位置和名称可能因Linux发行版和安装方式而有所不同,但通常情况下,它们会遵循上述搜索规则。
四、安全启动
UEFI Secure Boot(UEFI 安全引导)是一种验证机制,用于确保固件启动的代码是可信的。
正确、安全地使用 UEFI Secure Boot 需要对每个在引导时加载的二进制文件进行验证,验证过程是通过与固件中的已知密钥进行比对。这些密钥标识了可信的供应商和二进制文件的来源,或者通过加密哈希算法识别的特定可信二进制文件。
大多数 x86 硬件出厂时都预装了微软的密钥。这意味着我们通常可以依赖这些系统中的固件信任由微软签名的二进制文件,而 Linux 社区在很大程度上依赖这个假设来实现 Secure Boot。例如,Red Hat 和 SUSE 也采用了相同的流程。
许多 ARM 和其他架构也支持 UEFI Secure Boot,但可能没有在固件中预装密钥。在这些架构上,可能需要使用由硬件所有者加载到固件中的证书重新签名引导映像。
4.1 简介
Secure Boot(安全启动)是一项在UEFI(统一可扩展固件接口)标准中的安全功能,旨在为预引导过程添加一层保护。它通过维护一个加密签名的二进制文件列表来验证引导时运行的程序是否经过授权或禁止,从而提高机器核心引导组件(引导管理器、内核、initramfs)未被篡改的可信度。
简单来说,所谓安全启动,就是主板厂商在 UEFI 的 ROM 中内置了一个公钥,操作系统发行方用对应的私钥,去给系统的内核,或者引导器签名。这样,系统启动的时候,UEFI 会用 ROM 中内置的对应公钥,去验证这个内核,或者引导器是否为发行方提供的。如果是,那么验证通过,可以正常启动;如果不是,那么启动就会被终止。
私钥签名,公钥验证。
UEFI规范定义了一种称为Secure Boot的协议(BIOS不具备这样的内置安全机制),可以通过阻止加载未使用可接受数字签名签名的UEFI驱动程序或操作系统引导加载程序来保护引导过程。并未指定驱动程序的签名具体的机械细节。当启用Secure Boot时,它最初处于“设置”模式,允许将称为“平台密钥”(PK)的公钥写入固件。一旦密钥被写入,Secure Boot进入“用户”模式,只有使用平台密钥签名的UEFI驱动程序和操作系统引导加载程序才能被固件加载。可以将其他证书添加到存储在内存中的数据库中的“密钥交换密钥”(KEK)中,以允许使用其他证书,但它们仍然必须与平台密钥的私有部分有关联。Secure Boot也可以设置为“自定义”模式,在该模式下可以向系统中添加不与私钥匹配的其他公钥。
在2011年,微软宣布获得其Windows 8操作系统认证的计算机必须预装微软的公钥并启用Secure Boot。随后,该公司被批评者和自由软件/开源倡导者(包括自由软件基金会)指责试图利用UEFI的Secure Boot功能阻碍或完全阻止安装诸如Linux等替代操作系统。微软否认Secure Boot要求旨在形成闭环,并通过明确规定,获得Windows 8认证的基于x86架构的系统必须允许Secure Boot进入自定义模式或被禁用,但在使用ARM架构的系统上不允许。Windows 10允许OEM厂商决定用户是否可以管理其x86系统的Secure Boot。
其他开发人员对在Linux系统中实现Secure Boot的法律和实际问题提出了担忧。前红帽开发人员Matthew Garrett指出,GNU通用公共许可证第3版的条款可能会阻止在未向发行版的开发人员披露私钥的情况下使用GNU GRand Unified Bootloader(不过,自由软件基金会已经澄清了其立场,保证提供密钥的责任由硬件制造商承担),而对于启用了Secure Boot的高级用户来说,构建能够正常工作的自定义内核而不进行自签名也是困难的。其他开发人员建议提供带有另一个密钥的已签名的Linux版本,但指出说服OEM厂商随同微软密钥一起提供所需密钥将会很困难。
几个主要的Linux发行版已经针对Secure Boot开发了不同的实现方式。Garrett本人开发了一个称为shim的最小化引导加载程序,它是一个预编译、已签名的引导加载程序,允许用户单独信任Linux发行版提供的密钥。Ubuntu 12.10使用一个较旧版本的shim,预先配置为使用Canonical自己的密钥,仅验证引导加载程序并允许加载未签名的内核。开发人员认为仅签名引导加载程序的做法更可行,因为一个可信任的内核只能保护用户空间,而不能保护Secure Boot旨在增加保护的预引导状态。这也允许用户构建自己的内核并使用自定义内核模块,而无需重新配置系统。Canonical还维护自己的私钥,用于签名预装在运行该操作系统的经过认证的OEM计算机上的Ubuntu安装,并且还计划强制执行Secure Boot要求,即需要在固件中包含Canonical密钥和微软密钥(出于兼容性原因)。Fedora也使用shim,但要求内核及其模块也必须签名。
关于操作系统内核及其模块是否必须签名存在争议;虽然UEFI规范并没有要求,但微软声称他们的合同要求如此,并保留撤销用于签署可能用于破坏系统安全性的代码的任何证书的权利。在Windows中,如果启用了Secure Boot,所有内核驱动程序必须经过数字签名;非WHQL驱动程序可能会被拒绝加载。2013年2月,另一位红帽开发人员试图提交一个补丁给Linux内核,使其能够解析由微软签2011年,微软宣布获得其Windows 8操作系统认证的计算机必须预装微软的公钥并启用Secure Boot功能。这一举措引起了批评者和自由软件/开源倡导者(包括自由软件基金会)的指责,称微软试图利用UEFI的Secure Boot功能限制或完全阻止安装其他操作系统(如Linux)。微软否认Secure Boot要求旨在形成闭环,并明确规定获得Windows 8认证的基于x86架构的系统必须允许Secure Boot进入自定义模式或禁用,但使用ARM架构的系统则不受此限制。Windows 10允许OEM厂商决定用户是否可以管理其x86系统的Secure Boot。
关于在Linux系统中实现Secure Boot的法律和实际问题引起了其他开发人员的关注。前红帽开发人员Matthew Garrett指出,GNU通用公共许可证第3版的条款可能会阻止在未向发行版开发人员披露私钥的情况下使用GNU GRand Unified Bootloader。不过,自由软件基金会已经澄清了其立场,确保提供密钥的责任由硬件制造商承担。对于启用了Secure Boot的高级用户来说,构建能够与Secure Boot启用一起使用的自定义内核而无需自签名也是困难的。其他开发人员提议提供带有另一个密钥的已签名Linux版本,但指出说服OEM厂商将所需密钥与微软密钥一起提供可能很困难。
许多主要的Linux发行版现在都支持UEFI Secure Boot,例如RHEL(RHEL 7及更高版本)、CentOS(CentOS 7及更高版本)、Ubuntu、Fedora、Debian(Debian 10及更高版本)、OpenSUSE和SUSE Linux。这些发行版采用了不同的Secure Boot实现方式,例如使用shim引导加载程序、签名引导加载程序和内核,以及允许自定义内核等。
4.2 Linux
mokutil 命令用于管理机主密钥(MOK),这些密钥由 shim 层用于验证 grub2和内核映像,也可用于验证安全启动是否启用。
验证安全启动是否启用:
# mokutil --sb-state
or
# bootctl
查看当前所有已注册的密钥:
# mokutil --list-enrolled
在Ubuntu上,除了initrd映像之外,所有预计作为引导过程的一部分加载的预构建二进制文件都由Canonical的UEFI证书签名。Canonical的UEFI证书被嵌入在由微软签名的shim加载程序中,因此本身隐式地受到信任。
在没有预加载来自微软的签名证书的架构或系统上,用户可以替换shim或grub上的现有签名,并根据其自己在系统固件中导入的证书进行验证。
系统引导时,固件根据固件BootEntry变量加载shim二进制文件。Ubuntu在安装时安装自己的BootEntry,并且在每次更新GRUB引导加载程序时可能会更新它。由于shim二进制文件由微软签名,因此在与固件中已存在的证书进行验证时,它被固件验证和接受。由于shim二进制文件嵌入了Canonical的证书以及自己的信任数据库,因此引导环境的其他元素除了被预加载在固件中的可接受证书之一签名外,还可以使用Canonical的UEFI密钥签名。
shim加载完成后,接下来加载的是第二阶段映像。这可以是两种情况之一:如果系统正常启动,则加载GRUB;如果需要进行密钥管理任务,则加载MokManager,这是由固件变量配置的(通常在之前运行系统时更改)。
如果正常引导,GRUB二进制文件(grub*.efi)将被加载,并尝试根据所有先前已知的受信任来源进行验证。Ubuntu的GRUB二进制文件由Canonical的UEFI密钥签名,因此成功通过验证,引导过程继续进行。
如果引导以执行密钥管理任务,将加载MokManager二进制文件(mm*.efi)。通过由shim签名的临时密钥对MokManager二进制文件进行了明确的信任。这意味着只有与特定shim二进制文件一起构建的MokManager二进制文件才被允许运行,并且限制了使用受损工具的可能性。MokManager允许在系统控制台上的任何用户注册密钥、删除受信任的密钥、注册二进制哈希并在shim级别切换Secure Boot验证,但大多数任务需要输入先前设置的密码以确认控制台上的用户确实是请求更改的人。这些密码只在单次shim/MokManager运行期间保留,并在完成或取消过程后立即清除。完成密钥管理后,系统将重新启动,而不仅仅继续引导,因为可能需要密钥管理更改才能成功完成引导。
一旦系统继续引导到GRUB,GRUB过程将加载任何所需的配置(通常从ESP(EFI系统分区)加载配置,指向根分区或引导分区上的另一个配置文件),该配置将指向要加载的内核映像。
到目前为止,EFI应用程序完全可以访问系统固件,包括访问更改受信任固件变量的权限,因此要加载的内核也必须根据信任数据库进行验证。官方的Ubuntu内核由Canonical的UEFI密钥签名,因此它们成功通过验证,并将控制权移交给内核。initrd映像不会被验证。
对于非官方内核或用户构建的内核,如果用户希望在保留UEFI Secure Boot的全部功能的同时加载这些内核,还需要采取额外的步骤。当启用UEFI Secure Boot时,所有内核都必须经过签名才能被GRUB加载,因此用户需要进行签名。或者,用户可以在启用官方内核的情况下,通过使用’sudo mokutil --disable-validation’命令并在提示输入密码时提供密码,禁用shim中的验证,并重新启动;或者在固件中完全禁用Secure Boot。
到目前为止,任何验证要加载的映像失败都会导致关键错误,停止引导过程。系统不会继续引导,并且如果其他BootEntry变量可能包含有效和受信任的引导路径,可能会在一段时间后自动重新启动。
加载并验证的内核将禁用固件的Boot Services,从而降低权限并有效地切换到用户模式,其中对受信任变量的访问仅限于只读。鉴于内核模块的广泛权限,任何没有内核内置的模块在加载时也需要进行验证。由Canonical与官方内核一起构建和提供的模块由Canonical的UEFI密钥签名,因此受信任。用户自定义构建的模块需要用户在加载模块之前采取必要的步骤对模块进行签名。可以使用’kmodsign’命令来实现这一点。
未经签名的模块将被内核拒绝。使用insmod或modprobe尝试插入它们将失败并显示错误消息。
鉴于许多用户需要第三方模块才能使其系统正常工作或使某些设备正常运行,并且这些第三方模块需要在系统上进行本地构建以适应运行的内核,Ubuntu提供了工具化来自动化和简化签名过程。
参考资料
https://en.wikipedia.org/wiki/UEFI
https://www.intel.com/content/www/us/en/developer/articles/tool/unified-extensible-firmware-interface.html
https://mp.weixin.qq.com/s/N_lSjtorg0Ho_hSBiM5uKA
https://blog.csdn.net/u013434525/article/details/136433642
https://blog.csdn.net/Anhui_Chen/article/details/106988113
https://www.cnblogs.com/shamoguzhou/p/17380191.html
https://blog.csdn.net/hawava/article/details/116232981
https://www.cnblogs.com/liuzhenbo/p/10825136.html
https://www.freebuf.com/news/261563.html
https://wiki.ubuntu.com/UEFI/SecureBoot
https://www.zhihu.com/question/392260941
https://zhuanlan.zhihu.com/p/36370829
https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot