4、systemd第一阶段:systemd-stub与EFI stub的内核参数处理

在UEFI启动环境下,Linux内核的加载并非直接由bootloader完成,而是经过EFI stub和systemd-stub两个关键阶段。这两个组件负责从EFI固件接收控制权,解析并传递内核参数,最终将系统引导至initramfs。理解这一阶段的内核参数处理机制,对于调试启动失败、参数传递错误等问题至关重要。

核心概念:EFI stub是内嵌于Linux内核镜像中的小型EFI应用程序,负责从UEFI固件接管硬件;systemd-stub则是systemd项目提供的UEFI启动存根,用于统一管理内核命令行、设备树和凭据。

4.1 EFI stub:内核自带的启动存根

EFI stub是编译进Linux内核(CONFIG_EFI_STUB)的一段代码,它使得内核镜像本身成为一个合法的EFI可执行文件。当UEFI固件直接加载内核镜像(如vmlinuz.efi)时,EFI stub首先获得执行权。

4.1.1 EFI stub的主要职责

  • 初始化EFI运行时服务:获取EFI系统表、内存映射、启动服务等。
  • 解析EFI加载选项:从UEFI固件的LoadOptions变量中读取内核命令行参数。
  • 设置图形模式:初始化EFI GOP(Graphics Output Protocol),为早期启动提供显示支持。
  • 退出EFI启动服务:在跳转到内核主函数前,调用ExitBootServices(),将控制权完全交给内核。
  • 处理EFI内存映射:将UEFI内存映射转换为内核可识别的e820表。

4.1.2 内核参数传递流程

EFI stub从UEFI固件获取内核参数的路径如下:

  1. UEFI固件将内核命令行字符串存储在EFI_LOADED_IMAGE_PROTOCOL.LoadOptions中。
  2. EFI stub调用efi_get_loaded_image()获取该协议接口。
  3. 读取LoadOptions指向的Unicode字符串,转换为ASCII格式。
  4. 将转换后的字符串传递给内核的boot_params.hdr.cmd_line_ptr。
调试要点:如果内核参数未生效,首先检查UEFI固件是否正确传递了LoadOptions。可以在UEFI Shell中使用dmpstore -all查看当前启动选项变量。

4.2 systemd-stub:统一启动存根

systemd-stub(systemd-stub或sd-stub)是systemd项目提供的UEFI启动存根,通常与systemd-boot配合使用。它被设计为EFI stub的替代或补充,提供更丰富的功能,特别是对统一内核镜像(UKI)的支持。

4.2.1 systemd-stub与EFI stub的对比

特性 EFI stub systemd-stub
来源 内嵌于Linux内核 systemd项目独立编译
UKI支持 有限 原生支持,可嵌入initramfs、内核、cmdline、DTB
凭据管理 不支持 支持EFI变量和TPM2密封凭据
设备树处理 需额外配置 自动从UKI中提取并加载DTB
安全启动兼容性 基础支持 深度集成,支持PE签名和测量启动
内核参数来源 仅LoadOptions LoadOptions + UKI内嵌cmdline + 凭据中的参数

4.2.2 systemd-stub的内核参数合并策略

systemd-stub在启动时会从多个来源收集内核参数,并按照优先级合并:

  1. UKI内嵌命令行:编译时嵌入到.cmdlinePE节区,优先级最低。
  2. EFI变量:读取LoaderEntryKernelOptions等EFI变量。
  3. 凭据(Credentials):从TPM2密封或EFI变量中解封的凭据参数。
  4. UEFI交互式输入:如果用户在启动菜单中编辑了参数,则覆盖上述所有来源。
注意:systemd-stub在合并参数时,如果同一参数出现多次,最后一次出现的值生效。这意味着UEFI交互式输入具有最高优先级。

4.3 内核参数处理流程总览

下图展示了从UEFI固件到内核主函数的内核参数完整处理流程:

systemd-stub与EFI stub内核参数处理流程 UEFI固件 LoadOptions 变量 使用哪种stub? EFI stub systemd-stub 读取LoadOptions → 转ASCII 设置cmd_line_ptr 合并: 内嵌cmdline + EFI变量 + 凭据 交互式输入覆盖 → 最终cmdline 内核主函数 说明:EFI stub仅从LoadOptions获取参数;systemd-stub支持多源合并,且交互式输入优先级最高。

4.4 关键内核参数与调试方法

4.4.1 影响stub行为的关键参数

参数 作用阶段 说明
efi=debug EFI stub 启用EFI stub的调试输出,显示内存映射、加载选项等
noefi EFI stub 禁用EFI运行时服务,回退到传统BIOS模式
stub=debug systemd-stub 启用systemd-stub的详细日志,显示参数合并过程
systemd.efi-options systemd-stub 从EFI变量中读取额外选项的键名
initrd= 两者 指定initramfs路径,EFI stub从EFI分区加载,systemd-stub从UKI中提取
console= 两者 设置控制台设备,影响stub阶段的日志输出

4.4.2 调试技巧

  • 启用stub调试输出:在UEFI启动菜单中编辑内核命令行,添加efi=debug(EFI stub)或stub=debug(systemd-stub),观察屏幕输出的调试信息。
  • 检查EFI变量:在Linux启动后,使用efibootmgr -v查看当前启动项的内核参数设置。
  • 模拟UKI启动:使用systemd-boot的bootctl set-oneshot命令临时指定内核参数,测试参数传递是否正确。
  • 捕获stub日志:如果系统无法完全启动,可以在UEFI Shell中手动加载内核并添加debug参数,通过串口或帧缓冲捕获日志。
实战案例:某系统启动时内核参数root=始终不生效。通过添加efi=debug发现EFI stub读取的LoadOptions包含乱码,原因是UEFI固件将Unicode字符串错误地截断。解决方法是在UEFI设置中重新配置启动项,确保参数长度不超过固件限制。

4.5 安全启动与参数完整性

在启用安全启动(Secure Boot)的环境下,内核参数的篡改可能导致系统拒绝启动。systemd-stub通过以下机制保障参数完整性:

  • PE签名验证:UKI镜像的.cmdline节区被包含在PE签名范围内,任何修改都会导致签名失效。
  • TPM2测量:systemd-stub在启动时将内核参数哈希值扩展到TPM2 PCR(平台配置寄存器),用于远程证明。
  • 凭据密封:敏感参数(如磁盘加密密钥)可以通过TPM2密封,仅在PCR状态匹配时才能解封。
安全提示:在安全启动环境下,不要依赖UEFI交互式输入传递敏感参数,因为交互式输入不会被签名保护。应使用UKI内嵌或TPM2密封的凭据。

4.6 本章小结

EFI stub和systemd-stub是UEFI启动路径上的两个关键组件,负责从固件接收并处理内核参数。EFI stub作为内核内置的轻量级方案,仅支持从LoadOptions获取参数;而systemd-stub提供了更强大的多源合并、UKI支持和安全启动集成能力。掌握这两个阶段的内核参数处理机制,能够帮助开发者快速定位启动参数传递失败、参数被覆盖或安全启动校验失败等问题。