Skip to content

Latest commit

 

History

History
348 lines (266 loc) · 13.1 KB

79、用 systemd-boot 替换 grub.adoc

File metadata and controls

348 lines (266 loc) · 13.1 KB

用 systemd-boot 替换 grub

新增内容

Note

v2 版改版说明:

  • 在第一版中,笔者没有遵循 Fedora 对于 BOOT 分区和 EFI 分区的建议,导致了一些兼容性问题。在这一版中统一按照 Fedora 的规划进行设置。

  • 优化了部分操作过程,尽量使用现有的工具

  • 明确了部分用语

Important

请务必准备一个可以启动的 Fedora Workstation LiveCD,万一在下面的调整中出现任何错误,我们还有从 LiveCD 启动,并挂载硬盘 chroot 进行补救的操作。

Note

为了保证 chroot 后可以使用大部分工具,除了 mount 根目录之外,还需要额外挂载一些关键目录

# 这里做如下假设
# 1. EFI 分区在 sda1 上
# 2. 根目录具有 btrfs 文件系统,其在 sda2 上
# 3. 根目录挂载点设置在 /mnt 下

# 挂载根目录
# 由于 btrfs 的存在,这里我们要指定需要挂载的是 root 子卷
mount /dev/sda2 /mnt -o subvol=/root

# 挂载常见的虚拟文件系统
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
mount --bind /dev /mnt/dev
mount --bind /run /mnt/run

# 特别的,由于 bootctl install 需要修改 UEFI 变量,因此,我们需要额外挂载 efivars 文件夹
mount --bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars

这样设置之后,我们就有了一个基本可用的 chroot 环境了。使用 chroot /mnt 进入环境后,我们就可以使用 mount -a 自动加载其它的文件系统了。

Important

如果你阅读了第一版文章,且更新到了 Fedora 41,那么在 systemd-boot 引导后会出现 Error preparing initrd: not found,请使用下方的命令进行急救:

  1. 在临时 shell 中修改启动项

    1. 在启动到 systemd-boot 的时候,按 e 键,编辑启动参数,在行尾添加 rd.break,并按 Ctrl + x 引导。

    2. 输入 root 密码,进入临时 shell 状态

    3. /sysroot/boot 下新建 efi 文件夹

    4. 使用 blkid 命令列举当前的块设备,我们这里要查找的是文件系统为 vfat,标签为 EFI system partition 的设备

    5. 将该该设备挂载到 /sysroot/boot/efi

    6. 进入 /sysroot/boot/efi/loader/entries/ 找到其中文件名具有 fc41 的 .conf 文件

    7. 将该 .conf 文件中 initrd 行的行尾的 $tuned_initrd 删除,保存,重启。

  2. 在系统中进行修改

    1. 从我们刚刚修改过的启动项启动

    2. 依照下方的“备份可能需要的文件”备份 /boot 中的文件

    3. 使用 umount /boot 卸载 EFI 分区

    4. 依照下方的“准备 /boot 和 /boot/efi 文件夹”还原文件

    5. 修改 /etc/fstab,将 EFI 分区的挂载点修改为 /boot/efi

    6. 使用 systemctl daemon-reload 让 systemd 重读 fstab 内容

    7. 使用 mount -a 自动挂载 BOOT 分区

    8. 安装 sdubby 包,在 dnf 配置目录中对该包进行保护

    9. 使用 bootctl install 重新生成 efi 文件,并重新写入 UEFI 变量

    10. 使用 kernel-install add-all 重新生成所有的启动项

    11. 进行最后的检查

      1. 使用 lsblk -o+PARTUUID 获取 EFI 分区的 PARTUUID

      2. 使用 efibootmgr 确认当前激活的 Boot 项的 PARTUUID 与上面的 PARTUUID 相同

      3. 使用 bootctl list 确认可引导条目正确

    12. 重启系统

简述

systemd-boot 是 systemd 推出的仅支持 UEFI 模式启动的 boot loader,相较于 grub,systemd-boot 的配置更简单,使用也更加方便,如果你的机器支持 UEFI 启动,且你的系统也仅需 UEFI 启动,那么可以考虑使用 systemd-boot 替换 grub。

Important
  1. 本文仅在 x64 架构上测试成功,其它架构(如 AArch64)请确认是否需要额外的操作步骤

  2. 下面的操作仅支持 非 Secure Boot 模式,已经开启 Secure Boot 的机器切勿尝试

Note

若你打算在安装 Fedora 的时候直接使用 systemd-boot 作为引导程序,那么可以直接在安装盘的引导的 linuxefi 行的行尾添加 inst.sdboot,并启动 Anaconda 并完成安装。

inst.sdboot 参数会被 Anaconda 读取,从而设置安装参数。关于 Anaconda 支持的启动参数,参见 https://anaconda-installer.readthedocs.io/en/latest/boot-options.html

