10.4. 使用 GRUB 设定引导过程

10.4.1. 概述

[警告]

警告

如果您不小心错误地配置了 GRUB,可能导致您的系统完全无法使用,除非使用 CD-ROM 或可引导的 USB 存储器等备用引导设备。本节不是引导您的 LFS 系统的唯一方案,您可能只要修改现有的启动加载器 (如 Grub-Legacy 或 GRUB2) 配置即可引导 LFS。

您务必保证自己拥有一个紧急引导磁盘,它在计算机不可用 (无法引导) 时能够 抢修 计算机。如果您现在还没有引导设备,您可以执行以下命令创建一个。在运行下列命令前,您需要跳到 BLFS,安装包含 xorriso libisoburn 软件包:

cd /tmp
grub-mkrescue --output=grub-img.iso
xorriso -as cdrecord -v dev=/dev/cdrw blank=as_needed grub-img.iso

10.4.2. 关闭安全启动

LFS 不包含支持安全启动所需的软件包。因此,如果要按照本节的说明配置引导过程,必须在固件的配置界面关闭安全启动。阅读系统生产商提供的文档以找出关闭安全启动的方法。

10.4.3. GRUB 命名惯例

GRUB 使用一种独特的命名结构,为驱动器和分区命名。分区名的形式为 (hdn,m),这里 n 是硬盘驱动器编号,m 是分区编号。硬盘驱动器编号从 0 开始,但分区号对于主分区来说从 1 开始 (对于扩展分区来说从 5 开始)。例如,分区 sda1 在 GRUB 中的名字是 (hd0,1),而 sdb3 的名字是 (hd1,3)。和 Linux 不同,GRUB 不认为 CD-ROM 驱动器属于硬盘驱动器。例如,如果在 hdb 上有一个 CD-ROM 驱动器,而 hdc 上有第二个硬盘驱动器,则第二个硬盘驱动器仍然名为 hd1

10.4.4. 设定 GRUB 配置

如果使用 BIOS 引导系统,GRUB 会在硬盘的首个扇区 (称为主引导记录,或 MBR) 写入一个端桩。MBR 区域不属于任何文件系统。BIOS 会加载并执行 MBR 的内容,这样端桩即可从 BIOS Boot 分区 (没有文件系统结构) 加载 GRUB 主映像。这样,端桩就不需要支持任何文件系统,才能小到足以存放到 MBR 中。

如果通过 UEFI 引导系统,GRUB 将其主映像写入到位于标准位置的 PE-COFF 可执行文件 EFI/BOOT/BOOTX64.EFI 中 (对于 i386-efi 则是 EFI/BOOT/BOOTIA32.EFI)。UEFI 固件从标准位置加载并执行这个文件,启动 GRUB。

GRUB 主映像并不包含 GRUB 的大多数功能 (包括引导 Linux 内核)。实际上,这些功能作为 GRUB 模块,被存储在某个文件系统中。在多数 Linux 发行版中,这个文件系统的挂载点允许通过 /boot/grub 路径访问 GRUB 模块。为了避免“先有鸡还是先有蛋”的问题,grub-install 将访问这一文件系统所必须的模块嵌入到 GRUB 主映像中,使它能够找到和加载其他模块。

引导分区的位置可由负责进行配置的用户自行决定,作者推荐创建一个小的 (建议大小为 200 MB) 分区,专门存放引导信息。这样,不仅是 LFS,安装在硬盘上的所有 Linux 发行版都能访问各个发行版的引导文件。如果您选择这样做,您需要挂载这个单独的分区,将 /boot 中已有的文件 (例如上一节中构建的 Linux 内核) 移动到新的分区中。之后,解除该分区的挂载,再将它重新挂载为 /boot。另外,还要注意更新 /etc/fstab

[注意]

注意

如果宿主发行版为 /boot 使用了单独的分区,且希望 LFS 系统也以这个分区作为 /boot,直接在宿主发行版将它挂载到 $LFS/boot 即可。Linux 内核允许将一个分区挂载到多个挂载点。

直接将 /boot 目录保留在 LFS 分区也是可以的,但这样在配置多系统启动时比较麻烦。

第 2.4 节 “创建新的分区” 可以找到引导分区布局的更多信息和示例。

