EFI 启动存根

在 x86 和 ARM 平台上,内核 zImage/bzImage 可以伪装成 PE/COFF 镜像,从而说服 EFI 固件加载器将其作为 EFI 可执行文件加载。修改 bzImage 标头的代码以及固件加载器跳转到的 EFI 特定入口点统称为“EFI 启动存根”,分别位于 arch/x86/boot/header.S 和 drivers/firmware/efi/libstub/x86-stub.c 中。对于 ARM,EFI 存根在 arch/arm/boot/compressed/efi-header.S 和 drivers/firmware/efi/libstub/arm32-stub.c 中实现。在架构之间共享的 EFI 存根代码位于 drivers/firmware/efi/libstub 中。

对于 arm64,没有压缩内核支持,因此 Image 本身伪装成 PE/COFF 镜像,并且 EFI 存根被链接到内核中。arm64 EFI 存根位于 drivers/firmware/efi/libstub/arm64.c 和 drivers/firmware/efi/libstub/arm64-stub.c 中。

通过使用 EFI 启动存根,可以在不使用传统的 EFI 引导加载程序(例如 grub 或 elilo)的情况下引导 Linux 内核。由于 EFI 启动存根执行了引导加载程序的工作,因此在某种意义上它就是引导加载程序。

EFI 启动存根通过 CONFIG_EFI_STUB 内核选项启用。

如何安装 bzImage.efi

位于 arch/x86/boot/bzImage 中的 bzImage 必须复制到 EFI 系统分区 (ESP) 并使用扩展名“.efi”重命名。没有扩展名,EFI 固件加载器将拒绝执行它。无法从通常的 Linux 文件系统执行 bzImage.efi,因为 EFI 固件不支持它们。对于 ARM,应将 arch/arm/boot/zImage 复制到系统分区,并且可能不需要重命名。类似地,对于 arm64,应复制 arch/arm64/boot/Image,但不一定需要重命名。

从 EFI shell 传递内核参数

内核的参数可以在 bzImage.efi 之后传递,例如

fs0:> bzImage.efi console=ttyS0 root=/dev/sda4

“initrd=” 选项

像大多数引导加载程序一样,EFI 存根允许用户使用“initrd=”选项指定多个 initrd 文件。这是唯一特定于 EFI 存根的命令行参数,其他所有参数都会在内核启动时传递给它。

initrd 文件的路径必须是 ESP 开头的绝对路径,相对路径名不起作用。此外,该路径是 EFI 样式路径,目录元素必须用反斜杠 (\) 分隔。例如,给定以下目录布局

fs0:>
      Kernels\
                      bzImage.efi
                      initrd-large.img

      Ramdisks\
                      initrd-small.img
                      initrd-medium.img

如果当前工作目录为 fs0:Kernels,要使用 initrd-large.img 文件引导,则必须使用以下命令

fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img

请注意,可以使用相对路径指定 bzImage.efi。这是因为我们正在执行的镜像由 EFI shell 解释,它理解相对路径,而命令行的其余部分会传递给 bzImage.efi。

“dtb=” 选项

对于 ARM 和 arm64 架构,必须向内核提供设备树。通常,固件应通过 EFI 配置表提供设备树。但是,“dtb=”命令行选项可用于覆盖固件提供的设备树,或者在固件无法提供时提供一个。

请注意:固件会在引导内核之前向设备树添加运行时配置信息。如果使用 dtb= 来覆盖设备树,则固件提供的任何运行时数据都将丢失。dtb= 选项仅应作为调试工具使用,或者在 EFI 配置表中未提供设备树时作为最后的手段使用。

“dtb=”的处理方式与上面描述的“initrd=”选项相同。