2. U-Boot 源码结构解析:关键目录、配置系统与编译流程
好,咱们直接进入正题。U-Boot 这玩意儿,说白了就是一个“超级引导小程序”。你把它烧到 Flash 里,上电后它先跑起来,初始化硬件,然后加载内核。要玩转它,你得先知道它的“骨架”长什么样。
我刚开始接触 U-Boot 的时候,第一反应是:这目录也太多了吧?别慌,其实核心的就那么几个。咱们今天就把它的源码结构、配置系统和编译流程彻底捋一遍。
2.1 关键目录:别被文件夹淹没了
U-Boot 的源码根目录下,大概有几十个文件夹。但你记住,真正干活的主要就这几个:
| 目录名 | 作用 | 我的经验 |
|---|---|---|
arch/ |
架构相关代码。ARM、x86、RISC-V 都在这里。 | 调试启动卡死,90% 的问题出在这里的 start.S。 |
board/ |
板级初始化代码。你的板子特有的东西。 | 比如 DDR 参数、GPIO 配置,都在这里。 |
common/ |
通用命令和框架。比如 cmd/ 里的命令实现。 |
你想加个自定义命令?改这里。 |
drivers/ |
驱动。串口、网卡、Flash、MMC 等等。 | 网卡驱动不通?先查这里。 |
include/ |
头文件。特别是 configs/ 下的板级配置。 |
配置宏定义都在 include/configs/ 里。 |
lib/ |
库函数。比如字符串处理、CRC 校验。 | 一般不用动,但调试时偶尔会进去看。 |
tools/ |
主机端工具。比如 mkimage。 |
制作内核镜像时必用。 |
board/ 和 include/configs/。这两个目录决定了你的板子能不能跑起来。
2.2 配置系统:Kconfig 与板级头文件
U-Boot 的配置系统经历过一次“进化”。老版本用的是“板级头文件”方式,新版本引入了 Kconfig。现在两者并存,你得都懂。
2.2.1 老方式:include/configs/ 头文件
这种方式很直接。每个板子有一个头文件,比如 include/configs/rpi.h。里面全是宏定义:
/* 一个典型的板级配置头文件片段 */
#define CONFIG_SYS_TEXT_BASE 0x00200000
#define CONFIG_SYS_MALLOC_LEN (32 << 20) /* 32 MB */
#define CONFIG_BOOTCOMMAND "fatload mmc 0:1 0x1000000 zImage; bootz"
嗯,这里要注意:这种方式的优点是简单粗暴,缺点是“配置爆炸”。你想想看,几百个宏堆在一个文件里,维护起来很痛苦。
2.2.2 新方式:Kconfig
Kconfig 是 Linux 内核那套配置系统。U-Boot 从 2014 年开始引入。它把配置项组织成树形结构,支持依赖和选择。
配置流程是这样的:
- defconfig:板子的默认配置。比如
configs/rpi_3_defconfig。 - Kconfig 文件:分布在各个目录下,定义配置项。
- .config:最终生成的配置文件。
- autoconf.mk:从 .config 自动生成的 Makefile 片段。
2.3 编译流程:从源码到二进制
编译 U-Boot 其实就三步。但每一步背后都有讲究。我带你走一遍。
2.3.1 第一步:配置
# 以树莓派 3 为例
make rpi_3_defconfig
这条命令会做两件事:
- 从
configs/目录下找到rpi_3_defconfig。 - 生成
.config文件。
你可以用 make menuconfig 来图形化修改配置。我个人习惯是先跑 defconfig,再用 menuconfig 微调。
2.3.2 第二步:编译
# 编译
make -j4
编译过程大致是这样的:
- 预处理:处理
#include和宏定义。 - 编译:生成
.o文件。 - 链接:把所有
.o链接成u-boot文件。 - 格式转换:生成
u-boot.bin(纯二进制)和u-boot.img(带 U-Boot 头)。
-j8,结果链接报错。原因是某些 .o 文件还没生成就开始链接了。后来我改成 -j4 就没事了。多核编译虽好,但别贪心。
2.3.3 第三步:链接脚本
链接脚本 u-boot.lds 决定了 U-Boot 的内存布局。它通常在 arch/arm/cpu/ 或 board/ 目录下。
一个典型的链接脚本片段:
SECTIONS
{
. = 0x00200000; /* 起始地址 */
.text : {
*(.text*)
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
*(.bss*)
}
}
这里要注意:CONFIG_SYS_TEXT_BASE 必须和链接脚本的起始地址一致。否则,U-Boot 跑起来就会“飞掉”。
2.4 整体流程:一张图说清楚
下面这张图展示了从源码到二进制、再到烧录的完整流程。我建议你把它存下来,以后遇到编译问题,对着图查。
2.5 避坑指南:我踩过的几个坑
我曾经在
include/configs/ 里定义了一个宏,但 Kconfig 里也有同名选项。结果编译出来,宏生效了,Kconfig 的没生效。排查了半天。后来我统一用 Kconfig,头文件只放参数。
有一次我改了
CONFIG_SYS_TEXT_BASE,但忘了改链接脚本。结果 U-Boot 启动后,直接跑飞。用 JTAG 一看,PC 指针指向了错误地址。嗯,从那以后,我每次改地址都会检查链接脚本。
2.6 小结
U-Boot 的源码结构其实不复杂。你只要记住:arch/ 管 CPU,board/ 管板子,include/configs/ 管配置,drivers/ 管外设。配置系统是 Kconfig + 头文件双轨制。编译流程就是配置、编译、链接三步。