根据以上信息,确定 LFS 根分区 (或 boot 分区,如果使用了独立的 boot 分区) 的名称。下面假设 LFS 根分区 (或 boot 分区) 是 sda2

以下各节描述配置 BIOS 和 UEFI 引导的方法。为 BIOS,64 位 UEFI,以及 32 位 UEFI 安装的 GRUB 可共存并共用一份配置,它们的代码和数据存放在不同的位置。因此,可以既创建 BIOS Boot 分区,又创建 EFI 系统分区,再为所有支持的固件类型安装 GRUB (即运行三次 grub-install。在不确定固件类型,或者计划将硬盘移动到其他计算机上使用的情况下,可以这样直接覆盖所有情况。

[注意]

注意

如果使用 UEFI 引导系统,但已经创建了 Grub BIOS 分区,则可以执行配置 BIOS 引导的命令,以作为 UEFI 引导不能正常工作时的备份。

[注意]

注意

如果只需要为一种引导方案安装 GRUB,只运行为该引导方案进行安装的命令即可。

10.4.4.1. 使用 BIOS 进行引导

如果需要使用 BIOS 引导 LFS,首先确认 boot 分区已经挂载(如果使用了单独的 boot 分区),并确认 BIOS Boot 分区存在。然后即可将 GRUB 文件安装到 /boot/grub 并设定引导磁道:

[警告]

警告

以下命令会覆盖当前启动引导器,如果您不希望这样做,例如要使用第三方引导管理器管理 MBR,则不要运行该命令。

grub-install /dev/sda --target=i386-pc

10.4.4.2. 使用 UEFI 引导系统

如果需要使用 UEFI 引导 LFS,首先确认 boot 分区已经挂载(如果使用了单独的 boot 分区),并确认 EFI 系统分区已经挂载到 /boot/efi。然后即可将 GRUB 文件安装到 /boot/grub 并将 GRUB 主映像安装到 /boot/efi/EFI/BOOT/BOOTX64.EFI

[警告]

警告

以下命令会覆盖 /boot/efi/EFI/BOOT/BOOTX64.EFI 文件。如果该文件已经存在,则它很可能是另一引导加载器的入口点(例如宿主发行版安装的 GRUB,或者 Windows 引导管理器)。可以备份该文件,这样之后可以恢复它,或使用 LFS 新安装的 GRUB 将其加载为次级引导加载器。

grub-install --target=x86_64-efi --removable

上述命令假设 UEFI 固件是 64 位的。如果需要通过 32 位 UEFI 固件引导系统,需要将命令中的 x86_64-efi 改为 i386-efi

--removable 选项使得 grub-install 使用标准位置,EFI/BOOT/BOOTX64.EFI (或者对于 i386-efi 则为 EFI/BOOT/BOOTIA32.EFI),而非 GRUB 通常使用的位置 (EFI/GRUB/GRUBX64.EFI 或者 EFI/GRUB/GRUBIA32.EFI。)如果使用非标准位置,则需要将它记录在一个 EFI 变量中,但 LFS 没有 BLFS 软件包 efibootmgr,而 GRUB 需要它才能在 EFI 变量记录位置。

[注意]

注意

一些很少见的 UEFI 固件实现会忽略标准路径。这些系统一般出现在较为陈旧的系统上,例如旧款的的联想 Thinkpad 或 HP 台式机/笔记本。如果在固件设置中找不到对应于标准路径的引导项,则需要使用 BLFS 软件包 efibootmgr 创建引导项。如果宿主发行版提供了该软件包,也可以通过其包管理器安装并使用它。这样可以暂时避免为 LFS 系统下载更多源码包。

首先安装上述软件包,然后挂载 EFI 变量文件系统,除非它已被挂载:

mountpoint /sys/firmware/efi/efivars ||
  mount -v -t efivarfs efivarfs /sys/firmware/efi/efivars

现在创建一个 EFI 引导项:

efibootmgr -c -d /dev/sd<x> \
  -p <y> -L "LFS" -l '\EFI\BOOT\BOOT<X64>.EFI'

其中 /dev/sd<x> 应改为 ESP 所在磁盘对应的设备节点,<y> 应改为 ESP 在磁盘上的编号,例如如果 ESP 对应的设备节点是 /dev/sda2,则编号就是 2。如果使用 32 位 UEFI,需要将 <X64> 替换成 IA32

一些 (实现不正确的) 固件需要向 efibootmgr 传递更多选项,类似 --full-dev-path 或者 -e 1 -E。详见手册页 efibootmgr(8)

取消挂载 EFI 变量文件系统:

umount -v /sys/firmware/efi/efivars

10.4.5. 创建 GRUB 配置文件

生成 /boot/grub/grub.cfg

cat > /boot/grub/grub.cfg << "EOF"
# Begin /boot/grub/grub.cfg
set default=0
set timeout=5

insmod part_gpt
insmod ext2

set root=(hd0,2)

# For UEFI
insmod efi_gop
insmod efi_uga

set gfxpayload=1024x768x32

menuentry "GNU/Linux, Linux 6.19.12-lfs-r13.0-84-systemd" {
        linux   /boot/vmlinuz-6.19.12-lfs-r13.0-84-systemd root=/dev/sda2 ro
}
EOF

配置文件使用 insmod 命令加载 GRUB 模块 part_gptext2efi_gop,以及 efi_ugaext2 模块虽然其名称只包含一个文件系统,实际上却支持 ext2ext3,以及 ext4 文件系统。在 UEFI 系统上,efi_gopefi_uga 提供视频输出支持。GOP,即图形输出协议,是现代 UEFI 固件采用的视频接口。UGA,即通用图形适配器,则出现在一些老式 UEFI 固件上。在典型的系统配置中,grub-install 命令已经将 part_gptext2 嵌入 GRUB 主映像中,此时对应的两条 insmod 命令不会产生任何效果。但是无论如何它们不会造成损害,而且在一些少见的系统配置中它们可能是必要的。

set gfxpayload=1024x768x32 命令设置 VESA 帧缓冲的分辨率和色深。内核 SimpleDRM 驱动需要它才能使用 VESA 帧缓冲。可以自行改用更适合显示器的分辨率和色深值。这一行在使用 UEFI 引导系统时没有作用,但也不会造成损害。

[注意]

注意

GRUB 的视角来看,内核文件的位置相对于它使用的分区。如果您使用了单独的 /boot 分区,需要从上面的 linux 行删除 /boot,然后修改 set root 行,指向 /boot 分区。

[注意]

注意

如果新增或移除了一些存储设备 (包括 USB 闪存盘等可移动存储设备),则 GRUB 赋予分区的编号可能发生改变。这可能导致引导失败,因为 grub.cfg 仍然在使用旧的编号。如果希望避免这种问题,可以使用分区和文件系统的 UUID 指定分区,以代替 GRUB 编号。运行 lsblk -o UUID,PARTUUID,PATH,MOUNTPOINT 以显示文件系统 (在 UUID 列) 和分区 (在 PARTUUID 列) 的 UUID。之后将 set root=(hdx,y) 替换为 search --set=root --fs-uuid <内核所在文件系统的 UUID>,并将 root=/dev/sda2 替换为 root=PARTUUID=<构建 LFS 使用的分区的 UUID>

注意分区的 UUID 和该分区中文件系统的 UUID 是完全不同的。一些在线资料可能建议使用 root=UUID=<文件系统 UUID> 代替root=PARTUUID=<分区 UUID>,但是这种方法依赖于 initramfs,而 initramfs 超出了 LFS 的范畴。

/dev 中分区对应的设备节点名也可能发生改变 (这一现象在在配备多块 NVME 盘的系统上经常出现)。在 /etc/fstab 中,也可以将 /dev/sda1 这样的设备节点路径改为 PARTUUID=<分区 UUID>,从而避免设备节点命名发生改变时可能导致的引导失败。

GRUB 是一个很强大的程序,它提供了非常多的选项,可以支持多种设备、操作系统和分区类型,还有很多用于定制启动屏幕、声音、鼠标输入等的选项。这些选项的细节超过了本书的范围,不予讨论。

[小心]

小心

有一个命令 grub-mkconfig 可用于自动创建配置文件。它使用 /etc/grub.d 中的脚本创建新配置文件,这会覆盖您手动编写的配置。这些脚本主要是为非源代码发行版设计的,在 LFS 中不推荐使用它们。但是,如果您安装了商业发行版,它很可能在发行版中被运行,记得备份 grub.cfg 以防它被覆盖。