3. Bootloader阶段:GRUB/uboot如何加载内核与initramfs镜像

Bootloader 是系统启动流程中承上启下的关键环节。它负责初始化硬件、建立内存映射,并将内核与 initramfs 镜像从存储介质加载到内存中,最终将控制权移交给内核。本章深入解析 GRUB(x86/ARM64 服务器)与 U-Boot(嵌入式设备)两种主流 Bootloader 的加载机制。

3.1 Bootloader 的核心职责与加载流程

无论 GRUB 还是 U-Boot,在加载内核与 initramfs 时都遵循以下通用步骤:

  1. 设备初始化:初始化串口、时钟、内存控制器、存储控制器(MMC/SATA/NVMe)。
  2. 存储驱动加载:加载文件系统驱动(如 ext4、FAT、btrfs)或原始块设备读取能力。
  3. 镜像定位:根据预配置的路径或分区表,找到内核镜像(vmlinuz / Image)与 initramfs 文件。
  4. 内存加载:将内核与 initramfs 拷贝到约定的物理内存地址。
  5. 参数传递:设置内核启动参数(bootargs / cmdline),包括根设备、控制台、DTB 地址等。
  6. 跳转执行:将 CPU 寄存器设置为入口地址,跳转到内核起始代码。
关键差异:GRUB 通常运行在 UEFI 环境,使用文件系统驱动读取磁盘分区;U-Boot 更贴近硬件,常使用 bootm 命令处理打包的镜像格式(如 uImage、fitImage)。
Bootloader 加载内核与 initramfs 流程图 Bootloader 加载内核与 initramfs 流程图 上电 / 复位 硬件初始化(时钟/内存/存储) 加载文件系统驱动 定位并加载内核 + initramfs 设置启动参数 / DTB 跳转到内核入口

3.2 GRUB 加载机制详解

GRUB(GRand Unified Bootloader)是 x86/ARM64 平台上最常用的 Bootloader,支持 UEFI 与 Legacy BIOS 两种模式。其加载内核与 initramfs 的核心机制如下:

3.2.1 镜像定位与读取

GRUB 通过 grub.cfg 配置文件中的 linux 和 initrd 命令指定内核与 initramfs 路径。GRUB 使用自身的文件系统驱动(如 ext2、ext4、btrfs、xfs)直接从磁盘分区读取文件。

# grub.cfg 典型配置片段
menuentry "Linux Kernel" {
    set root=(hd0,gpt2)
    linux   /vmlinuz-6.1.0 root=/dev/sda3 console=tty0 console=ttyS0,115200
    initrd  /initramfs-6.1.0.img
}
加载地址约定:GRUB 将内核加载到约定的物理地址(通常为 0x1000000 或由 UEFI 指定),initramfs 则加载到内核之后的高位内存区域。内核启动时会从 boot_params 结构中读取 initramfs 的起始地址与大小。

3.2.2 启动参数传递

GRUB 通过 setup_header 和内核协议(Linux Boot Protocol)传递参数。关键参数包括:

参数 来源 说明
root= grub.cfg 命令行 指定根文件系统设备(如 /dev/sda3 或 UUID=xxx)
initrd= grub.cfg 命令行 initramfs 文件路径(GRUB 自动转换为内存地址)
console= grub.cfg 命令行 指定控制台设备(如 ttyS0,115200)
quiet grub.cfg 命令行 减少内核启动日志输出

3.2.3 UEFI 启动流程

在 UEFI 系统上,GRUB 作为 UEFI 应用程序(grubx64.efi)运行。UEFI 固件负责加载 GRUB,GRUB 再加载内核与 initramfs。关键区别在于:

  • UEFI 使用 GPT 分区表,GRUB 通过 EFI System Partition 读取配置文件。
  • UEFI 提供 LoadImage / StartImage 服务,GRUB 可直接调用。
  • initramfs 通过 UEFI 的 LoadFile 协议或 GRUB 的文件系统驱动加载。

3.3 U-Boot 加载机制详解

U-Boot 是嵌入式系统(ARM、RISC-V、MIPS)的事实标准 Bootloader。其加载方式更加灵活,支持多种镜像格式与存储介质。

3.3.1 镜像格式与加载命令

U-Boot 支持三种主要的镜像格式:

格式 命令 说明
uImage bootm 传统格式,包含 U-Boot 头部(CRC、加载地址、入口点)
fitImage bootm Flattened Image Tree,支持多配置、签名验证、DTB 打包
zImage / Image bootz 直接加载原始内核镜像(ARM 平台常用)
# U-Boot 加载内核与 initramfs 典型命令序列
# 从 MMC 分区加载内核与 DTB
mmc dev 0
fatload mmc 0:1 0x42000000 kernel.itb
fatload mmc 0:1 0x44000000 board.dtb

# 设置启动参数
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait'

# 启动 fitImage(包含内核、DTB、initramfs)
bootm 0x42000000#conf@board1

# 或分别加载内核与 initramfs
fatload mmc 0:1 0x42000000 zImage
fatload mmc 0:1 0x43000000 initramfs.cpio.gz
bootz 0x42000000 0x43000000 0x44000000

