从GRUB迁移至systemd-boot

某天在群里和各位大佬们作OT的闲聊时突然觉得Grub作为引导器过于臃肿,于是我就产生了一个换一个更加轻量的引导器的想法。

因为systemd-boot与Linux系统关系更紧密,而且有一些其他引导器没有的优秀特性,我决定更换这个引导器。

安装systemd-boot

首先需要安装systemd-boot

注意以systemd-boot作为引导器的时候需要将EFI分区作为系统的Boot分区,即把EFI分区挂载到Boot分区下。这里假设系统只有一个EFI分区,如果有多个EFI分区,需要给安装指令添加额外的参数。

bootctl install

配置systemd-boot

systemd-boot需要自行编写配置文件和启动项配置。

引导器配置

/boot/loader/loader.conf

#timeout 10
#console-mode keep
default arch.conf

一般来说写这么多就够了,对以上几个参数做一下解释

timeout即菜单超时,默认值为0。为0时自动引导默认启动项,若开机同时按下按键,将显示菜单。我个人很喜欢这个设计,可以在单系统的情况下节省宝贵的1秒钟,而且还不会牺牲扩展性。

console-mode默认值为keep,最好别动,否则启动菜单可能不是以屏幕分辨率呈现的。

default为默认启动项,值为/boot/loader/entries/下的文件名,此处为arch.conf,则entries文件夹下必须存在一个叫arch.conf的启动项文件。

启动项配置

/boot/loader/arch.conf

title   Arch Linux Zen Kernel
linux   /vmlinuz-linux-zen
initrd  /amd-ucode.img
initrd  /initramfs-linux-zen.img
options root=UUID=a83955ce-352c-4c12-bcce-8d0e049c6fc6 rw

格式大致如上。

title是该启动项在菜单中的显示名称

linux,initrd,options是引导Linux系统必需的参数,可以参考Grub的配置文件修改。

此处要注意不同版本内核的内核文件名不一致,需要修改。我使用的是Linux-zen内核,所以这里的内核文件带zen后缀。

别忘了添上微码补丁的加载项。

内核参数可以从/boot/grub/grub.cfg中去抄。最好不要从/etc/default/grub里抄,如果使用了LUKS加密系统盘,这样做可能会出问题。

如果只是加密了系统(或者没加密),做到这一步就可以卸载Grub重启了。

配置安全启动

如果还设置了安全启动,需要继续配置内核自动签名。ArchWiki提供了两种方法,这里依旧采用Pacman HOOK的方法。

关于安全启动的详细配置过程,请参考这篇文章

配置自动签名(HOOK)

如果使用Grub时按照ArchWiki教程配置了自动签名HOOK,这里需要删除这些HOOK。

/etc/pacman.d/hooks/90-mkinitcpio-install.hook

/usr/local/share/libalpm/scripts/mkinitcpio-install

创建两个新HOOK

/etc/pacman.d/hooks/100-systemd-boot.hook

[Trigger]
Type = Package
Operation = Upgrade
Target = systemd

[Action]
Description = Updating systemd-boot
When = PostTransaction
Exec = /usr/bin/bootctl update

这个是当systemd更新时自动更新systemd-boot的HOOK。

/etc/pacman.d/hooks/99-secureboot.hook

[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = usr/lib/modules/*/vmlinuz 
Target = usr/lib/initcpio/* 
Target = systemd

[Action]
Description = Signing Kernel for SecureBoot
When = PostTransaction
Exec = /usr/bin/sh -c "/usr/bin/find /boot/ -type f \( -name 'vmlinuz-*' -o -name 'systemd*' \) -exec /usr/bin/sh -c 'if ! /usr/bin/sbverify --list {} 2>/dev/null | /usr/bin/grep -q \"signature certificates\"; then /usr/bin/sbsign --key db.key --cert db.crt --output {} {}; fi' \;"
Depends = sbsigntools
Depends = findutils
Depends = grep

这个是当内核或systemd更新时自动签名内核和引导器的HOOK。

需要将db.keydb.crt修改为对应的绝对路径。

这个HOOK做了一些修改,将Target更换成了如上内容(内容抄自Grub的自动签名HOOK),这样可以兼容检测所有版本的内核的更新,ArchWiki中的版本只能检测linux原版内核一种内核的更新。

注意在这里,更新引导器的HOOK必须比自动签名HOOK先执行。不过systemd-boot.hook使用了100作为前缀,看起来是好像99要比100先执行,其实这里的排序是逐个字符对比排序的,所以100要比99先执行。

签名systemd-boot

最后需要将引导器签名,否则系统不会启动systemd-boot。

sbsign --key db.key --cert db.crt --output /boot/EFI/systemd/systemd-bootx64.efi /boot/EFI/systemd/systemd-bootx64.efi
sbsign --key db.key --cert db.crt --output /boot/EFI/BOOT/BOOTX64.EFI /boot/EFI/BOOT/BOOTX64.EFI

同样需要将db.keydb.crt修改为对应的绝对路径。

卸载Grub

sudo pacman -R grub os-prober

卸载完后Pacman会提示有两个文件被转移,这两个文件也要删掉。

然后需要删除Boot分区中的grub文件夹和EFI分区中的grub启动文件,最后用efibootmgr清理掉残留的GRUB启动项就可以了。

参考资料

systemd-boot - ArchWiki