3. U-Boot 源码结构:顶层目录与关键模块
说实话,我第一次打开 U-Boot 源码树的时候,也有点懵。目录太多了,不知道从哪看起。但干这行久了,你会发现它的结构其实很有章法。今天我就带你捋一遍,哪些目录是核心,哪些是辅助,以及我平时调试时最常翻哪些地方。
3.1 顶层目录:一眼看穿骨架
你解压 U-Boot 后,第一眼看到的顶层目录大概有二十多个。别慌,我帮你分成几类:
| 类别 | 目录名 | 一句话说明 |
|---|---|---|
| 核心代码 | arch/ | CPU 架构相关,启动最关键的汇编代码在这 |
| 核心代码 | board/ | 板级初始化,你的板子独有逻辑 |
| 核心代码 | common/ | 通用命令和核心框架 |
| 核心代码 | drivers/ | 外设驱动,网口、串口、存储都在这里 |
| 构建系统 | configs/ | 默认配置文件,defconfig 文件全在这 |
| 构建系统 | scripts/ | Kconfig 和构建脚本 |
| 构建系统 | Makefile | 顶层 Makefile,入口 |
| 辅助工具 | tools/ | mkimage 等主机端工具 |
| 辅助工具 | doc/ | 文档,但说实话我很少看,不如读代码 |
| 辅助工具 | include/ | 头文件,全局宏定义 |
board/ 和 configs/ 找对应目录。如果找不到,八成得自己从头写板级文件。
3.2 arch/ 目录:CPU 架构的起点
这是 U-Boot 启动的第一站。说白了,arch/ 里放的是跟 CPU 内核绑定的代码。比如 ARM 的异常向量表、x86 的实模式切换、RISC-V 的 M 模式初始化。
我拿 ARM 举例,你进去会看到:
arch/arm/
├── cpu/ # 具体 CPU 型号,如 armv7、armv8
├── include/ # 架构级头文件
├── lib/ # 架构公共函数,如 cache 操作
├── mach-*/ # SoC 厂商目录,如 mach-sunxi、mach-imx
└── start.S # 汇编入口,第一条指令
嗯,这里有个重点:start.S。它是 U-Boot 的入口点。CPU 上电后第一条指令就落在这里。我在项目中遇到过好几次,因为 start.S 里关中断的时机不对,导致调试器连不上。后来我习惯在 start.S 开头加一段 GPIO 点灯,确认 CPU 确实跑起来了。
arch/arm/cpu/ 下的文件,忘了更新 arch/arm/mach-xxx/ 里的时钟配置。结果串口输出全是乱码。记住,mach- 目录管的是 SoC 级别的外设,不是 CPU 核。
3.3 board/ 目录:你的板子,你的代码
board/ 是板级代码的集中营。每个板子一个子目录,比如:
board/ti/am335x/ # TI AM335x 系列
board/freescale/mx6q/ # NXP i.MX6Q
board/samsung/smdk2440/ # 三星 SMDK2440
这里面通常有:
board.c— 板级初始化函数,比如 DDR 参数、GPIO 复用Kconfig— 板级配置选项MAINTAINERS— 谁维护这块板子
你想想看,U-Boot 为什么要把 board/ 和 arch/ 分开?因为 CPU 架构是通用的,但板子上的 DDR 型号、网卡 PHY 地址、LED 引脚都是独一无二的。我见过有人把板级代码塞进 arch/ 里,结果换了个板子,整个架构目录都得改。千万别这么干。
3.4 common/ 目录:命令与核心逻辑
common/ 是 U-Boot 的“大脑”。它包含了:
- 命令处理框架 —
command.c、main.c - 通用命令实现 —
cmd/子目录,比如cmd/bootm.c、cmd/nand.c - 环境变量管理 —
env/子目录 - 启动逻辑 —
bootm.c、bootstage.c
我个人最常翻的是 common/cmd/。比如你想知道 bootm 命令到底怎么解析镜像头,直接看 cmd/bootm.c 就行。代码写得挺清楚,就是函数调用链有点深。
common/ 里加板级相关的 ifdef。我见过有人直接在 common/main.c 里写 #ifdef CONFIG_MY_BOARD,结果代码越来越乱。正确的做法是在 board/ 里实现钩子函数,通过 board_late_init() 等接口回调。
3.5 drivers/ 目录:外设驱动大本营
U-Boot 的驱动模型(Driver Model,DM)现在很成熟了。drivers/ 下面按设备类型分目录:
| 子目录 | 驱动类型 | 常见文件 |
|---|---|---|
| serial/ | 串口驱动 | ns16550.c、pl011.c |
| net/ | 网卡驱动 | designware.c、fec_mxc.c |
| mmc/ | SD/eMMC 驱动 | sdhci.c、dwc_mmc.c |
| spi/ | SPI 控制器驱动 | spi_flash.c、mxc_spi.c |
| i2c/ | I2C 控制器驱动 | i2c_core.c、mv_i2c.c |
| gpio/ | GPIO 驱动 | gpio-uclass.c、mxc_gpio.c |
为什么要单独说 drivers/?因为你在移植 U-Boot 时,80% 的时间都在调驱动。我记得有一次调网卡驱动,死活 ping 不通。最后发现是 drivers/net/ 里的 PHY 驱动初始化顺序不对,先配了 MAC 再配 PHY,导致自动协商失败。调了两天,就改了一行代码。
3.6 其他关键目录速览
- include/ — 全局头文件。
include/configs/里是板级配置头文件,老版本 U-Boot 的核心配置都在这里。新版本虽然用 Kconfig 了,但有些板子还在用。 - configs/ — 默认配置文件。每个板子一个
xxx_defconfig,编译时用make xxx_defconfig加载。 - tools/ — 主机工具。比如
mkimage用来给内核镜像加 U-Boot 头,fdtgrep用来裁剪设备树。 - scripts/ — Kconfig 和构建脚本。如果你要加新的配置选项,得改这里。
3.7 一张图看懂 U-Boot 源码结构
下面这张 SVG 图,是我自己梳理的 U-Boot 源码层级关系。从顶层到底层,你编译和启动时,代码是怎么流动的,一目了然。
3.8 总结一下
U-Boot 的源码结构,说白了就是“分层解耦”。arch/ 管 CPU,board/ 管板子,common/ 管逻辑,drivers/ 管外设。你只要记住这个原则,找代码就不会迷路。
我个人的工作流是这样的:拿到一个新板子,先去 configs/ 找 defconfig,然后去 board/ 看板级初始化,最后去 drivers/ 调外设。如果启动卡住了,90% 的问题出在 arch/arm/cpu/ 的 lowlevel_init 或者 board/ 的 DDR 配置上。
git grep 在源码里搜你的板子名,能快速定位所有相关文件。比如 git grep -l "myboard",比手动翻目录快多了。