
Linux x86_64 UEFI 启动支持指南内核配置、ESP 分区部署与 EFI 运行时故障排查【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读本文围绕 Linux 内核文档 Documentation/arch/x86/x86_64/uefi.rst 展开系统讲解在 x86_64 平台上让 Linux 内核配合 UEFIUnified Extensible Firmware Interface固件正常启动的完整流程从内核构建选项、EFI 系统分区ESP的创建与启动文件部署到 EFI 运行时服务Runtime Services出问题时的内核命令行参数排障手段。读完本文你将掌握编译一个可直接由 UEFI 固件加载的 bzImage以及在 EFI 环境下诊断重启、内存映射等异常的完整实战方案并了解这些行为在内核源码中的具体实现位置。需要说明的是EFI 与 UEFI 在该文档及本文中可互换使用——UEFI 是 EFI 规范的后续演进名称二者描述的是同一套固件接口体系。一、x86_64 UEFI 支持的整体脉络x86_64 平台上的 Linux 内核 UEFI 支持分为两条主线EFI boot stub 方式内核的 bzImage 被改造为同时具备 PE/COFF 镜像头能够伪装成 EFI 可执行程序直接被 UEFI 固件的加载器firmware loader识别并执行。这种方式不再需要传统意义上的独立引导加载程序如 GRUB、elilo——从某种意义上说EFI boot stub 本身就扮演了引导加载程序的角色。详见 Documentation/admin-guide/efi-stub.rst。独立 bootloader 方式由 GRUB 等引导加载程序负责加载内核内核侧仅需配合提供 EFI 运行时服务支持。无论采用哪种方式内核都依赖一组共同的 UEFI 基础设施。在源码中x86 侧的 EFI stub 入口与 bzImage 头部改造位于 arch/x86/boot/header.S而 EFI 相关的启动路径实现位于 drivers/firmware/efi/libstub/x86-stub.c跨架构共享的 EFI stub 代码则集中在 drivers/firmware/efi/libstub 目录下。二、构建内核必须开启的配置项在 x86_64 UEFI 平台上构建内核文档给出如下基准配置CONFIG_FB_EFIy CONFIG_FRAMEBUFFER_CONSOLEyCONFIG_FB_EFI启用基于 EFI 图形输出协议GOP / Graphics Output Protocol的 framebuffer 驱动。它对应的驱动实现是 drivers/video/fbdev/efifb.c由 drivers/video/fbdev/Makefile 中的obj-$(CONFIG_FB_EFI) efifb.o编译进内核。efifb 驱动会从内核启动时固件写入的screen_infostruct screen_info中读取显存基址、分辨率、行距等信息来注册 framebuffer从而在启动早期即可显示内核日志。CONFIG_FRAMEBUFFER_CONSOLE则让内核在 framebuffer 之上运行文本控制台使得启动信息能直接显示在屏幕上这对 UEFI 平台通常无传统 VGA 文本模式尤其重要。如果希望使用EFI 运行时服务例如访问 EFI 变量、设置启动项、读取固件信息还需要开启CONFIG_EFIy CONFIG_EFIVAR_FSy 或 m # 可选CONFIG_EFI在 arch/x86/Kconfig 中定义为 EFI runtime service support其说明明确指出该选项使内核能够使用 EFI 固件提供的运行时服务如 EFI 变量服务且仅对具有 EFI 固件的系统有意义同时注意它depends on ACPI即依赖 ACPI 支持。即使开启该选项编译出的内核在非 EFI 平台上依然可以正常启动。CONFIG_EFIVAR_FS在 fs/efivarfs/Kconfig 中定义为 EFI Variable filesystem是tristate选项且default m。它对应的实现是 fs/efivarfs 目录下的 efivarfs 文件系统。根据其 Kconfig 帮助文本efivarfs 是旧 sysfs EFI 变量接口的替代品突破了旧接口 1024 字节的变量大小限制。编译为模块时模块名为efivarfs。挂载方式通常为mount -t efivarfs efivarfs /sys/firmware/efi/efivars。补充说明CONFIG_EFI_STUB见 arch/x86/Kconfig是与 EFI boot stub 方式直接相关的选项定义为 EFI stub supportdepends on EFI并select RELOCATABLE。它的作用是让 bzImage 能被 EFI 固件无需 bootloader 直接加载。若使用独立 bootloader 方式则此选项并非必需。三、创建 EFI 系统分区ESPEFI 固件只能从 FAT 文件系统上加载可执行程序因此必须在磁盘上准备一个带EFI System标志的 VFAT 分区。文档给出使用fdisk的完整操作序列1. g - 初始化 GPT 分区表 2. n - 创建新分区 3. t - 将分区类型改为 EFI System类型号 1 4. w - 写入并保存修改随后用mkfs将该分区初始化为 VFAT 文件系统mkfs.fat /dev/your-partition实际命令示例将/dev/sda上的新分区格式化为 FAT32mkfs.fat -F 32 /dev/sda1创建完成后建议将该分区挂载到/boot/efi或/efi之类的挂载点供后续部署启动文件使用。四、部署启动文件EFI stub 与独立 bootloader4.1 使用 EFI stub直接复制 bzImage如果采用 EFI stub 方式内核 bzImage 本身就是 EFI 可执行程序。只需把它复制到 ESP 分区上的固定路径EFI/boot/bootx64.efi固件就会在开机时自动发现并启动它cp arch/x86/boot/bzImage esp-mount-point/EFI/boot/bootx64.efi这是 UEFI 规范约定的默认启动路径\EFI\BOOT\BOOTX64.EFI适用于未配置任何启动项的 x86_64 固件。根据 Documentation/admin-guide/efi-stub.rst 的说明复制时必须保留.efi扩展名即重命名为bootx64.efi否则 EFI 固件加载器会拒绝执行并且由于 EFI 固件不支持普通 Linux 文件系统bzImage.efi无法从 ext4 等常规文件系统上直接执行。关于内核参数与 initramfs 的传递方式详见 Documentation/admin-guide/efi-stub.rst。其要点包括在 EFI shell 中参数直接跟在镜像名之后例如fs0: bzImage.efi consolettyS0 root/dev/sda4唯一的 EFI stub 专有命令行参数是initrd用于指定 initramfs。路径必须是从 ESP 根开始的绝对路径相对路径不生效且使用EFI 风格的反斜杠分隔目录。例如在fs0:\Kernels目录下启动并加载fs0:\Kernels\initrd-large.imgfs0:\Kernels bzImage.efi initrd\Kernels\initrd-large.img注意镜像名可以写相对路径因为镜像由理解相对路径的 EFI shell 解释执行而命令行其余部分原样传给内核。ARM/arm64 平台另有dtb参数用于覆盖固件提供的设备树x86 平台一般不涉及。4.2 使用独立 bootloader如果采用自定义的引导加载程序如 GRUB则跳过 4.1 的路径约定按所选 bootloader 自身的文档配置启动项即可。内核侧只需保证第二节中的配置项正确开启。五、EFI 运行时服务异常时的内核命令行参数文档特别给出了三类用于关闭或绕过 EFI 运行时服务的命令行参数用于固件实现存在缺陷、部分运行时服务失效的场景参数作用noefi关闭所有EFI 运行时服务reboot_typek关闭 EFI 重启reboot运行时服务add_efi_memmap将 EFI 内存映射中额外的物理内存条目并入内核可用内存映射下面结合源码逐一说明其底层机制。5.1noefi整体关闭 EFI 运行时服务noefi在 drivers/firmware/efi/efi.c 中注册为 early 参数static bool disable_runtime IS_ENABLED(CONFIG_EFI_DISABLE_RUNTIME); static int __init setup_noefi(char *arg) { disable_runtime true; return 0; } early_param(noefi, setup_noefi); bool efi_runtime_disabled(void) { return disable_runtime; }可以看到noefi将全局disable_runtime置为true内核各子系统通过efi_runtime_disabled()判断是否跳过 EFI 运行时服务调用。CONFIG_EFI_DISABLE_RUNTIME定义于 drivers/firmware/efi/Kconfig可使该默认值在编译期即为 true。该参数在 Documentation/admin-guide/kernel-parameters.txt 中的描述为 Disable EFI runtime services support并标注为[EFI,EARLY]即必须在启动早期生效。适用场景固件运行时服务实现有缺陷例如调用后系统挂死、返回错误数据、或出于安全/调试目的完全不需要 EFI 运行时服务时。注意关闭运行时服务后efivarfs 等依赖运行时服务的功能将不可用。5.2reboot_typek仅关闭 EFI 重启服务reboot_typek是 x86 平台通用的重启方式选择参数k即BOOT_KBD键盘控制器重启。在 arch/x86/kernel/reboot.c 中reboot_type可取BOOT_ACPI、BOOT_BIOS、BOOT_EFI、BOOT_CF9_FORCE、BOOT_KBD、BOOT_TRIPLE等值其中 EFI 重启方式由 DMI 探测回调set_efi_reboot()自动选择static int __init set_efi_reboot(const struct dmi_system_id *d) { if (reboot_type ! BOOT_EFI !efi_runtime_disabled()) { reboot_type BOOT_EFI; ... } return 0; }注意此处同样检查了efi_runtime_disabled()——如果前面传了noefiEFI 重启方式也不会被选用。当某些机器无法正确处理默认 ACPI 重启方式时需要改用 EFI 重启方式反之若固件的 EFI ResetSystem 运行时服务有缺陷导致重启卡死则可通过reboot_typek强制改用键盘控制器方式完成重启。Documentation/admin-guide/kernel-parameters.txt 中列出reboot_type的合法取值为bios, acpi, kbd, triple, efi, pci之一即reboot_typekbd、reboot_typeefi等写法同样有效。5.3add_efi_memmap并入 EFI 内存映射当 EFI 内存映射Memory Map中存在未出现在 E820 映射中的额外条目时可用add_efi_memmap将这些区域并入内核的可用物理内存视图。其实现位于 arch/x86/platform/efi/efi.cstatic int add_efi_memmap __initdata; static int __init setup_add_efi_memmap(char *arg) { add_efi_memmap 1; return 0; } early_param(add_efi_memmap, setup_add_efi_memmap);do_add_efi_memmap()遍历 EFI 内存描述符并按照类型映射为对应的 E820 类型后通过e820__range_add()合并进内核的 E820 表例如EFI_LOADER_CODE、EFI_LOADER_DATA、EFI_BOOT_SERVICES_CODE/DATA、EFI_CONVENTIONAL_MEMORY映射为E820_TYPE_RAM带 Write-Back 属性时软保留内存例外EFI_ACPI_RECLAIM_MEMORY映射为E820_TYPE_ACPIEFI_ACPI_MEMORY_NVS映射为E820_TYPE_NVSEFI_PERSISTENT_MEMORY映射为E820_TYPE_PMEM其余运行时服务代码/数据、MMIO 等一律归入E820_TYPE_RESERVED。该参数在 Documentation/admin-guide/kernel-parameters.txt 中标注为[EFI,X86,EARLY]。适用场景固件通过 EFI 内存映射报告了 E820 中缺失的可使用物理内存例如某些传统 E820 上限 128 条描述符限制之外的区域需要让内核把这些内存纳入管理时。从源码结构看add_efi_memmap默认关闭默认值为 0且当启用软保留内存EFI_MEMORY_SP时会自动触发完整的 EFI 内存映射导入。六、排障思路小结综合文档与源码x86_64 UEFI 平台常见的启动/运行异常可按如下思路排查内核无法被固件加载确认内核开启了CONFIG_EFI_STUB且 bzImage 已复制到 ESP 的EFI/boot/bootx64.efi保留.efi扩展名确认 ESP 分区类型标志为 EFI System 且文件系统为 VFAT。启动无显示输出确认CONFIG_FB_EFIy与CONFIG_FRAMEBUFFER_CONSOLEy均已开启efifb 驱动drivers/video/fbdev/efifb.c依赖固件在screen_info中填充的显示信息。EFI 变量无法读写确认CONFIG_EFIy与CONFIG_EFIVAR_FSy/m并检查是否误传了noefi它会关闭全部运行时服务。重启/关机异常按序尝试reboot_typeefi、reboot_typekbd、reboot_typeacpi、reboot_typebios等取值观察哪种方式在当前固件上可用。物理内存偏少若 EFI 内存映射中包含 E820 之外的可用内存可在早期阶段传入add_efi_memmap将其并入。这些参数均在启动早期early_param解析生效因此修改后需在 bootloader 或 EFI stub 的命令行中配置并重新引导。结语x86_64 平台上的 UEFI 支持涉及固件接口、内核构建选项、磁盘分区布局与运行时服务四层内容。本文以 Documentation/arch/x86/x86_64/uefi.rst 为骨架完整覆盖了其给出的配置项、fdisk 建区步骤、bzImage 部署方式与三条排障参数并结合 arch/x86/platform/efi/efi.c、drivers/firmware/efi/efi.c、arch/x86/kernel/reboot.c 等源码验证了noefi、reboot_typek、add_efi_memmap的实际实现路径。进一步的引导细节如initrd参数传递、ARM 平台的dtb选项可继续阅读配套文档 Documentation/admin-guide/efi-stub.rst。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考