3. U-Boot源码结构
好,咱们今天聊聊U-Boot的源码结构。说实话,我第一次接触U-Boot源码时,也被它的目录数量吓了一跳。但别慌,摸清规律后你会发现,它其实挺有条理的。
3.1 顶层目录结构
你下载完U-Boot源码,解压后第一眼看到的,就是顶层目录。我习惯先看README文件,里面通常有编译和配置的说明。但真正干活时,我们关注的是这几个核心目录和文件:
- board/:板级相关代码。每个支持的板子都有自己的文件夹。
- arch/:架构相关代码。ARM、x86、RISC-V等都在这里。
- common/:通用功能模块。命令、环境变量、启动流程等。
- drivers/:驱动代码。网卡、串口、存储等。
- include/:头文件。配置宏定义、数据结构等。
- configs/:默认配置文件。每个板子的
defconfig文件。 - scripts/:编译脚本和工具。
- Makefile:顶层Makefile,整个工程的入口。
嗯,这里要注意:board/和arch/是移植工作的主战场。我当年第一次移植U-Boot,就是在这两个目录里来回折腾。
核心思路:U-Boot的目录设计遵循「分层解耦」原则。架构相关代码在arch/,板级相关代码在board/,通用逻辑在common/。这样你换一块板子,只需要改board/和configs/,架构代码基本不动。
3.2 关键子目录详解
3.2.1 board/ 目录
这个目录下,每个板子一个子文件夹。比如board/ti/am335x/就是TI的AM335x板子。里面通常包含:
board.c:板级初始化函数,比如board_init_f()、board_init_r()。mux.c:引脚复用配置。我踩过坑,引脚配错了,串口死活没输出。Kconfig:板级配置选项。Makefile:编译规则。
个人经验:我建议你在移植新板子时,先找一个参考板,把它的board/目录复制一份,然后改名字和关键配置。这样能省不少时间。
3.2.2 arch/ 目录
架构目录,按CPU架构分。以ARM为例:arch/arm/。里面又细分:
cpu/:CPU核心代码。比如armv7/、armv8/。include/:架构相关的头文件。lib/:架构相关的库函数,比如缓存操作、MMU设置。mach-*/:SoC厂商相关的代码。比如mach-sunxi/是全志的。
为什么会这样分?说白了,ARMv7和ARMv8的启动流程差异很大,必须分开处理。我记得有一次调试一个ARMv8的板子,发现MMU初始化代码在arch/arm/cpu/armv8/里,找了半天才找到。
3.2.3 common/ 目录
这个目录是U-Boot的「大脑」。它包含了:
main.c:U-Boot的主循环,命令解析和执行。command.c:命令注册和查找。console.c:控制台输入输出。env_*.c:环境变量管理。有env_nand.c、env_mmc.c等。board_r.c:第二阶段板级初始化。
你想想看,U-Boot启动后,你敲的命令都是通过common/里的代码处理的。我调试时经常在common/main.c里加打印,看命令执行到哪一步了。
3.2.4 drivers/ 目录
驱动目录,按设备类型分:
| 子目录 | 说明 | 我踩过的坑 |
|---|---|---|
| serial/ | 串口驱动 | 波特率配错,输出乱码 |
| net/ | 网卡驱动 | PHY地址不对,ping不通 |
| mmc/ | MMC/SD卡驱动 | 时序参数不对,读卡失败 |
| mtd/ | NAND/SPI Flash驱动 | 分区表配错,内核加载失败 |
| usb/ | USB驱动 | — |
避坑指南:我曾经在移植网卡驱动时,发现drivers/net/里有个phy/子目录,里面是PHY芯片驱动。如果你用的PHY不在支持列表里,得自己加。别傻乎乎地从头写,找个类似的改改就行。
3.3 配置文件体系
U-Boot的配置体系,说白了就是三件套:defconfig、Kconfig、config.h。我刚开始觉得这玩意儿绕,后来用熟了发现真香。
3.3.1 defconfig 文件
每个板子一个,放在configs/目录下。比如configs/am335x_evm_defconfig。它定义了:
- 架构、CPU、板子类型
- 需要开启的功能(如网络、USB)
- 文本段基地址
举个例子:
CONFIG_ARM=y
CONFIG_ARCH_OMAP2PLUS=y
CONFIG_TARGET_AM335X_EVM=y
CONFIG_NET=y
CONFIG_DM_ETH=y
CONFIG_TEXT_BASE=0x80800000
嗯,这里要注意:CONFIG_TEXT_BASE是U-Boot的运行地址,配错了板子起不来。我吃过这个亏,当时把地址写成了0x80000000,结果和DDR地址冲突,死得很难看。
3.3.2 Kconfig 体系
Kconfig是配置菜单系统。你运行make menuconfig时看到的界面,就是它生成的。它分布在各个目录下:
arch/Kconfig:架构选项board/xxx/Kconfig:板级选项drivers/xxx/Kconfig:驱动选项common/Kconfig:通用选项
我个人习惯是先跑一遍make defconfig,再用make menuconfig微调。这样不会漏掉依赖关系。
3.3.3 config.h 头文件
这是传统配置方式,现在逐渐被Kconfig取代。但很多老项目还在用。它放在include/configs/目录下,比如include/configs/am335x_evm.h。
里面定义的是宏:
#define CONFIG_SYS_TEXT_BASE 0x80800000
#define CONFIG_SYS_MALLOC_LEN (16 << 20)
#define CONFIG_BOOTCOMMAND "run distro_bootcmd"
关键点:新项目建议用Kconfig+defconfig方式。老项目维护时,才去改config.h。我见过一个项目,两种方式混用,结果宏定义冲突,编译报错查了半天。
3.4 知识体系结构图
下面这张图,是我梳理的U-Boot源码结构关系。你一看就明白:
这张图把U-Boot的源码骨架画清楚了。顶层目录是入口,四个子目录是核心,配置文件体系把一切串起来。你移植时,就是在这几个框框里填自己的内容。
我的建议:刚开始别贪多,先把board/和configs/搞明白。这两个目录搞通了,U-Boot移植就完成了一半。剩下的,遇到什么问题再查什么。
好了,这一章就到这里。源码结构是基础,后面我们会深入每个模块的启动流程和调试技巧。记住,U-Boot的代码虽然多,但核心逻辑并不复杂——初始化硬件、加载内核、启动系统,就这三步。