如果你阅读了上一篇文章:在 RPI3 UEFI 上安装 Arch Linux ARM,那么可能会有这么一个问题:如何优雅地处理内核参数(cmdline)问题?今天我探索了一下 Arch Linux ARM(UEFI)下的 Grub 引导。这样做可以完全做到和固件分离,并且非常方便。
注意!
这不是任何产品的官方文档、帮助或使用说明,仅代表博主个人的经验总结,难免会有疏漏和错误之处。请务必结合官方文档进行阅读,以作补充。
我个人不喜欢 Grub 的其中一个原因是其配置复杂,我并不熟悉操作,所以很少使用之。不过,在 Arch Linux ARM 上,默认使用 EFISTUB。如果要更改参数,没有更优的选项:
- 直接在 UEFI 固件更改。有长度限制,不好升级(?),容易丢失(?)等。
cmdline.txt
似乎不可行?(没有测试)systemd-boot
、rEFIND
、Clover
均不可用。
所以,Grub 就是我们最后一根稻草。幸运的是,Grub 有着对 AArch64 的支持,而且 Arch Linux ARM 有包。
首先,需要一个 Patch。 #
这里不得不提到 Grub 的配置生成脚本,它们位于 /etc/grub.d/
。由于个人并不懂 Shell 编程,所以不能做一个很详细的解释和研究。不过,它似乎会定位 /boot/vmlinuz-*
作为内核(Arch 和 Ubuntu 上都使用 vmlinuz-file
工具查看文件类型)。然而,Arch Linux ARM 默认会将内核安装到 /boot/Image
。这个文件是 Pre-built EFISTUB 可执行文件,并不是由 mkinitcpio
生成的(请参考 这个新闻)。
很明显,grub-mkconfig
会找不到 Linux 内核,也就不会生成正确的配置。于是,有两种方式可以解决。
二〇二二年一月十三日更新:群友 Menci 已经向 Arch Linux Arm Grub 包的 PKGBUILD 提交了 Patch,现在 Arch Linux Arm 的 Grub 默认会搜索
Image
了,因此读者可以安全地跳过这个章节。
修改/etc/grub.d/10_linux
脚本,让它搜索Image
。重命名/boot/Image
,让它符合习惯。
很明显,由于我不会 Shell 编程,第二种方式是最优的。考虑到每次更新 linux
(内核包)都需要重命名,我写了一个 libalpm
Hook,会自动在 /boot/Image
被软件包更新或新建后重命名:
# /etc/pacman.d/hooks/99-kernel-rename.hook
[Trigger]
Operation = Install
Operation = Upgrade
Type = Path
Target = boot/Image
Target = boot/Image.gz
[Action]
When = PostTransaction
Exec = /usr/bin/sh -c "/usr/bin/mv /boot/Image /boot/vmlinuz-linux"
然后重装 linux
,就可以看到效果。至此,我们已经可以安装 Grub 了。
安装 Grub #
安装 Grub 并不是难事,我们开始吧。
首先,确保 ESP 挂载到了 /boot
。然后安装软件包 grub
和 efibootmgr
。Grub 是本体,efibootmgr
用于修改 UEFI 引导项(虽说似乎没有作用,而且在本次并不需要修改,这个后面会讲)。然后,执行:
grub-install --target=arm64-efi --efi-directory=/boot --bootloader-id=Grub --removable # 需要 Root
详解:
--target=arm64-efi
设定平台。具体可选项可以用grub-install --help
查看。--efi-directory
设定 ESP 挂载位置。--bootloader-id
这个有什么用不太清楚,不过大概是 UEFI 引导项的名字吧。--removable
这一点很重要,虽说它不是启动系统必须的。它会把 EFI 可执行文件安装到/boot/EFI/BOOT/bootaa64.efi
而不是默认的/boot/EFI/Grub/grub
。EFI/boot/bootaa64.efi
是 UEFI 的默认路径(在启动的时候选择整个分区,即不设定具体程序,就会默认启动它),方便和 UEFI 固件分离设置,更加清晰、便捷。这也就是为什么我们不需要修改 UEFI 启动项,因为这是默认路径。
至此,Grub 已安装到系统。
修改内核参数 #
又到了这一部分。由于没有了 UEFI 固件的长度限制,我们可以写多些。默认内核参数保存在 /etc/default/grub:
# /etc/default/grub
# (Skipped)
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX=""
# (Skipped)
这两个变量组合,就会成为最终的参数。我们这里选择修改 GRUB_CMDLINE_LINUX_DEFAULT
。
和上一篇一样,我们设定 root
和 rootwait
。不过,既然没有了长度限制,那么就可以用 PARTUUID
了,这样就可以做到和分区号的分离,方便迁移。同时,还可以从 Arch Linux ARM 官方 GitHub 的一个 cmdline 里面复制一些其他的参数,我也不太明白是做甚么的,读者不要随意模仿(尤其是 RPI4 用户,因为 Arch Linux ARM 还有一个叫 rpi4 的 cmdline,具体有什么用就请读者自行研究了)。
最终,这里是我的配置:
# /etc/default/grub
# (Skipped)
GRUB_CMDLINE_LINUX_DEFAULT="root=PARTUUID=639a9dd5-06e6-ed4e-b09e-0ddb2657b546 rw rootwait console=ttyAMA0,115200 console=tty1 selinux=0 plymouth.enable=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 kgdboc=ttyAMA0,115200 elevator=noop aduit=0"
GRUB_CMDLINE_LINUX=""
# (Skipped)
生成配置 #
生成配置是最简单的,一行便可。请注意,每次修改配置都需要重新生成(来源请求),并且不要手工修改生成的配置。
grub-mkconfig -o /boot/grub/grub.cfg # 需要 Root
-o
指定输出位置,Grub 会自动加载 /boot/grub/grub.cfg,所以无需修改。
修改引导项 #
诶,不是说和固件分离吗,怎么又要修改引导项了?
我们的配置确实和固件分离了,不过还需要清理干净之前的配置。打开 UEFI 固件设置,
- 看看有没有之前自定义的引导项(名字不是分区的等等),删掉
- 看看引导顺序,可以留默认(如果是 USB 引导,U 盘的 ESP 可能是第二个)
- 如果 U 盘的 ESP 不在引导顺序的第一个,确保之前的分区没有
EFI/boot/bootaa64.efi
。之前已经说了,EFI 会自动启动这个文件。如果有,自然会优先引导他们啦。
好了,本篇就到此为止了。重启系统,看看效果吧。