2. U-Boot源码结构:从根目录开始,一步步拆解
拿到U-Boot源码的第一件事,别急着编译。我个人的习惯是,先花半小时把顶层目录扫一遍。说白了,你得知道每个文件夹是干嘛的,不然遇到问题就像无头苍蝇。
U-Boot的源码结构,其实比Linux内核简单得多。它没有复杂的设备树覆盖层,也没有几千个Kconfig选项。但它的目录划分非常清晰——board放板级代码,arch放架构相关,common放通用逻辑,drivers放驱动。咱们一个一个来看。
2.1 源码获取方式
先解决从哪里拿代码的问题。U-Boot的官方仓库在denx.de的Git服务器上,但国内访问速度不太理想。我建议你用以下方式:
- 官方Git仓库:
git clone https://source.denx.de/u-boot/u-boot.git - 国内镜像:清华、中科大的Git镜像站都有同步,速度更快
- 厂商BSP包:如果你用的是NXP、TI、Rockchip的芯片,它们会提供基于某个U-Boot版本的定制包。嗯,这里要注意,厂商的代码往往加了私有的补丁,和主线有差异
2.2 顶层目录结构分析
解压或clone完源码后,在根目录执行ls -l,你会看到类似下面的结构。我挑几个重点目录说:
u-boot/
├── arch/ # 架构相关代码(CPU、SoC)
├── board/ # 板级代码(具体硬件平台)
├── common/ # 通用功能(命令行、环境变量、启动流程)
├── configs/ # 默认配置文件(defconfig)
├── doc/ # 文档
├── drivers/ # 设备驱动(串口、网卡、存储等)
├── dts/ # 设备树源文件
├── fs/ # 文件系统支持(ext4、fat、ubifs等)
├── include/ # 头文件
├── lib/ # 库函数(crc、sha、lzma等)
├── net/ # 网络协议栈
├── scripts/ # 构建脚本
├── tools/ # 主机工具(mkimage、fdt等)
└── Makefile # 顶层构建文件
这个结构从U-Boot诞生到现在,基本没大变过。你想想看,一个项目能保持二十多年的目录布局不变,说明当初的设计确实经得起考验。
核心原则:U-Boot的目录划分遵循架构→SoC→板级的三层抽象。arch/管CPU和SoC通用逻辑,board/管具体板卡的差异化逻辑。这种分层让你移植新板卡时,只需要关注board/目录下的文件。
2.3 关键子目录功能详解
2.3.1 arch/ —— 架构的根
这个目录存放所有与CPU架构相关的代码。进去看一眼:
arch/
├── arm/ # ARM架构(最庞大)
├── x86/ # x86架构
├── riscv/ # RISC-V架构
├── mips/ # MIPS架构
├── powerpc/ # PowerPC架构
└── sandbox/ # 模拟测试架构
每个架构目录下,又按SoC厂商细分。以arm为例:
arch/arm/
├── cpu/ # CPU核心初始化(reset、cache、mmu)
├── dts/ # 该架构的设备树
├── include/ # 架构级头文件
├── lib/ # 架构通用库(如bootm、cache操作)
└── mach-*/ # 各SoC厂商目录(mach-sunxi、mach-rockchip等)
我在移植全志H3的时候,就经常泡在arch/arm/mach-sunxi/里。这里放的是SoC级别的初始化代码,比如时钟配置、引脚复用、DRAM初始化。说白了,这部分代码决定了你的芯片能不能跑起来。
2.3.2 board/ —— 板卡的身份证
这是移植工作的主战场。每个板卡在board/下都有一个专属目录:
board/
├── allwinner/ # 全志系列
│ ├── orangepi_zero/
│ └── nanopi_neo/
├── freescale/ # NXP系列
│ ├── imx6q_sabresd/
│ └── ls1046ardb/
├── rockchip/ # Rockchip系列
│ ├── evb_rk3399/
│ └── tinker_board/
└── ti/ # TI系列
├── am335x_evm/
└── dra7xx_evm/
每个板卡目录里通常包含:
- board.c:板级初始化函数(board_init、board_late_init)
- Kconfig:板级配置选项
- Makefile:编译规则
- imximage.cfg(某些平台):镜像头配置
2.3.3 common/ —— 心脏地带
这个目录存放U-Boot的核心逻辑,不依赖具体硬件。我挑几个关键文件:
| 文件/目录 | 功能 | 我的备注 |
|---|---|---|
| main.c | U-Boot主循环(命令行解析) | 启动后就在这里循环等待用户输入 |
| cli_hush.c | 命令行解释器(类似bash) | 支持管道、变量替换,比你想的强 |
| env_*.c | 环境变量存储(env_nand、env_mmc、env_sf) | 移植时一定要确认存储介质驱动正常 |
| bootm.c | 启动内核的核心函数 | 解析镜像头、跳转到内核入口 |
| autoboot.c | 自动启动逻辑 | 倒计时、bootcmd执行 |
common/里的代码,你不需要全部读懂。但main.c和bootm.c建议精读一遍。我当年就是靠啃这两个文件,才真正理解了U-Boot的启动流程。
2.3.4 drivers/ —— 外设驱动仓库
这个目录的结构和Linux内核的drivers/很像:
drivers/
├── serial/ # 串口驱动(最基础,没有它你啥也看不到)
├── net/ # 网卡驱动(e1000、dwmac、sunxi_emac等)
├── mmc/ # MMC/SD卡驱动
├── spi/ # SPI控制器驱动
├── i2c/ # I2C控制器驱动
├── mtd/ # NAND Flash、SPI Flash驱动
├── usb/ # USB主机/设备驱动
├── gpio/ # GPIO驱动
├── pinctrl/ # 引脚控制驱动
└── video/ # 显示驱动(HDMI、LCD)
每个驱动子目录下,又按芯片厂商或IP核分类。比如drivers/net/里,有sunxi_emac.c(全志)、dwmac.c(DesignWare)、fec_mxc.c(NXP)等。
drivers/serial/开始。串口驱动正常了,你才能看到打印信息。我习惯的做法是:先让串口输出"U-Boot 2024.01",然后逐步添加MMC、网卡、NAND驱动。每一步都验证,不要一口气加完所有驱动再调试。
2.4 其他重要目录
除了上面四个核心目录,还有几个地方值得关注:
- configs/:存放所有板卡的默认配置文件(
orangepi_zero_defconfig)。移植时,你通常从这里复制一份修改 - dts/:设备树源文件。U-Boot从2016年开始全面采用设备树,现在新板卡必须提供dts
- include/configs/:旧版配置头文件(
#define CONFIG_*)。虽然逐渐被Kconfig取代,但很多老项目还在用 - tools/:主机工具,比如
mkimage(制作U-Boot镜像)、fdtgrep(过滤设备树)
2.5 整体架构关系图
下面这张图,帮你理清各个目录之间的调用关系。我画的时候特意把arch/和board/放在底层,因为它们是硬件相关的;common/和drivers/在上层,通过抽象接口调用底层代码。
从这张图你能看出,U-Boot的分层非常清晰。上层代码不直接操作硬件寄存器,而是通过驱动框架调用底层接口。这种设计让你在移植时,只需要关注arch/和board/两个目录,其他部分基本不用动。
2.6 小结
这一章我们把U-Boot的源码结构拆了个遍。你记住几个关键点:
- arch/管CPU和SoC,board/管板级硬件,职责要分清楚
- common/是核心逻辑,drivers/是外设驱动,这两部分基本不用改
- 移植新板卡,从
board/目录开始,配合configs/和dts/的配置 - 源码获取首选主线Git仓库,学习阶段用稳定版