操作概述

主要分以下几个步骤

  1. 安装相关的包

  2. 使用 bootctl 将 systemd-boot 的 .efi 文件注入 EFI 分区中

  3. 使用 kernel-install 为 systemd-boot 生成启动条目

  4. 让机器的 UEFI 加载 systemd-boot,并引导至操作系统

  5. 移除 grub 相关的程序和配置文件

需要注意的一些路径:

  1. /boot,Fedora 将启动相关的信息(比如 grub 的 .efi 文件和配置信息)保存在一个独立的磁盘分区中,该分区就会被 Fedora 挂载在 /boot 分区下,默认情况下,该分区为磁盘的第二个分区,且其文件系统为 ext4

  2. /boot/efi,该路径并非是 /boot 对应的磁盘分区的内容,而是 EFI 分区的挂载路径,它的文件系统为 EFI Filesystem(其实是 FAT32)

详细步骤

Important

实际操作前须知:
错误操作 boot loader 会导致引导失败,虽然不会直接导致数据丢失,但会导致无法正确引导至系统
强烈建议在实际操作之前,使用虚拟机完整走一遍流程,再考虑是否要实际引用在 bare metal 上

安装相关的包

dnf install --allowerasing sdubby

安装 sduddy 包会同时安装 systemd-boot-unsigned,并移除 grubby 包。

sdubby 包中有不少与 systemd-boot 相关的配置文件,而 systemd-boot-unsigned 包则包含了 systemd-boot 所需的 efi 文件。

将 systemd-boot 引导写入 EFI 分区中

Important

在这一步之后,bootctl 会调整 UEFI 的默认启动项,但由于缺少必要的操作,此时新的默认启动项是无法正确引导系统的。
如果你不幸关闭了系统,那么可以在 BIOS/UEFI 中选择 shimx64.efi 进行启动。

bootctl install

该操作将会在 /boot/efi 中释放相关的文件,主要是将 sysstemd-bootx64.efi 拷贝至 /boot/efi/EFI/systemd/ 下,并用相同的文件覆盖了 /boot/efi/EFI/BOOT/BBOTX64.efi;并在 EFI 自己的 NVRAM 里写入 systemd-boot 对应的启动项配置,我们可以通过 efibootmgr 命令观察到名为 Linux Boot Manager 的启动项。

之后我们就可以通过下面的命令查看可以由 systemd-boot 引导的系统:

bootctl list

给出的信息会有各种报错,这是因为系统上默认的配置文件并不符合 systemd-boot 的配置要求,因此我们还需要使用其它工具生成正确的配置。

使用 kernel-install 产生可用的配置文件

kernel-install --verbose add-all

重启后的配置

在重启之后,我们再次使用 bootctl stataus 命令,就可以看到其输出中有

Current Boot Loader:
    Product: systemd-boot XXX

字样,就表示本次启动是通过 systemd-boot 引导的了。

不过我们在启动过程中,应该没有看到任何的启动菜单界面,这个并不符合我们的要求,因此我们还需要再做一些配置,让启动菜单显示出来。

修改 /boot/efi/loader/loader.conf 文件,将 timeout 3 这行前的注释去掉,然后保存文件。再次重启,就能看到 systemd-boot 的启动菜单界面了。

其实到这一步,我们的系统就已经是 systemd-boot 引导的了,我们就可以着手删除 grub 相关的程序和配置了

【可选】删除 grub 引导

Important

请再次确认系统可以正确被 systemd-boot 引导,否则我们在删除 grub 之后,可能会造成引导失败

Note

