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 解释,EFI shell 了解相对路径,而命令行的其余部分将传递给 bzImage.efi。

“dtb=” 选项

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

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

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