3.3.2 内存布局与地址约定

U-Boot 使用固定的内存地址加载各组件。典型的内存布局如下:

组件 典型加载地址 说明
内核(zImage/Image) 0x42000000 ARM 平台常用起始地址,需避开 U-Boot 自身占用区域
initramfs 0x43000000 紧接内核之后,需保证不重叠
DTB 0x44000000 设备树二进制,通常放在最高地址
U-Boot 自身 0x40000000 附近 由板级配置决定,通常位于低端内存
地址冲突风险:如果内核解压后的大小超过预期,可能覆盖 initramfs 或 DTB 所在区域。建议在 bootm 或 bootz 之前,使用 fdt addr 和 iminfo 检查各镜像的加载地址是否合理。

3.3.3 fitImage 的优势

fitImage 是 U-Boot 推荐的现代镜像格式,它将内核、initramfs、DTB 和配置打包在一个文件中,支持:

  • 多配置选择:同一镜像可包含多个板级配置,通过 #conf@board1 选择。
  • 哈希校验与签名:支持 SHA256 哈希和 RSA 签名验证,防止镜像被篡改。
  • 自动内存布局:U-Boot 根据 fitImage 头部信息自动将各组件加载到指定地址。
# fitImage 的 ITS 配置文件示例(kernel.its)
/dts-v1/;
/ {
    description = "Linux kernel with initramfs and DTB";
    #address-cells = <1>;
    images {
        kernel {
            description = "Linux kernel";
            data = /incbin/("./zImage");
            type = "kernel";
            arch = "arm";
            os = "linux";
            compression = "none";
            load = <0x42000000>;
            entry = <0x42000000>;
            hash-1 { algo = "sha256"; };
        };
        initramfs {
            description = "initramfs";
            data = /incbin/("./initramfs.cpio.gz");
            type = "ramdisk";
            arch = "arm";
            os = "linux";
            compression = "gzip";
            load = <0x43000000>;
            hash-1 { algo = "sha256"; };
        };
        fdt-board1 {
            description = "Flattened Device Tree for board1";
            data = /incbin/("./board1.dtb");
            type = "flat_dt";
            compression = "none";
            hash-1 { algo = "sha256"; };
        };
    };
    configurations {
        default = "conf@board1";
        conf@board1 {
            description = "Board 1 configuration";
            kernel = "kernel";
            ramdisk = "initramfs";
            fdt = "fdt-board1";
            signature-1 { algo = "sha256,rsa2048"; };
        };
    };
};

3.4 GRUB 与 U-Boot 对比总结

特性 GRUB U-Boot
主要平台 x86, ARM64 (服务器) ARM, RISC-V, MIPS (嵌入式)
固件接口 UEFI / Legacy BIOS 裸机 / 自定义板级初始化
文件系统支持 ext4, btrfs, xfs, FAT, NTFS FAT, ext4, ubifs, squashfs
镜像格式 vmlinuz + initramfs (独立文件) uImage, fitImage, zImage
参数传递 内核协议 (setup_header) bootargs 环境变量 + DTB
安全启动 UEFI Secure Boot + shim fitImage 签名 + 可信执行环境
调试手段 GRUB 命令行 + 串口输出 U-Boot 命令行 + 串口 + JTAG

3.5 常见问题与排查思路

场景1:GRUB 提示 "file not found"
检查 grub.cfg 中的 set root 是否正确指向包含内核的分区。使用 GRUB 命令行执行 ls 列出可用分区,ls (hd0,gpt2)/ 查看文件列表。
场景2:U-Boot 启动后内核 panic,提示 "VFS: Unable to mount root fs"
确认 bootargs 中的 root= 参数与 initramfs 中的根设备名称一致。检查 initramfs 是否包含必要的存储驱动(如 mmc_block、ahci)。
场景3:initramfs 加载后内核无法解压
检查 initramfs 的压缩格式是否与内核支持的格式匹配。U-Boot 下使用 iminfo 0x43000000 查看镜像头部信息;GRUB 下检查 initrd 文件是否完整。
GRUB 与 U-Boot 加载架构对比 GRUB 与 U-Boot 加载架构对比 GRUB (x86/ARM64) UEFI 固件 → GRUB EFI 应用程序 文件系统驱动读取 grub.cfg linux /vmlinuz + initrd /initramfs setup_header 传递 boot_params 跳转到内核入口 (0x1000000) U-Boot (ARM/RISC-V) 板级初始化 (SPL → U-Boot proper) fatload / ext4load 加载镜像 bootm / bootz 解析镜像格式 bootargs + DTB 地址传递 跳转到内核入口 (0x42000000) 两者最终目标一致:将内核与 initramfs 加载到内存并跳转

3.6 小结

Bootloader 阶段的核心任务是将内核与 initramfs 从存储介质安全、高效地加载到内存,并传递正确的启动参数。GRUB 依赖 UEFI 服务和文件系统驱动,适合通用计算平台;U-Boot 更贴近硬件,支持灵活的镜像格式与内存布局,适合嵌入式场景。理解两者的加载机制与内存地址约定,是排查启动失败、根文件系统挂载错误的基础。