3. U-Boot 源码结构解析:顶层目录、board、arch、common、drivers 等关键目录作用
拿到 U-Boot 源码包,第一件事是什么?
我个人习惯,不是急着编译,而是先看看目录结构。你想想看,一个几十万行代码的工程,如果连文件怎么组织的都不清楚,后面出了问题根本无从下手。今天我们就来拆解 U-Boot 的源码骨架。
3.1 顶层目录:一眼看穿 U-Boot 的骨架
解压 U-Boot 后,顶层目录大概有 30 多个文件夹。我刚开始接触时也觉得眼花,但后来发现,真正核心的其实就几个。咱们先看一张整体结构图:
这张图我建议你保存下来。每次改代码前,先想想你要动的是哪个目录。
3.2 arch/ 目录:CPU 架构的「方言」
arch/ 目录存放的是与 CPU 架构紧密相关的代码。说白了,ARM 有 ARM 的启动方式,x86 有 x86 的套路,RISC-V 又有自己的玩法。这些差异全在这里隔离。
| 子目录 | 对应架构 | 典型文件 |
|---|---|---|
arch/arm/ |
ARM 系列 | start.S, cpu.c, cache.c |
arch/x86/ |
x86 系列 | start.S, interrupts.c |
arch/riscv/ |
RISC-V 系列 | start.S, trap.c |
arch/sandbox/ |
模拟调试 | 模拟器相关 |
每个架构目录下,通常还有 cpu/、include/、lib/ 子目录。比如 arch/arm/cpu/ 下面会按 ARM 核版本再细分:armv7、armv8 等。
我的经验:移植到新平台时,arch/arm/cpu/ 下的 start.S 是第一个要读的文件。它负责最底层的初始化——关中断、设栈指针、清 BSS 段。这块要是写错了,板子根本跑不起来。我曾经在一个项目里因为栈指针设错,调试了整整两天才找到问题。
3.3 board/ 目录:板级定制的「身份证」
board/ 目录是按厂商和板子型号组织的。比如:
board/
├── freescale/
│ ├── mx6qsabreauto/
│ └── mx6qsabresd/
├── samsung/
│ ├── smdkv310/
│ └── origen/
└── ti/
├── am335x/
└── omap5_uevm/
每个板子目录下,通常包含:
- 板级初始化文件:比如
board.c,负责 DDR 初始化、时钟配置、GPIO 设置 - 板级头文件:定义板子特有的宏和配置
- 配置文件:
Kconfig或MAINTAINERS
我建议你移植时,先找一个和自己板子最接近的参考板,把它的 board/ 目录整个复制过来,再改。这样比从零开始写快得多。
3.4 common/ 目录:U-Boot 的「公共库」
common/ 目录存放的是与架构和板级无关的通用功能。说白了,就是所有板子都能用的代码。
| 文件/子目录 | 功能 |
|---|---|
main.c |
U-Boot 主循环,命令解析入口 |
command.c |
命令注册与分发框架 |
console.c |
控制台输入输出 |
env_common.c |
环境变量公共接口 |
cli_hush.c |
命令行解析器(类似 bash) |
这里有个细节:common/ 里的代码虽然通用,但很多功能可以通过配置裁剪。比如你不需要 hush 解析器,可以在配置里关掉,编译时就不会把 cli_hush.c 编进去。
注意:不要轻易修改 common/ 下的代码。它被所有板子共享,改一处可能影响全局。我见过有人为了加个调试打印,改了 main.c,结果其他板子全编译不过。正确的做法是:如果必须改,用 #ifdef 隔离,或者提 patch 到上游。
3.5 drivers/ 目录:外设驱动的「大仓库」
drivers/ 是 U-Boot 里最大的目录之一。它按设备类型组织:
drivers/
├── serial/ # 串口驱动
├── net/ # 网卡驱动
├── mmc/ # SD/MMC 卡驱动
├── spi/ # SPI 总线驱动
├── i2c/ # I2C 总线驱动
├── gpio/ # GPIO 驱动
├── usb/ # USB 主机/设备驱动
├── mtd/ # NAND Flash、SPI Flash 等
└── video/ # 显示驱动
每个子目录下,通常有:
- 通用框架文件:比如
serial-uclass.c,定义串口驱动的标准接口 - 具体芯片驱动:比如
ns16550.c,实现 16550 UART 的具体操作
移植时,最常打交道的就是 drivers/。比如你的板子用的是某个型号的网卡,就去 drivers/net/ 下找对应的驱动文件。
我的习惯:先确认芯片型号,然后在 drivers/ 对应目录下搜索芯片型号关键字。如果找不到,就去芯片厂商的官方 U-Boot 分支找。实在没有,就只能自己写了——不过这种情况现在越来越少,大部分主流芯片都有现成驱动。
3.6 其他关键目录
除了上面四个,还有几个目录也值得了解:
| 目录 | 作用 |
|---|---|
include/ |
全局头文件,包括 configs/(板级配置)、linux/(兼容 Linux 内核头文件) |
lib/ |
通用库函数,如字符串处理、CRC 校验、时间函数 |
tools/ |
主机端工具,如 mkimage(制作 U-Boot 镜像)、envtools(环境变量工具) |
net/ |
网络协议栈,包括 TFTP、NFS、DHCP 等客户端 |
fs/ |
文件系统支持,如 ext4、FAT、UBIFS |
doc/ |
文档,包括 README、设备树绑定说明 |
这里特别提一下 include/configs/。每个板子都有一个对应的头文件,比如 mx6qsabresd.h。这个文件里定义了板子的所有配置宏:DDR 参数、环境变量位置、支持的命令列表等。移植时,这个文件是修改的重点之一。
3.7 总结:移植时该看哪些目录?
最后,我整理了一个速查表,方便你移植时快速定位:
| 移植任务 | 主要目录 | 次要目录 |
|---|---|---|
| CPU 初始化 | arch/arm/cpu/ |
arch/arm/include/ |
| 板级 DDR/时钟 | board/<vendor>/<board>/ |
arch/arm/mach-<soc>/ |
| 串口驱动 | drivers/serial/ |
include/configs/ |
| 网卡驱动 | drivers/net/ |
include/configs/ |
| 存储驱动 | drivers/mmc/ 或 drivers/mtd/ |
include/configs/ |
| 环境变量 | common/ |
include/configs/ |
| 命令裁剪 | cmd/(旧版在 common/) |
include/configs/ |
嗯,源码结构就讲到这里。记住一个原则:架构差异找 arch/,板级差异找 board/,外设驱动找 drivers/,通用功能找 common/。这个思路能帮你省下大量翻代码的时间。