第2章:U-Boot项目简介
U-Boot,全称 Universal Boot Loader。说白了,它就是嵌入式系统的「第一段程序」。CPU上电后,最先跑的就是它。我刚开始接触嵌入式时,总觉得Bootloader就是个跳板,没啥技术含量。后来被坑过几次才明白——U-Boot玩不转,后面Linux内核调试根本无从下手。
这一章,我们先把U-Boot的底细摸清楚。历史、版本、源码结构、启动流程、配置系统,一个一个来。
2.1 U-Boot的历史与版本
U-Boot的前身是8xxROM,由德国工程师Wolfgang Denx在1999年左右发起。后来合并了PPCBoot、ARMBoot等项目,2002年正式更名为U-Boot。这个名字很直白——Universal Boot Loader,通用引导加载程序。
为什么说它「通用」?因为它支持几乎所有的嵌入式CPU架构:ARM、RISC-V、x86、MIPS、PowerPC、RISC-V……我这些年经手的项目,从Cortex-M3到Cortex-A72,从龙芯到飞腾,底层全是U-Boot。
版本方面,U-Boot目前采用基于时间的命名方式:
| 版本格式 | 示例 | 说明 |
|---|---|---|
| v2023.01 | 2023年1月发布 | 年度大版本,功能稳定 |
| v2023.04-rc1 | 2023年4月候选版 | 开发中版本,可能有新功能 |
| v2023.07 | 2023年7月发布 | 半年一次正式版 |
2.2 U-Boot的源码结构
下载一份U-Boot源码,解压后你会看到这样的目录布局:
u-boot/
├── arch/ # 架构相关代码(核心)
│ ├── arm/ # ARM架构
│ ├── riscv/ # RISC-V架构
│ ├── x86/ # x86架构
│ └── ...
├── board/ # 板级支持包
│ ├── myboard/ # 你的板子
│ └── ...
├── common/ # 通用功能
├── configs/ # 默认配置文件
├── drivers/ # 驱动(串口、网卡、存储等)
├── dts/ # 设备树源文件
├── include/ # 头文件
├── lib/ # 库函数
├── net/ # 网络协议栈
├── cmd/ # 命令行命令
├── tools/ # 编译工具
├── Makefile # 顶层Makefile
└── Kconfig # 顶层Kconfig
这个结构,我建议你记住三个关键目录:
- arch/:CPU架构相关,移植时基本不动
- board/:板级代码,移植的主战场
- configs/:配置文件,决定编译哪些功能
嗯,这里要注意。很多新手一上来就改arch/,其实完全没必要。你想想看,CPU架构代码是U-Boot团队维护的,几百号人盯着,一般不会出问题。真正需要你动手的,是board/目录下的板级支持。
2.3 U-Boot的启动流程概述
U-Boot的启动流程,我习惯把它分成三个阶段:
- SPL(第一阶段):初始化CPU、时钟、DDR,加载完整U-Boot
- U-Boot proper(第二阶段):初始化外设,解析环境变量,进入命令行
- 启动内核(第三阶段):加载内核镜像和设备树,跳转到内核入口
下面这张图,是我自己画的一个简化流程:
2.4 U-Boot的配置系统(Kconfig/Makefile)
U-Boot的配置系统,沿用了Linux内核的那一套——Kconfig + Makefile。你想想看,U-Boot支持几百种板子,几千个配置选项,没有一套自动化的配置管理,根本玩不转。
配置流程大致是这样的:
- make <board>_defconfig:加载板级默认配置
- make menuconfig:图形化调整配置(可选)
- make:根据配置编译
举个例子,假设你有一块叫「myboard」的板子:
# 第一步:加载默认配置
make myboard_defconfig
# 第二步:如果需要调整,打开图形界面
make menuconfig
# 第三步:编译
make -j4
Kconfig文件定义了配置选项的层次结构。比如你想开启网络功能,需要在Kconfig里找到:
config CMD_NET
bool "Enable networking commands"
default y
help
This enables basic networking commands like ping, tftp, etc.
Makefile则根据这些配置决定编译哪些文件:
obj-$(CONFIG_CMD_NET) += cmd_net.o
obj-$(CONFIG_CMD_PING) += cmd_ping.o
嗯,这里有个坑。我刚开始移植时,以为改了Kconfig就万事大吉了。结果编译报错,说找不到某个函数。排查了半天才发现——Kconfig只是定义了开关,Makefile里的编译规则也得配上。说白了,Kconfig是「要不要」,Makefile是「怎么要」。
最后,说一个实用技巧。U-Boot的配置系统支持「碎片化配置」——你可以在board目录下放一个名为myboard.config的文件,里面写一些额外的配置项。编译时,这些配置会自动合并到最终配置中。这样,你就不用每次都去改顶层的Kconfig了。
# board/myboard/myboard.config
CONFIG_BOOTDELAY=3
CONFIG_BOOTCOMMAND="tftp 0x82000000 kernel.img; bootm"
CONFIG_BAUDRATE=115200
这个技巧,我在多个项目里都用过。特别是当你的板子有多个变种时,每个变种放一个.config文件,管理起来非常清爽。