2. U-Boot源码结构
好,咱们今天来聊聊U-Boot的源码结构。说实话,我第一次接触U-Boot源码时,也被它的目录数量吓了一跳。但别慌,这东西就像你家的工具箱——看着乱,但每样工具都有它的位置。
U-Boot的源码结构,说白了就是一套模块化、可移植的嵌入式引导代码。它支持几十种CPU架构、上千块开发板,如果没有清晰的目录划分,那维护起来简直是噩梦。我个人习惯把U-Boot源码比作一个「乐高积木盒」——每个目录都是一类积木,你挑出需要的,拼出适合你板子的镜像。
2.1 顶层目录结构解析
我们先从顶层看起。你解压U-Boot源码后,会看到类似这样的目录布局:
u-boot/
├── arch/ # 架构相关代码
├── board/ # 板级相关代码
├── common/ # 通用功能模块
├── configs/ # 板级默认配置文件
├── doc/ # 文档
├── drivers/ # 设备驱动
├── dts/ # 设备树源文件
├── fs/ # 文件系统支持
├── include/ # 头文件
├── lib/ # 库函数
├── net/ # 网络协议栈
├── scripts/ # 构建脚本
├── tools/ # 主机端工具
├── Makefile # 顶层Makefile
└── Kconfig # 顶层Kconfig配置
嗯,这里要注意:arch/ 和 board/ 是U-Boot移植工作的核心区域。我刚开始做移植时,80%的时间都花在这两个目录里。
核心原则:U-Boot通过「架构 → SoC → 板级」三层抽象,实现了代码复用。你改得越少,说明你的板子越「标准」。
2.2 关键子目录功能详解
咱们挑几个最重要的目录,一个一个说清楚。
2.2.1 board/ —— 板级适配层
这个目录存放的是具体开发板的初始化代码。每个板子一个子目录,比如:
board/
├── ti/
│ ├── am335x/ # BeagleBone Black等
│ └── omap5/ # OMAP5系列
├── freescale/
│ ├── mx6ul_14x14_evk/
│ └── ls1046ardb/
└── ...
每个板级目录里通常包含:
board.c—— 板级初始化函数(比如DDR初始化、时钟配置)Kconfig—— 板级配置选项MAINTAINERS—— 维护者信息
我曾经接手过一个项目,板子用的DDR颗粒和参考设计不一样。我花了整整两天在 board.c 里调时序参数。说白了,板级代码就是你和硬件「握手」的地方。
2.2.2 cpu/ —— 但等等,现在叫 arch/
老版本的U-Boot确实有 cpu/ 目录,但新版本已经改成了 arch/。这里存放的是CPU架构相关的代码:
arch/
├── arm/ # ARM架构
│ ├── cpu/ # 具体CPU型号(armv7, armv8等)
│ ├── include/ # 架构头文件
│ └── lib/ # 架构通用库
├── x86/ # x86架构
├── riscv/ # RISC-V架构
└── ...
你想想看,ARM和RISC-V的启动流程能一样吗?当然不能。所以这部分代码是架构隔离的。我建议你只关注你用的架构,其他的先放一边。
2.2.3 drivers/ —— 设备驱动的大本营
这个目录很大,也很重要。它按设备类型分了子目录:
| 子目录 | 功能 | 常见文件 |
|---|---|---|
| serial/ | 串口驱动 | ns16550.c, pl011.c |
| spi/ | SPI总线驱动 | spi_flash.c, fsl_spi.c |
| mmc/ | MMC/SD卡驱动 | mmc.c, sdhci.c |
| net/ | 网络接口驱动 | phy.c, designware.c |
| usb/ | USB主机/设备驱动 | ehci-hcd.c, musb.c |
U-Boot的驱动模型(Driver Model)从2014年开始引入,现在已经是主流。我个人觉得,这套模型比Linux的驱动模型简单多了——你只需要实现 probe 和 remove 两个回调,剩下的框架帮你搞定。
小技巧:如果你要添加一个新驱动,先看看 drivers/ 里有没有类似的。复制粘贴再改,比从头写快得多。我经常这么干。
2.2.4 common/ —— 通用功能模块
这个目录放的是U-Boot的核心逻辑,比如:
main.c—— U-Boot的主循环(命令解析、执行)command.c—— 命令注册与查找console.c—— 控制台输入输出env_common.c—— 环境变量管理
说白了,common/ 就是U-Boot的「大脑」。你敲的每条命令,最终都会经过这里处理。
2.3 Makefile与Kconfig构建系统
这部分是U-Boot编译的「指挥中心」。没有它,你连个镜像都生成不了。
2.3.1 顶层Makefile
U-Boot的构建系统基于GNU Make,顶层Makefile是整个编译过程的入口。它的核心流程是:
- 配置阶段:读取
.config文件(由Kconfig生成) - 依赖生成:自动生成头文件依赖(
.d文件) - 编译阶段:递归进入各子目录编译
- 链接阶段:将所有目标文件链接成
u-boot镜像
常用的Makefile目标:
make defconfig # 使用默认配置
make menuconfig # 图形化配置界面
make # 编译U-Boot
make u-boot.bin # 生成二进制镜像
make u-boot.img # 生成带头部的镜像(用于SPL)
我记得有一次,我改了某个驱动文件,但编译时死活不生效。后来发现是Makefile里的依赖没更新。嗯,这里要注意:修改Makefile后,最好 make clean 再重新编译。
2.3.2 Kconfig配置系统
Kconfig是U-Boot从Linux内核借鉴来的配置系统。它用树形结构管理所有配置选项:
# 示例:drivers/spi/Kconfig
config SPI_FLASH
bool "SPI Flash support"
help
Enable support for SPI Flash devices.
选中后,U-Boot会编译SPI Flash驱动。
Kconfig的配置项会生成 .config 文件,然后被 include/config.h 引用。整个流程是:
Kconfig → .config → autoconf.mk → C代码中的宏
你想想看,如果没有Kconfig,你要手动定义几百个宏,那得多痛苦?Kconfig说白了就是帮你可视化地管理这些宏。
避坑指南:我曾经在 menuconfig 里改了一个配置,但忘了保存。结果编译出来的镜像还是老样子。记住:改完配置一定要选「Save」,或者直接 make savedefconfig。
2.4 知识体系结构图
下面这张图,是我自己整理的U-Boot源码结构关系。你看完应该能明白各个目录是怎么配合的:
2.5 小结
好了,这一章的内容就这些。总结一下:
- 顶层目录:U-Boot按功能模块划分,核心是
arch/、board/、drivers/、common/ - 构建系统:Makefile负责编译,Kconfig负责配置,两者配合生成最终镜像
- 移植要点:改得越少越好,尽量复用现有代码
下一章,我们会深入 arch/ 目录,看看U-Boot是如何在CPU上「活过来」的。到时候见。
一句话记住:U-Boot源码结构 = 架构隔离 + 板级适配 + 驱动复用 + 配置灵活。把这四个点吃透,你就掌握了U-Boot的「骨架」。