U-Boot源码结构:顶层目录、board、arch、common、drivers等关键目录解析
拿到U-Boot源码包,第一件事是什么?我个人的习惯是——先看目录结构。别急着编译,别急着改代码。你得先搞清楚,这个庞大的工程里,每个文件夹是干什么的。
U-Boot的源码结构,说白了就是一个嵌入式操作系统的微缩版。它有自己的硬件抽象层、驱动框架、命令行解析器,甚至还有文件系统支持。你想想看,一个能在裸机上跑起来的bootloader,要管内存初始化、设备枚举、网络协议栈……这复杂度可不低。
2.1 顶层目录:U-Boot的"门面"
解压U-Boot源码后,你看到的顶层目录大概有20-30个文件夹。我挑几个最重要的说。
| 目录名 | 作用 | 我眼中的重要性 |
|---|---|---|
board/ |
板级初始化代码,每个板子一个子目录 | ★★★★★ 移植必改 |
arch/ |
CPU架构相关代码(ARM、RISC-V、x86等) | ★★★★★ 选对架构就行 |
common/ |
通用功能模块(命令行、环境变量、启动流程) | ★★★★☆ 理解启动流程必看 |
drivers/ |
设备驱动(串口、网卡、MMC、NAND等) | ★★★★☆ 调试硬件时频繁进出 |
include/ |
头文件、配置宏、数据结构定义 | ★★★★★ 配置系统核心 |
lib/ |
通用库函数(字符串、数学、压缩算法) | ★★★☆☆ 一般不动 |
net/ |
网络协议栈(TFTP、NFS、DHCP) | ★★★☆☆ 网络启动时关注 |
fs/ |
文件系统支持(FAT、EXT4、UBIFS) | ★★★☆☆ 需要读文件系统时看 |
tools/ |
主机端工具(mkimage、fdt工具等) | ★★☆☆☆ 打包镜像时用 |
doc/ |
文档说明 | ★★☆☆☆ 遇到问题翻一翻 |
board/ 和 include/configs/ 这两个目录。前者告诉你板子怎么初始化,后者告诉你功能怎么配。这两个搞定了,移植就完成了一半。
2.2 board/ 目录:板级代码的"根据地"
board/ 目录下,每个板子厂商都有自己的子目录。比如 board/ti/am335x/、board/freescale/imx6/。这里面放的是什么呢?
- 板级初始化函数:
board_init_f()、board_init_r()、dram_init()等 - 板级特定配置:GPIO复用、时钟树设置、DDR参数
- 低电平驱动:板载外设的初始化代码
我记得有一次移植一个新板子,DDR死活初始化不过。折腾了两天,最后发现是 board/ 目录下的 dram_init() 函数里,时序参数写错了。嗯,这种坑,踩过一次就记住了。
arch/ 目录里。千万别这么干!板级代码就应该放在 board/ 下,这是U-Boot的约定。乱放会导致后续维护困难,别人接手你的代码时想骂人。
2.3 arch/ 目录:CPU架构的"大本营"
arch/ 目录按CPU架构划分。ARM、RISC-V、x86、MIPS……每个架构一个子目录。以ARM为例:
arch/arm/
├── cpu/ # CPU核心代码(异常处理、MMU、cache)
│ ├── armv7/ # Cortex-A7/A8/A9
│ ├── armv8/ # Cortex-A53/A72
│ └── ...
├── include/ # 架构相关头文件
├── lib/ # 架构通用函数(如 cache 操作)
├── mach-*/ # SoC厂商代码(mach-sunxi、mach-imx等)
└── dts/ # 设备树文件
这里有个关键点:你移植时基本不用改 arch/ 下的代码。除非你用的是全新的CPU核,或者要修复某个架构的bug。大多数情况下,你只需要选对架构,然后通过配置宏来适配。
说白了,arch/ 是U-Boot团队帮你写好的。你只需要告诉它"我用的是ARMv7",它就会把对应的启动代码、异常向量表、MMU配置都准备好。
2.4 common/ 目录:U-Boot的"大脑"
common/ 目录存放的是U-Boot的核心逻辑。这里面的代码,与硬件无关。比如:
main.c:U-Boot的主循环,命令行解析入口cli.c:命令行交互实现env_*.c:环境变量存储与加载(env_nand.c、env_mmc.c、env_sf.c)bootm.c:启动内核的核心函数autoboot.c:自动启动逻辑
我建议你花点时间读一读 common/main.c 里的 main_loop() 函数。它只有几十行,但把U-Boot的启动流程串起来了:检查环境变量、等待用户输入、超时自动启动。你想想看,整个bootloader的"大脑"就在这里。
common/ 目录的代码,你几乎不需要修改。但你必须理解它。因为你的板级代码最终会被 common/ 里的启动流程调用。搞不懂这个调用关系,移植就是瞎蒙。
2.5 drivers/ 目录:硬件驱动的"仓库"
drivers/ 目录下,每个子系统一个子目录:
drivers/
├── mmc/ # MMC/SD卡驱动
├── net/ # 网卡驱动(设计以太网、RTL8169等)
├── serial/ # 串口驱动
├── spi/ # SPI控制器驱动
├── i2c/ # I2C控制器驱动
├── gpio/ # GPIO驱动
├── mtd/ # NAND Flash、NOR Flash驱动
├── usb/ # USB主机/设备驱动
└── ...
移植时,你主要关注 mmc/、serial/、net/ 这三个。为什么?因为:
- 串口:没有串口驱动,你看不到任何打印信息
- MMC:你要从EMMC启动,MMC驱动是必须的
- 网卡:调试阶段,网络启动比从存储介质启动方便得多
我记得有一次调一个板子,串口死活没输出。查了半天,发现是 drivers/serial/ 里的波特率计算公式和实际晶振频率对不上。这种问题,光看代码是看不出来的,必须拿示波器量波形。
2.6 核心知识结构图
下面这张图,把U-Boot源码的核心目录和它们之间的关系画出来了。你移植时,重点关注蓝色节点。
2.7 其他重要目录
除了上面四个核心目录,还有几个你迟早会碰到的:
include/configs/:板级配置头文件。每个板子一个.h文件,里面用宏定义开启或关闭功能。移植时,这个文件是你的"主战场"。include/dt-bindings/:设备树绑定头文件。如果你用设备树,这里定义了各种中断号、时钟号、GPIO号的宏。scripts/:编译脚本、配置脚本。Kconfig 配置文件也在这里。configs/:默认配置文件。每个板子一个_defconfig文件,比如am335x_evm_defconfig。
include/configs/ 下的配置文件,复制一份,然后逐行修改。别从零开始写,那太容易漏东西了。
2.8 总结一下
U-Boot的源码结构,其实就三句话:
- board/ 管板子差异,移植时改这里
- arch/ 管CPU差异,选对架构就行
- common/ + drivers/ 管通用逻辑和驱动,理解它们怎么被调用