在这个步骤中,我们将

  1. 为 sdubby 包和 systemd-boot-unsigned 包添加保护

  2. 删除 grub 和 shim

  3. 将 /boot 对应的分区删除,将其容量交给 EFI 分区

  1. 为 sdubby 和 systemd-boot-unsigned 包添加保护
    /etc/dnf/protected.d/ 下添加文件 systemd-boot.conf,并在其中写入下面的内容

    /etc/dnf/protected.d/systemd-boot.conf
    sdubby
    systemd-boot-unsigned
  2. 测试 sdubby 和 systemd-boot-unsigned 是否被保护成功

    dnf remove --setopt=tsflags=test sdubby systemd-boot-unsigned

    如果返回错误表示要移除的是 protected packages 则表示保护成功

  3. 移除 dnf 中 grub2、shim 包相关的保护

    rm -f /etc/dnf/protected.d/grub2-*.conf /etc/dnf/protected.d/shim.conf
  4. 移除 grub 和 shim 相关的包

    Important

    执行移除之后,切勿关机,此时系统缺失 /boot/efi/BOOT/BOOTX64.EFI 这个文件,会无法自动引导操作系统
    如果此时不幸系统掉电,则请在机器启动时,进入 UEFI Shell,然后通过 FS0: 进入第一个文件系统,然后 EFI\systemd\systemd-bootx64.efi 启动系统

    dnf remove grub* shim* mactel-boot

    注:我的电脑不是 mac,因此我还移除了引导 mac 相关的 mactel-boot 包

  5. 备份可能需要的文件

    mkdir ~/boot_bk
    mv config* symvers* System.map* .vmlinuz* ~/boot_bk
  6. 卸载 EFI 分区和 /boot 对应的分区

    umount /boot/efi /boot
  7. 准备 /boot 和 /boot/efi 文件夹

    # 新建 /boot/efi,作为 EFI 分区的新挂载点
    mkdir /boot/efi
    
    # 还原我们备份的文件
    mv ~/boot_bk/* ~/boot_bk/.* /boot
    
    # 若你启用了 SELinux,这里还可以还原一下 /boot 整个目录的 SELinux 上下文
    restorecon -v -R /boot
    
    # 还可以进一步还原 SELinux 上下文
    chcon --recursive --verbose --user system_u /boot
  8. 删除 BOOT 分区,并调整 EFI 分区的大小

    1. 用 gdisk 删除 BOOT 分区和 EFI 分区

    2. 创建新的 EFI 分区,其分区类型的编号为 EF00

  9. 新建 EFI 分区的 vfat 文件系统

    mkfs.vfat /dev/sda1
  10. 修改 /etc/fstab

    1. lsblk -o+UUID 确认 EFI 分区的文件系统的 UUID

    2. /etc/fstab 中删除 BOOT 分区的挂载配置,并将 EFI 分区的文件系统的 UUID 修改为与 lsblk 返回的一致。

  11. 尝试挂载 /boot/efi

    # systemd 似乎接管了 mount 的工作
    systemctl daemon-reload
    
    mount /boot/efi
  12. 再次重建整个 EFI 分区的内容

    # 移除残余的 Boot 启动项
    bootctl remove
    
    # 新建 systemd-boot 引导程序
    bootctl install
    
    # 重新生成可引导内核和 initrd
    kernel-install --verbose add-all
  13. 在重启电脑前,做最后的检查

    1. lsblk -o+PARTUUID 返回的 EFI 分区的 PARTUUID 与 efibootmgr 中启动项的磁盘的 PARTUUID 一致

    2. lsblk -o+UUID 返回的 EFI 分区的文件系统的 UUID 与 /etc/fstab 中记录的一致

    3. bootctl list 中返回的可引导条目的内容无误

    4. 当然,由于我们重建了整个 EFI 分区,我们还可以继续调整 /boot/efi/loader/lodaer.conf 中的配置

  14. 重启电脑

  15. 使用 efibootmgr 检查是否有残余的 EFI 启动项,如果有,用 efibootmgr --bootnum <要删除的启动项编号> --delete-bootnum 清除

到此,我们就完成了使用 systemd-boot 作为唯一 boot loader 的流程

其它常见的操作

systemd-boot 启动菜单的尺寸过小

这个一般出现在屏幕是高 DPI 屏的时候,直接按 r 键,就能切换显示模式,一般都有一到两个模式是能放大显示的

无法通过 loader.conf 文件设置 systemd-boot 启动菜单的 timeout

一般来说,是由于在启动菜单中设置了 timeout,或者通过 bootctl set-timeout 导致的
这两个方法会直接将 timeout 设置到 EFI 专有的 NVRAM 中,而且 NVRAM 中的值的优先度是高于 loader.conf 配置文件的
因此我们有两种方案解决这个问题

  • 直接在 systemd-boot 启动菜单中按减号键 -,直到屏幕上显示 Menu timeout defined by configuration file.

  • 删除 /sys/firmware/efi/efivars/LoaderConfigTimeout-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f 这个 EFI 变量
    删除前需要用 chattr -i 移除该文件的删除保护

设置 Linux kernel 的命令行参数

systemd 使用命令 kernel-install 来向 EFI/BOOT 分区注入可引导内核和相关信息,且 kernel-install 具有 systemd-boot 的配置文件。

kernel-install 的插件会读取 /etc/kernel/cmdline 来为 Linux kernel 添加命令行参数。因此我们可以修改 /etc/kernel/cmdline 的内容,并调用 kernel-install 来生成新的启动配置。

/etc/kernel/cmdline 中就是要传递给 kernel 的参数文本,因此我们直接进行修改即可

之后我们执行 kernel-install add-all 让 kernel-install 查找系统上存在的所有内核,并重新生成所有的启动条目。

Note

kernel-install 二进制程序更像流程的前端处理器,它负责收集来自命令行和系统的信息,并呈递给插件(脚本)来处理实际的问题。