2. U-Boot源码结构:顶层目录结构、关键子目录功能解析
拿到一份U-Boot源码,第一反应是什么?我当年第一次解压出来,看到满屏的目录名,说实话有点懵。但别怕,这东西的目录结构其实很有规律。说白了,它就是一套为嵌入式系统量身定制的启动引导程序,每个目录都有自己的使命。
这一章,我带你把U-Boot的顶层目录和关键子目录挨个捋一遍。你搞清楚了这些,后面改代码、加驱动、调分区,心里就有底了。
2.1 顶层目录结构一览
先看个全貌。U-Boot的顶层目录大概有二十多个,我挑最重要的说。你可以在源码根目录下执行 ls -l 看看,大概长这样:
├── arch/ # 架构相关代码(CPU、SoC级)
├── board/ # 板级支持代码(具体开发板)
├── common/ # 通用功能模块
├── configs/ # 默认配置文件
├── doc/ # 文档
├── drivers/ # 设备驱动
├── dts/ # 设备树源文件
├── fs/ # 文件系统支持
├── include/ # 头文件
├── lib/ # 库函数
├── net/ # 网络协议栈
├── scripts/ # 编译脚本
├── tools/ # 主机端工具
├── cmd/ # 命令行命令
├── env/ # 环境变量处理
└── examples/ # 示例代码
嗯,这里要注意:arch/ 和 board/ 是移植工作的核心。我见过不少新手一上来就改 common/ 里的东西,结果编译一堆冲突。其实大部分板级适配,你只需要动 arch/ 和 board/ 就够了。
核心原则:U-Boot采用三层分离设计——架构层(arch)、板级层(board)、通用层(common/drivers)。你改代码时,先想清楚改的是哪一层。
2.2 关键子目录深度解析
2.2.1 arch/ —— 架构的灵魂
这个目录存放的是CPU架构相关的代码。说白了,就是跟具体芯片内核绑定的那部分。比如ARM、RISC-V、x86、MIPS等等。进去看看:
arch/
├── arm/ # ARM架构
│ ├── cpu/ # CPU核初始化(如 cortex-a, armv8)
│ ├── mach-xxx/ # SoC厂商代码(mach-sunxi, mach-rockchip)
│ ├── lib/ # 架构通用库
│ └── dts/ # 设备树
├── riscv/ # RISC-V架构
├── x86/ # x86架构
└── sandbox/ # 模拟测试架构
我个人习惯,拿到一个新板子,先看 arch/arm/cpu/ 下的启动文件。比如 start.S,那是CPU上电后执行的第一段代码。我在项目中遇到过一个问题:某款ARM芯片在低温下启动不稳定,查了半天,最后发现是 arch/arm/cpu/ 里某个时序配置的等待周期太短。嗯,这种坑,不翻底层代码根本想不到。
小技巧:如果你要移植U-Boot到新板子,先找到 arch/ 下最接近的参考平台,复制一份出来改。别从零开始写,那太折磨人了。
2.2.2 board/ —— 板子的身份证
board/ 目录存放的是具体开发板的初始化代码。每个板子一个子目录,里面通常包含:
- board.c —— 板级初始化函数(如
board_init_f,board_init_r) - Kconfig —— 板级配置选项
- MAINTAINERS —— 维护者信息
- Makefile —— 编译规则
举个例子,我手头有块全志H3的板子,它的board目录结构大概是:
board/sunxi/
├── board.c # 板级初始化
├── Kconfig # 配置选项
├── MAINTAINERS # 维护者
└── Makefile # 编译
你想想看,board/ 和 arch/ 的区别在哪?arch/ 管的是CPU怎么跑,board/ 管的是板子上外设怎么配。比如DDR3的时序参数、GPIO的复用功能、电源管理芯片的初始化,这些都在 board/ 里。
我曾经踩过一个坑:某块板子在U-Boot阶段网络不通,我查了三天驱动,最后发现是 board.c 里没有配置PHY的复位引脚。你说冤不冤?所以,板级初始化代码一定要仔细核对原理图。
2.2.3 common/ —— 公共工具箱
common/ 是U-Boot的公共功能模块。这里面的代码不依赖具体硬件,属于"通用逻辑"。比如:
- main.c —— U-Boot主循环(命令解析、启动流程)
- cli.c —— 命令行交互
- bootm.c —— 启动内核的核心函数
- autoboot.c —— 自动启动逻辑
- hush.c —— Shell解析器
这里我要强调一点:尽量不要修改 common/ 下的代码。除非你确实需要改动U-Boot的通用行为,否则改这里会影响所有平台。我见过有人为了加一个调试打印,直接改了 common/main.c,结果提交代码后被review打回来——因为其他板子也被影响了。
警告:修改 common/ 目录下的文件前,先问自己三个问题:这个改动是所有平台都需要的吗?能不能放在 board/ 或 arch/ 里?有没有更局部的实现方式?
2.2.4 drivers/ —— 外设驱动大本营
drivers/ 是U-Boot的设备驱动集合。它的子目录按设备类型划分:
| 子目录 | 功能 | 典型文件 |
|---|---|---|
| serial/ | 串口驱动 | ns16550.c, pl011.c |
| net/ | 网卡驱动 | dw_eth.c, sun8i_emac.c |
| mmc/ | SD/MMC/eMMC驱动 | sunxi_mmc.c, dw_mmc.c |
| spi/ | SPI总线驱动 | spi-sunxi.c, spi-nor.c |
| i2c/ | I2C总线驱动 | i2c-gpio.c, mvtwsi.c |
| gpio/ | GPIO驱动 | sunxi_gpio.c |
| usb/ | USB主机/设备驱动 | ehci-sunxi.c, musb.c |
| video/ | 显示驱动 | sunxi_display.c |
U-Boot的驱动模型(Driver Model)从2014年开始引入,现在已经是主流。它的核心思想是:驱动通过设备树匹配,运行时动态绑定。你写一个新驱动,只需要实现几个回调函数,然后注册到驱动框架里就行。
举个例子,我最近在给一块新板子加网卡驱动。流程大概是:
- 在
drivers/net/下写驱动文件,实现probe、send、recv等接口 - 在设备树中描述网卡硬件信息(基地址、中断、PHY地址)
- 在Kconfig中使能驱动编译
- 重新编译,烧录,测试
你看,有了驱动模型,加驱动变得很规范。不像早期U-Boot,驱动代码散落在各处,找起来都费劲。
2.2.5 其他重要目录
除了上面四个核心目录,还有几个你一定会打交道的:
- configs/ —— 存放各板子的默认配置文件(如
orangepi_zero_defconfig)。编译前先make xxx_defconfig,就是从这里读取配置。 - dts/ —— 设备树源文件(
.dts和.dtsi)。现在新板子基本都靠设备树描述硬件,这个目录越来越重要。 - cmd/ —— U-Boot命令行命令的实现。比如
cmd/bootm.c实现了bootm命令,cmd/nand.c实现了nand命令。你想加自定义命令?在这里加。 - include/ —— 头文件。特别是
include/configs/下,每个板子有一个头文件,里面定义了很多宏开关。老版本U-Boot的配置主要靠这些头文件,现在逐渐被Kconfig替代,但很多项目还在用。 - net/ —— 网络协议栈。U-Boot有自己的TCP/IP协议栈,虽然精简,但够用。支持TFTP、NFS、DHCP等协议。
- fs/ —— 文件系统支持。U-Boot可以读写FAT、EXT4、UBIFS等文件系统,方便从存储设备加载内核。
2.3 知识体系结构图
下面这张图,把U-Boot源码的核心目录和它们的关系画出来了。你看一眼,就能明白整个框架是怎么组织的。
2.4 目录间的协作关系
搞清楚了每个目录是干什么的,还得知道它们怎么配合。我画个简单的启动流程,你就明白了:
- arch/arm/cpu/start.S:上电后第一条指令,设置异常向量、初始化CPU模式
- arch/arm/lib/:调用板级初始化函数
board_init_f() - board/xxx/board.c:执行板级初始化——时钟、DDR、串口
- common/main.c:进入主循环,解析命令行或自动启动
- drivers/:根据设备树加载驱动,初始化外设
- cmd/bootm.c:执行
bootm命令,启动操作系统
你看,这个流程里,arch/、board/、common/、drivers/ 各司其职,环环相扣。我当年调试一块板子时,发现启动到一半就卡住了。顺着这个流程查,最后定位到是 board.c 里DDR初始化参数配错了。嗯,这种问题,如果不了解目录间的调用关系,排查起来就像无头苍蝇。
调试建议:遇到启动问题,先确认卡在哪个阶段。在 board_init_f()、board_init_r()、main_loop() 等关键函数入口加串口打印,能快速定位问题所在目录。
2.5 本章小结
这一章我们把U-Boot的源码目录结构过了一遍。核心就记住三点:
- arch/ 管CPU架构,移植新芯片先看这里
- board/ 管板级硬件,调外设先看这里
- common/ + drivers/ 管通用逻辑和驱动,尽量不动