3. U-Boot 源码结构解析:顶层目录、board、arch、common、drivers 等关键目录作用

拿到 U-Boot 源码包,第一件事是什么?

我个人习惯,不是急着编译,而是先看看目录结构。你想想看,一个几十万行代码的工程,如果连文件怎么组织的都不清楚,后面出了问题根本无从下手。今天我们就来拆解 U-Boot 的源码骨架。

3.1 顶层目录:一眼看穿 U-Boot 的骨架

解压 U-Boot 后,顶层目录大概有 30 多个文件夹。我刚开始接触时也觉得眼花,但后来发现,真正核心的其实就几个。咱们先看一张整体结构图:

U-Boot 顶层目录核心结构 U-Boot 根目录 arch/ (架构相关) board/ (板级) common/ (通用) drivers/ (驱动) include/ (头文件) lib/ (库函数) tools/ (工具) arm/ x86/ riscv/ 厂家目录 板级文件 配置文件 serial/ net/ 💡 核心思路:arch 管 CPU 差异,board 管板级差异,drivers 管外设差异 移植时主要改 board/ 和 arch/,驱动问题去 drivers/ 找

这张图我建议你保存下来。每次改代码前,先想想你要动的是哪个目录。

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/。这个思路能帮你省下大量翻代码的时间。