2. 源码获取与目录结构:从官方Git仓库获取U-Boot源码、顶层目录结构分析
好,咱们正式开始动手了。这一章我带你做两件事:第一,把U-Boot源码从官方仓库拉下来;第二,把顶层目录结构掰开揉碎讲清楚。这两步走完,你就能对U-Boot有个整体印象,知道什么东西该去哪找。
2.1 从官方Git仓库获取源码
U-Boot的官方仓库托管在denx.de的Git服务器上。我个人习惯用denx的master分支,因为它是主线,最干净。当然你也可以用u-boot的官方GitHub镜像,但要注意——镜像可能比主线慢一两个commit。
拉取命令很简单:
git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot
git checkout master
嗯,这里要注意:如果你在公司内网,可能连不上source.denx.de。我遇到过这种情况,当时用git://git.denx.de/u-boot.git也不行。后来发现是防火墙把9418端口封了。解决办法是用GitHub镜像:
git clone https://github.com/u-boot/u-boot.git
拉下来之后,看看版本:
git describe --tags
你会看到类似v2024.01-rc3-123-gabcdef这样的输出。这告诉你当前是基于哪个tag,后面有多少个commit。我建议你至少用v2023.10之后的版本,因为早期的目录结构跟现在差别挺大。
git clone --depth=1 --branch v2024.01只拉一个tag,省带宽。但做BSP开发,我建议全量拉取,因为后面你可能需要看历史commit来理解某个改动的原因。
2.2 顶层目录结构概览
拉完代码,ls一下,你会看到一堆目录。别慌,我帮你理一理。U-Boot的顶层目录大概分这么几类:
- arch/ — 架构相关代码。这是BSP开发的核心区域之一。
- board/ — 板级代码。每个板子一个子目录。
- common/ — 通用功能模块。比如命令行、环境变量、启动流程。
- drivers/ — 驱动代码。串口、网卡、存储、显示等。
- include/ — 头文件。全局的配置、数据结构定义。
- lib/ — 库函数。比如字符串处理、数学运算、压缩算法。
- cmd/ — 命令实现。每个
U-Boot>下的命令对应一个文件。 - net/ — 网络协议栈。TFTP、NFS、DHCP等。
- fs/ — 文件系统支持。ext4、FAT、UBIFS等。
- scripts/ — 构建脚本、配置工具。
- tools/ — 主机工具。比如
mkimage、fdtget。 - doc/ — 文档。说实话,有些文档写得比代码还难懂。
我画了一张结构图,帮你理解这些目录之间的关系:
你看,这个结构是分层的。核心层在最上面,它不依赖任何板子。架构层在下面,它依赖CPU架构。板级层再往下,它依赖具体的板子。驱动层在最下面,它被上面所有层调用。功能模块是横向的,它们可以被任何层使用。
2.3 关键目录深度解析
arch/ — 架构相关
这个目录下是按CPU架构分的子目录:
arch/
├── arm/ # ARM架构
├── x86/ # x86架构
├── riscv/ # RISC-V架构
├── mips/ # MIPS架构
├── powerpc/ # PowerPC架构
├── sandbox/ # 模拟器架构(用于测试)
└── ...
每个架构目录下又有cpu/、include/、lib/等子目录。以ARM为例:
arch/arm/
├── cpu/ # CPU初始化、异常向量表
│ ├── armv7/ # Cortex-A7/A8/A9等
│ ├── armv8/ # Cortex-A53/A72等
│ └── ...
├── include/ # 架构相关的头文件
├── lib/ # 架构相关的库函数
├── mach-*/ # 厂商相关的代码(mach-sunxi、mach-rockchip等)
└── dts/ # 设备树源文件
我在做i.MX6ULL的BSP时,经常要改arch/arm/cpu/armv7/下的代码。比如调整CPU频率、配置MMU页表。你想想看,如果连CPU都跑不起来,后面所有工作都是白搭。
board/ — 板级代码
这个目录是按厂商和板子名组织的:
board/
├── freescale/ # 飞思卡尔/恩智浦的板子
│ ├── mx6ul_14x14_evk/
│ └── ...
├── rockchip/ # 瑞芯微的板子
│ ├── evb_rk3399/
│ └── ...
├── sunxi/ # 全志的板子
│ ├── sun8i/
│ └── ...
└── ...
每个板子目录下通常有:
board.c— 板级初始化函数Kconfig— 板级配置选项MAINTAINERS— 维护者信息Makefile— 构建规则
嗯,这里要注意:有些板子代码可能不在board/下,而是放在arch/arm/mach-*/里。这是历史遗留问题,新板子都建议放board/下。
drivers/ — 驱动代码
这是U-Boot里最大的目录,没有之一。它按设备类型分了子目录:
drivers/
├── serial/ # 串口驱动
├── net/ # 网卡驱动
├── mmc/ # MMC/SD卡驱动
├── spi/ # SPI控制器驱动
├── i2c/ # I2C控制器驱动
├── gpio/ # GPIO控制器驱动
├── video/ # 显示驱动
├── usb/ # USB主机/设备驱动
├── pinctrl/ # 引脚控制驱动
├── clk/ # 时钟驱动
├── reset/ # 复位驱动
├── power/ # 电源管理驱动
└── ...
每个子目录下又有具体的芯片驱动。比如drivers/net/下可能有fec_mxc.c(恩智浦的以太网控制器)、dwmac.c(DesignWare的GMAC)等。
drivers/下看到很多xxx-uclass.c文件,那就是DM的uclass实现。
common/ — 通用功能
这个目录放的是U-Boot的核心逻辑:
common/
├── main.c # 主循环
├── cli.c # 命令行接口
├── cli_hush.c # Hush shell解析器
├── env_common.c # 环境变量管理
├── bootm.c # 启动内核的核心函数
├── bootstage.c # 启动阶段计时
├── image.c # 镜像解析
└── ...
我刚开始学U-Boot时,最喜欢看common/main.c。它从board_init_r()开始,一路调用到cli_loop(),整个启动流程一目了然。
include/ — 头文件
这个目录下最重要的文件是:
configs/— 板级配置头文件(.h文件)config.h— 自动生成的配置头文件common.h— 全局通用头文件dm/— 驱动模型相关的头文件fdtdec.h— 设备树解析接口
你可能会问:为什么U-Boot还用.h文件做配置,而不是完全用Kconfig?其实U-Boot现在已经是Kconfig为主了,但有些老板子还在用include/configs/下的头文件。我建议新板子直接用Kconfig,别走回头路。
2.4 目录结构背后的设计思想
U-Boot的目录结构不是随便分的。它背后有几个设计原则:
- 分层隔离 — 架构层、板级层、驱动层互不干扰。你换一块板子,只需要改
board/和arch/下的代码,common/和drivers/基本不用动。 - 模块化 — 每个功能都是一个独立的模块。你想加一个命令,就在
cmd/下加一个文件;你想加一个驱动,就在drivers/下加一个文件。 - 可移植性 — 通过
Kconfig和Makefile,你可以精确控制哪些代码被编译进最终镜像。不需要的驱动一个字节都不会被链接。
说白了,U-Boot的目录结构就是一套「乐高积木」。你只需要选对积木,拼在一起,就能得到一个能跑起来的bootloader。
board/目录复制过来改。结果编译出来死活起不来。后来发现是arch/arm/mach-*/下的代码跟板子不匹配。记住:board/和arch/是强关联的,不能随便混用。
2.5 动手实践:浏览你的U-Boot源码
光看不练假把式。我建议你现在就打开终端,进到U-Boot源码目录,执行以下命令:
# 查看顶层目录
ls -F
# 查看arch下的架构
ls arch/
# 查看board下的板子
ls board/
# 查看drivers下的驱动类型
ls drivers/
# 查看common下的核心文件
ls common/*.c | head -20
你可能会发现,有些目录下有很多文件,有些目录下只有几个。这很正常。比如drivers/serial/下可能有几十个文件,因为串口驱动太多了。而drivers/rtc/下可能只有十几个文件。
嗯,看到这里,你应该对U-Boot的源码结构有了整体认识。下一章,我会带你深入arch/arm/,看看CPU是怎么初始化的。不过在那之前,我建议你先自己翻翻代码,找找你的目标板子对应的目录在哪。
可可精品资料库