U-Boot 引导加载流程详细分析

本文档详细分析了 U-Boot 的引导加载流程,包括从系统加电到将控制权转移给操作系统内核的完整过程。

1. 系统启动概述

U-Boot 的引导加载流程可以分为以下几个主要阶段:

  1. 硬件初始化阶段:初始化 CPU、内存、时钟等基本硬件
  2. 设备初始化阶段:初始化各种外设,如串口、存储设备、网络设备等
  3. 引导准备阶段:加载环境变量、解析配置信息
  4. 内核加载阶段:从存储设备或网络加载操作系统内核
  5. 启动参数准备阶段:准备内核启动参数和设备树
  6. 控制权转移阶段:将控制权转移给操作系统内核

2. 详细引导流程

2.1 硬件初始化阶段

当系统加电或复位时,CPU 会从预定义的地址开始执行代码。对于 U-Boot,这个过程通常从架构特定的 start.S 文件开始:

  1. 复位向量:CPU 从复位向量开始执行,通常位于闪存的起始地址
  2. 基本硬件初始化
    • 禁用中断
    • 设置 CPU 模式(如 ARM 架构的 SVC 模式)
    • 配置时钟和内存控制器
    • 初始化堆栈
  3. 内存初始化
    • 配置 DRAM 控制器
    • 测试和初始化内存
    • 将 U-Boot 从闪存复制到内存中运行(如果需要)

2.2 设备初始化阶段

在硬件基本初始化完成后,U-Boot 会初始化各种外设:

  1. 串口初始化:配置串口波特率,用于输出调试信息
  2. 存储设备初始化
    • 初始化 MMC/SD 卡控制器
    • 初始化 NOR/NAND 闪存控制器
    • 初始化 SATA/SCSI 控制器(如果有)
  3. 网络设备初始化
    • 初始化以太网控制器
    • 配置网络参数(IP 地址、网关等)
  4. 其他设备初始化:根据板级配置初始化其他必要的设备

2.3 引导准备阶段

设备初始化完成后,U-Boot 会进行引导前的准备工作:

  1. 加载环境变量
    • 从非易失性存储(如闪存、EEPROM)加载环境变量
    • 如果环境变量损坏或不存在,使用默认环境变量
  2. 解析配置信息
    • 读取板级配置信息
    • 解析引导命令和参数
  3. 显示启动信息
    • 显示 U-Boot 版本信息
    • 显示板级信息和硬件配置
    • 显示内存配置信息

2.4 内核加载阶段

引导准备完成后,U-Boot 会根据配置加载操作系统内核:

  1. 确定引导设备
    • 根据环境变量 bootcmd 或用户输入确定引导设备
    • 可能的引导设备包括:MMC/SD 卡、NAND 闪存、网络(TFTP)、USB 等
  2. 加载内核镜像
    • 从引导设备读取内核镜像到内存指定位置
    • 支持多种镜像格式,如 zImage、uImage、FIT 镜像等
  3. 加载设备树
    • 从引导设备读取设备树文件(.dtb)到内存
    • 或者使用内置的设备树
  4. 验证镜像
    • 检查镜像的完整性(如校验和)
    • 对于安全启动,验证镜像的数字签名

2.5 启动参数准备阶段

内核镜像加载完成后,U-Boot 会准备内核启动参数:

  1. 设置内核命令行参数
    • 根据环境变量 bootargs 设置内核命令行
    • 命令行参数包括根文件系统位置、控制台设备、调试选项等
  2. 准备设备树
    • 调整设备树内容(如修改内存大小、添加节点等)
    • 将设备树地址传递给内核
  3. 设置启动参数结构
    • 按照内核要求的格式准备启动参数
    • 对于 ARM 架构,使用 ATAGs 或设备树

2.6 控制权转移阶段

一切准备就绪后,U-Boot 会将控制权转移给操作系统内核:

  1. 设置内核启动环境
    • 禁用中断
    • 刷新缓存
    • 设置 CPU 模式和状态
  2. 跳转到内核入口
    • 对于 ARM 架构,调用 bootmbootz 命令
    • 传递内核地址、设备树地址和启动参数地址
  3. 内核接管
    • 内核开始执行初始化代码
    • U-Boot 的使命完成

3. 引导设备类型

U-Boot 支持从多种设备加载内核:

3.1 存储设备

  1. MMC/SD 卡

    • 命令:mmc readfatloadext2load
    • 示例:fatload mmc 0:1 0x80000000 uImage
  2. NAND 闪存

    • 命令:nand read
    • 示例:nand read 0x80000000 0x200000 0x400000
  3. NOR 闪存

    • 命令:cp.b
    • 示例:cp.b 0x1000000 0x80000000 0x400000
  4. SATA 设备

    • 命令:sata readext2load
    • 示例:ext2load sata 0:1 0x80000000 /boot/uImage

3.2 网络设备

  1. TFTP

    • 命令:tftpboot
    • 示例:tftpboot 0x80000000 uImage
  2. NFS

    • 命令:nfs
    • 示例:nfs 0x80000000 192.168.1.100:/nfsroot/uImage

3.3 其他设备

  1. USB 设备

    • 命令:usb startfatload
    • 示例:usb start; fatload usb 0:1 0x80000000 uImage
  2. 串口

    • 命令:loady
    • 示例:loady 0x80000000

