1. 课程导入

做嵌入式开发这些年,我接触过不少 bootloader。从早期的 RedBoot 到 Barebox,各有各的脾气。但要说哪个最普及、社区最活跃、文档最全——那还得是 U-Boot。说白了,它就是嵌入式世界的「标准件」。今天咱们就从它的历史、定位和源码结构聊起。

1.1 U-Boot 的历史与作用

U-Boot 的全称是 Das U-Boot,德语里是「潜艇」的意思。这个名字挺有意思,暗示它像潜艇一样潜伏在系统底层。

它最早源于 1999 年的 PPCBoot 项目,专门给 PowerPC 平台用。后来 ARM 火了,社区把它扩展成了 U-Boot。我记得 2010 年左右我刚入行时,U-Boot 还只支持几十款板子。现在呢?上千种 SoC 和开发板都能跑。

U-Boot 的核心作用就三件事:

  • 初始化硬件——时钟、DDR、串口、存储控制器,让板子「活过来」
  • 加载操作系统——从 Flash、SD 卡、网络把内核和设备树搬到内存
  • 提供交互环境——通过串口命令行调试、烧写、测试硬件

一句话总结:U-Boot 是硬件和操作系统之间的「翻译官」。没有它,CPU 上电后只能跑死循环。

1.2 U-Boot 在嵌入式系统中的位置

你想想看,一块嵌入式板子上电后,执行顺序是这样的:

  1. ROM Code(芯片内部固件)——初始化最基础的时钟和存储,加载 U-Boot SPL
  2. SPL(Secondary Program Loader)——初始化 DDR,加载完整的 U-Boot
  3. U-Boot 主程序——初始化外设,加载内核
  4. Linux 内核——接管系统

U-Boot 就站在第 2 和第 3 步。它不像 BIOS 那样固化在主板上,而是存在 Flash 里,可以随时更新。这一点在项目开发阶段特别方便——我经常一天刷十几次 U-Boot,调试新板子的 DDR 参数。

我的经验:很多新手以为 U-Boot 只负责「引导内核」。其实它还是个强大的硬件测试工具。我曾经用 U-Boot 的 md/mw 命令直接读写寄存器,定位过好几起 DDR 布线问题。

1.3 U-Boot 源码结构概览

U-Boot 的源码目录乍一看挺吓人,但摸清规律后其实很清晰。我习惯把它分成几个大块:

目录/文件 作用 我关注的点
arch/ CPU 架构相关代码(arm、riscv、x86 等) 启动汇编、异常向量表、cache 操作
board/ 具体板级代码(DDR 参数、GPIO 配置) 移植时改得最多的目录
drivers/ 外设驱动(串口、网卡、MMC、SPI 等) 调试新硬件时重点看
common/ 通用命令和框架代码 cli、bootm、环境变量处理
include/ 头文件和配置文件 configs/ 下的板级配置
net/ 网络协议栈 tftp、nfs 引导相关
fs/ 文件系统支持(ext4、fat、ubifs) 从存储设备读内核时用到
tools/ 主机端工具(mkimage、fdt 处理) 制作 U-Boot 镜像时必用

嗯,这里要注意:arch/arm/ 下面还有 cpu/ 子目录,里面按厂商分(armv7、armv8、tegra、omap 等)。我第一次移植时就在这迷过路——找了半天没找到 start.S,后来才发现它在 arch/arm/cpu/armv7/ 里。

避坑指南:我曾经犯过一个低级错误——直接在 board/ 目录下改别人的板级文件,结果编译出来跑不起来。后来才明白,U-Boot 的板级代码和配置是强耦合的。改板子一定要新建目录,别偷懒。

1.4 U-Boot 启动流程全景图

为了让你对 U-Boot 的整体脉络有个直观印象,我画了张流程图。这张图涵盖了从 CPU 上电到加载内核的完整路径:

U-Boot 启动流程全景图 U-Boot 启动流程全景图 阶段1:ROM Code(芯片内部) 阶段2:SPL(初始化DDR) 阶段3:U-Boot主程序(初始化外设、命令行) ↓ 自动引导 或 进入命令行 阶段4:加载内核 + 设备树(DTB) 阶段5:启动内核(bootm命令) 内核加载来源(U-Boot 支持多种介质) Flash (NOR/NAND) SD/MMC 卡 网络 (TFTP/NFS) USB 存储

这张图里,阶段 2 和阶段 3 是我们移植工作的重点。SPL 负责把 DDR 初始化好,主程序负责把外设驱动起来。我见过不少项目在 SPL 阶段卡住——DDR 参数配错了,串口死活没输出。这时候连调试都难,只能靠逻辑分析仪。

1.5 为什么选择 U-Boot?

你可能要问:市面上 bootloader 那么多,为什么偏偏选 U-Boot?

  • 社区活跃——邮件列表每天几百封,遇到问题基本能找到答案
  • 架构支持广——ARM、RISC-V、x86、MIPS、PowerPC,全都有
  • 驱动模型统一——从 2016 年开始引入 DM(Driver Model),写驱动比以前规范多了
  • 调试工具丰富——md/mw 读写内存、i2c 探测、mmc 读写、网络 ping,基本够用

我的建议:如果你刚开始学 U-Boot 移植,别急着看源码。先拿一块成熟的开发板(比如 BeagleBone Black 或 Raspberry Pi),把 U-Boot 编译烧进去,跑起来。亲眼看到串口打印出 U-Boot 的 logo,比读十遍代码都有用。

1.6 本章小结

这一章我们聊了 U-Boot 的来龙去脉、它在系统里的位置,以及源码目录怎么组织。说白了,U-Boot 就是个「硬件初始化 + 内核加载器」,但它的灵活性和社区支持让它成了嵌入式开发的事实标准。