2. U-Boot 源码结构:顶层目录与关键子目录解析

拿到一份 U-Boot 源码,第一眼看到几十个文件夹,很多人会懵。我刚开始接触 U-Boot 时也一样,对着目录列表发呆——这玩意儿从哪看起?

其实没那么复杂。U-Boot 的目录结构设计得很清晰,说白了就是「平台相关」和「平台无关」两大块。今天我就带你捋一遍,哪些是核心,哪些是辅助,哪些你平时根本不用碰。

核心观点:U-Boot 源码结构遵循「分层 + 模块化」设计。顶层目录按功能划分,关键子目录对应硬件抽象、驱动框架、命令系统、配置体系四大块。理解了这个骨架,你就能快速定位任何功能代码。

2.1 顶层目录结构总览

先看一张我画的整体框架图,把顶层目录的关系理清楚:

U-Boot 顶层目录结构框架 U-Boot 根目录 平台相关 核心通用 工具/文档 board/ cpu/ include/configs/ dts/ drivers/ common/ lib/ cmd/ net/ fs/ tools/ doc/ scripts/ examples/ 关键说明 • 红色区域:与具体硬件平台绑定,移植时主要修改这里 • 绿色区域:通用代码,所有平台共享,驱动和命令都在这里 • 紫色区域:辅助工具和文档,不参与编译但开发时经常用到 • 箭头表示依赖关系:平台相关代码会调用核心通用模块的接口

这张图我建议你保存下来。每次移植新板子时,对着图找目录,效率高很多。

2.2 board/ 目录——板级硬件描述

board/ 是我个人认为移植时最重要的目录。它存放的是「这块板子长什么样」的信息。

每个板子厂商或型号在这里都有一个子目录,比如:

board/
├── samsung/
│   ├── smdk2440/
│   └── exynos5/
├── ti/
│   ├── am335x/
│   └── omap5/
└── mycompany/
    └── myboard/

每个板级目录里通常包含:

  • board.c:板级初始化代码,比如 DDR 初始化、GPIO 配置
  • Kconfig 和 Makefile:编译配置
  • 有时还有 lowlevel_init.S:最早期的汇编初始化

我的经验:移植新板子时,最快的方法是找一个参考板,把它的 board/ 目录复制一份,然后改 DDR 参数和时钟配置。我在做 i.MX6 项目时就这么干的,省了至少一周时间。

2.3 cpu/ 目录——CPU 架构核心代码

cpu/ 目录存放的是与 CPU 架构紧密相关的代码。说白了,ARM、RISC-V、x86 的启动流程完全不同,这部分就是处理这些差异的。

常见的子目录:

子目录 架构 关键文件
arm/ ARM 系列 start.S, interrupts.c, cache.c
armv7/ Cortex-A7/A8/A9 lowlevel_init.S, cpu.c
armv8/ Cortex-A53/A72 等 64位 start.S, exceptions.S
riscv/ RISC-V start.S, trap.S
x86/ x86 架构 start.S, car.S

这里有个坑,我提醒一下:cpu/ 里的 start.S 是 U-Boot 的第一条指令入口。如果你改了链接脚本或者入口地址,一定要同步修改这里。我曾经因为忘了改 start.S 中的链接地址,折腾了两天才发现是这里的问题。

2.4 drivers/ 目录——驱动框架

drivers/ 是 U-Boot 中最大的目录,没有之一。它实现了所有外设的驱动:串口、网卡、MMC、USB、NAND Flash、SPI、I2C 等等。

目录结构大致如下:

drivers/
├── serial/        # 串口驱动
├── net/           # 网卡驱动(如 DM9000、RTL8169)
├── mmc/           # SD/MMC/eMMC 驱动
├── spi/           # SPI 控制器驱动
├── i2c/           # I2C 控制器驱动
├── mtd/           # NAND/NOR Flash 驱动
├── usb/           # USB 主机/设备驱动
├── gpio/          # GPIO 驱动
└── pinctrl/       # 引脚复用控制

U-Boot 现在全面采用 DM(Driver Model) 框架。你写驱动时,需要实现 DM 规定的接口,比如 probe()、remove()、of_to_plat() 等。

注意:不是所有驱动都用了 DM 框架。一些老旧的板子还在用「legacy」驱动方式。移植时先确认你的板子用的是哪种,否则容易混淆。我建议新项目一律用 DM,维护起来省心很多。

2.5 include/ 目录——配置与头文件

include/ 目录是 U-Boot 的「大脑」。它包含所有全局头文件和板级配置。

重点关注两个子目录:

  • include/configs/:板级配置头文件,比如 smdk2440.h。这里定义了内存大小、环境变量分区、支持的命令列表等。
  • include/dt-bindings/:设备树绑定头文件,定义时钟、中断、GPIO 等宏。

举个例子,一个典型的板级配置头文件片段:

/* include/configs/myboard.h */
#define CONFIG_SYS_TEXT_BASE      0x87800000
#define CONFIG_NR_DRAM_BANKS      1
#define CONFIG_SYS_SDRAM_BASE     0x80000000
#define CONFIG_SYS_SDRAM_SIZE     (512 << 20)  /* 512 MB */
#define CONFIG_ENV_SIZE           0x20000       /* 128 KB */
#define CONFIG_ENV_OFFSET         0xE0000

这些宏直接决定了 U-Boot 的运行地址、内存布局、环境变量位置。改错一个,板子就可能起不来。

2.6 其他关键目录速览

剩下的目录我快速过一遍,你有个印象就行:

目录 作用 什么时候用
common/ 通用函数,如命令行解析、控制台输出 几乎不用改,除非你要加新命令
cmd/ 所有 U-Boot 命令的实现(如 bootm、ping) 想自定义命令时来这里
lib/ 加密库、压缩库、字符串处理等 一般不动,除非要加新算法
net/ 网络协议栈(ARP、ICMP、TFTP 等) 调试网络启动时可能会看
fs/ 文件系统支持(FAT、ext4、UBIFS 等) 需要从文件系统加载内核时用到
tools/ 主机端工具,如 mkimage、env 工具 制作镜像、打包固件时用
dts/ 设备树源文件(.dts) 新板子必须在这里添加设备树

2.7 我的目录导航建议

刚接触 U-Boot 时,别想着把所有目录都看懂。我建议你按这个顺序来:

  1. 先看 board/:找到你的板子目录,看 board.c 和 Kconfig
  2. 再看 include/configs/:找到板级配置头文件,理解宏定义
  3. 然后看 dts/:确认设备树是否正确描述了硬件
  4. 最后看 drivers/:如果某个外设不工作,来这里找对应驱动

一句话总结:U-Boot 源码结构不复杂。平台相关的在 board/、cpu/、include/configs/、dts/;平台无关的在 drivers/、common/、cmd/、lib/。移植时改前者,调试时看后者。记住这个原则,你就能在 U-Boot 源码里来去自如。