2. U-Boot源码结构:顶层目录、板级目录、CPU架构目录、驱动目录解析
拿到一份U-Boot源码,第一眼看到几十个文件夹,很多人会懵。我刚开始接触U-Boot时也是这样,对着目录结构发愁——到底该从哪里看起?
其实U-Boot的目录设计很有章法。它把通用代码、硬件相关代码、板级特有代码分得清清楚楚。你只要抓住三条主线:架构无关、架构相关、板级相关,整个源码脉络就清晰了。
核心原则:U-Boot源码按"三层分离"组织——通用层(arch无关)、架构层(arch)、板级层(board)。移植新板卡时,你只需要关注板级层和少量架构层配置。
2.1 顶层目录:U-Boot的"总控室"
先看顶层目录。这些目录是U-Boot的骨架,每个都有明确职责。
| 目录/文件 | 作用 | 我的备注 |
|---|---|---|
arch/ |
CPU架构相关代码(ARM、RISC-V、x86等) | 移植重点目录之一 |
board/ |
板级初始化、外设配置 | 每个板卡一个子目录 |
common/ |
通用命令、控制台、环境变量 | 基本不用动 |
drivers/ |
各类设备驱动 | 串口、网卡、存储都在这里 |
include/ |
头文件、配置宏、板级配置 | configs/ 子目录放板级配置 |
lib/ |
通用库函数(字符串、加密、压缩) | 与架构无关 |
net/ |
网络协议栈(TFTP、NFS、DHCP) | 调试时经常改 |
fs/ |
文件系统支持(ext4、FAT、UBIFS) | 启动内核时用到 |
cmd/ |
命令行命令实现 | 可以自己加命令 |
tools/ |
主机端工具(mkimage、env工具) | 打包镜像用 |
doc/ |
文档、README | 移植前先看这里 |
我个人习惯,拿到一个新板卡,先看 board/ 和 include/configs/。这两个目录直接决定了板卡能不能跑起来。
小技巧:顶层 Makefile 是整个构建的入口。你执行 make menuconfig 时,它调用 scripts/kconfig/ 下的配置系统。想理解编译流程,从 Makefile 开始追是最快的。
2.2 板级目录:你的板卡"身份证"
board/ 目录下每个子目录对应一个板卡或一个厂商系列。比如 board/ti/am335x/ 就是TI AM335x系列板卡的代码。
一个典型的板级目录包含这些文件:
board.c— 板级初始化函数board_init()、board_late_init()Kconfig— 板级配置选项MAINTAINERS— 维护者信息Makefile— 编译规则mux.c— 引脚复用配置(很多板卡都有)u-boot.lds— 链接脚本(有时在arch/下)
我记得有一次移植一块新板卡,板级目录里只有个空壳。我花了整整两天配置DDR参数和时钟树——这些都在 board.c 的 board_init_f() 里完成。
避坑指南:我曾经在 board.c 里漏掉了 gd->bd->bi_arch_number 的赋值,结果内核启动时传参错误,系统直接挂掉。板级目录里的每个函数都有它的作用,不要随意删减。
2.3 CPU架构目录:硬件差异的"隔离层"
arch/ 目录是U-Boot最复杂的部分之一。它按CPU架构分门别类:
arch/
├── arm/ # ARM架构(最常用)
│ ├── cpu/ # 具体CPU型号(armv7、armv8、arm11等)
│ ├── dts/ # 设备树文件
│ ├── include/ # 架构相关头文件
│ ├── lib/ # 架构通用函数(cache、MMU、中断)
│ └── mach-*/ # 厂商特有代码(mach-sunxi、mach-rockchip等)
├── riscv/ # RISC-V架构
├── x86/ # x86架构
├── mips/ # MIPS架构
├── powerpc/ # PowerPC架构
└── sandbox/ # 模拟测试架构
ARM目录下,cpu/ 子目录存放不同ARM核的启动代码。比如 armv7/ 下的 start.S 就是Cortex-A系列的第一条指令入口。
为什么会这样设计?说白了,不同CPU核的初始化流程不一样。ARMv7需要设置SVC模式、关中断、初始化MMU和Cache;RISC-V则要设置CSR寄存器。U-Boot通过 arch/ 目录把这些差异封装起来,上层代码不用关心具体架构细节。
关键点:arch/arm/cpu/armv7/start.S 是U-Boot的"第一滴血"。CPU上电后第一条指令就在这里。它负责设置栈指针、清零BSS段、跳转到 board_init_f()。如果你想理解U-Boot的启动流程,这个文件是必读的。
2.4 驱动目录:设备驱动的"大本营"
drivers/ 目录是U-Boot设备驱动的集合。它按设备类型组织:
| 子目录 | 包含的驱动 | 典型文件 |
|---|---|---|
serial/ |
串口驱动 | ns16550.c、pl011.c |
net/ |
网卡驱动 | designware.c、fec_mxc.c |
mmc/ |
MMC/SD卡驱动 | sdhci.c、dwc_mmc.c |
spi/ |
SPI控制器驱动 | spi_flash.c、omap3_spi.c |
i2c/ |
I2C控制器驱动 | i2c_core.c、mvtwsi.c |
gpio/ |
GPIO驱动 | gpio-uclass.c |
usb/ |
USB主机/设备驱动 | ehci-hcd.c、musb_udc.c |
mtd/ |
NAND/SPI NOR Flash驱动 | nand_base.c、sf_probe.c |
video/ |
显示驱动 | simple_panel.c |
U-Boot现在的驱动模型(Driver Model,简称DM)很成熟。每个驱动都注册为一个 udevice,通过 uclass 统一管理。你想想看,以前写驱动要手动调用初始化函数,现在只需要在设备树里配好,驱动框架自动帮你搞定。
经验之谈:调试串口驱动时,我建议先确认 drivers/serial/ 下有没有现成的驱动。很多SoC的串口都是16550兼容的,直接配寄存器地址和时钟就能用。我踩过最大的坑是忘了配置引脚复用——串口驱动本身没问题,但引脚没切换到UART功能,死活不出log。
2.5 知识体系总览
下面这张图帮你理清U-Boot源码的整体结构。我把它画成了分层架构图,方便你理解各目录之间的关系。
从这张图可以看得很清楚:板级层在最上面,直接面对你的硬件板卡;架构层在中间,隔离不同CPU的差异;驱动层和通用层在下面,提供跨平台的基础服务。这种分层设计让U-Boot能支持上千种板卡,而核心代码改动很小。
2.6 几个实用技巧
最后分享几个我在项目中积累的经验:
- 快速定位板卡配置:执行
ls board/和ls include/configs/,找到你的板卡名。比如include/configs/am335x_evm.h就是AM335x评估板的配置头文件。 - 理解编译依赖:U-Boot的Kconfig系统在
scripts/kconfig/下。执行make时,它会读取_defconfig arch/../Kconfig和board/../Kconfig生成.config。 - 调试驱动加载顺序:在
drivers/下加debug打印,或者在common/board_r.c的init_sequence_r[]数组里跟踪初始化顺序。 - 查看设备树绑定:U-Boot的设备树文件在
arch/arm/dts/下。驱动通过compatible字符串匹配设备节点。
重要提醒:不要直接修改 common/ 和 lib/ 下的通用代码。这些代码被所有板卡共享,改错了会影响整个U-Boot生态。正确的做法是在板级目录或驱动目录里做定制。