4. 引导命令分析

U-Boot 提供了丰富的引导命令,用于加载和启动内核:

4.1 bootm 命令

用于启动 uImage 格式的内核:

bootm [addr [arg ...]]
  • addr:内核镜像在内存中的地址
  • arg:可选参数,如设备树地址

4.2 bootz 命令

用于启动 zImage 格式的内核:

bootz [addr [initrd[:size]] [fdt]]
  • addr:内核镜像在内存中的地址
  • initrd: initramfs 地址(可选)
  • fdt:设备树地址(可选)

4.3 bootefi 命令

用于启动 EFI 格式的内核:

bootefi image_addr [fdt_addr]
  • image_addr:EFI 镜像在内存中的地址
  • fdt_addr:设备树地址(可选)

4.4 run 命令

用于执行环境变量中定义的命令序列:

run var [...]
  • var:环境变量名称,如 bootcmd

5. 环境变量与引导配置

U-Boot 使用环境变量来配置引导行为:

5.1 关键环境变量

环境变量 描述 示例值
bootcmd 自动引导命令 mmc dev 0; fatload mmc 0:1 0x80000000 uImage; bootm 0x80000000
bootargs 内核命令行参数 console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait
bootdelay 自动引导延迟时间(秒) 3
serverip TFTP 服务器 IP 地址 192.168.1.100
ipaddr 设备 IP 地址 192.168.1.10
netmask 网络掩码 255.255.255.0
gatewayip 网关 IP 地址 192.168.1.1

5.2 配置示例

# 设置引导命令
setenv bootcmd "mmc dev 0; fatload mmc 0:1 0x80000000 uImage; fatload mmc 0:1 0x81000000 dtb; bootm 0x80000000 - 0x81000000"

# 设置内核命令行
setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait"

# 保存环境变量
saveenv

6. 设备树处理

设备树(Device Tree)是 U-Boot 和 Linux 内核之间传递硬件配置信息的重要方式:

6.1 设备树加载

  1. 从存储设备加载

    fatload mmc 0:1 0x81000000 board.dtb
    
  2. 内置设备树

    • U-Boot 可以将设备树编译到镜像中
    • 使用 bootm 命令时自动使用内置设备树

6.2 设备树修改

U-Boot 提供了 fdt 命令来修改设备树:

# 查看设备树节点
fdt list /

# 修改设备树属性
fdt set /memory reg <0x80000000 0x20000000>

# 添加设备树节点
fdt mknode / new_node

7. 安全启动

U-Boot 支持安全启动功能,确保引导过程的安全性:

  1. 数字签名验证

    • 使用密钥对引导镜像进行签名
    • 启动时验证镜像的签名
  2. 安全启动配置

    • 启用 CONFIG_SECURE_BOOT 选项
    • 配置密钥存储位置
  3. 安全启动流程

    • 验证 U-Boot 自身的签名
    • 验证内核镜像的签名
    • 验证设备树的签名

8. 常见问题与解决方案

8.1 内核加载失败

症状:U-Boot 无法从存储设备加载内核

解决方案

  • 检查存储设备是否正确初始化
  • 检查内核镜像路径是否正确
  • 检查存储设备分区是否正确

8.2 内核启动失败

症状:内核加载成功但无法正常启动

解决方案

  • 检查内核命令行参数是否正确
  • 检查设备树是否与硬件匹配
  • 检查内核镜像是否与硬件架构匹配

8.3 网络引导失败

症状:无法通过网络加载内核

解决方案

  • 检查网络连接是否正常
  • 检查 TFTP 服务器是否运行
  • 检查 IP 地址配置是否正确

9. 代码分析

9.1 启动流程代码

U-Boot 的启动流程主要在以下文件中实现:

  1. 架构特定启动代码

    • arch/<arch>/cpu/<cpu>/start.S:CPU 启动代码
    • arch/<arch>/lib/board.c:板级初始化代码
  2. 通用启动代码

    • common/board_f.c:第一阶段初始化
    • common/board_r.c:第二阶段初始化
    • common/main.c:主函数
  3. 引导命令代码

    • cmd/bootm.c:bootm 命令实现
    • cmd/bootz.c:bootz 命令实现

9.2 关键函数

  1. board_init_f():第一阶段初始化函数,初始化硬件
  2. board_init_r():第二阶段初始化函数,初始化设备和环境
  3. do_bootm():bootm 命令处理函数
  4. do_bootz():bootz 命令处理函数
  5. boot_jump_linux():跳转到 Linux 内核

10. 总结

U-Boot 的引导加载流程是一个复杂但有序的过程,从硬件初始化到内核启动,每个阶段都有其特定的功能和职责。理解这个流程对于嵌入式系统开发和调试非常重要。

通过本文档的分析,我们可以看到 U-Boot 提供了灵活、强大的引导加载能力,支持多种设备和启动方式,能够满足不同嵌入式系统的需求。同时,U-Boot 的模块化设计也使得它易于定制和扩展,可以适应各种硬件平台。

掌握 U-Boot 的引导加载流程,对于嵌入式系统开发者来说是一项基本技能,有助于快速定位和解决引导相关的问题,提高系统开发效率。

更多推荐