2. U-Boot源码结构:顶层目录结构解析、关键子目录功能、Makefile与Kconfig构建系统

好,咱们今天来聊聊U-Boot的源码结构。说实话,我第一次接触U-Boot源码时,也被它的目录数量吓了一跳。但别慌,这东西就像你家的工具箱——看着乱,但每样工具都有它的位置。

U-Boot的源码结构,说白了就是一套模块化、可移植的嵌入式引导代码。它支持几十种CPU架构、上千块开发板,如果没有清晰的目录划分,那维护起来简直是噩梦。我个人习惯把U-Boot源码比作一个「乐高积木盒」——每个目录都是一类积木,你挑出需要的,拼出适合你板子的镜像。

2.1 顶层目录结构解析

我们先从顶层看起。你解压U-Boot源码后,会看到类似这样的目录布局:

u-boot/
├── arch/          # 架构相关代码
├── board/         # 板级相关代码
├── common/        # 通用功能模块
├── configs/       # 板级默认配置文件
├── doc/           # 文档
├── drivers/       # 设备驱动
├── dts/           # 设备树源文件
├── fs/            # 文件系统支持
├── include/       # 头文件
├── lib/           # 库函数
├── net/           # 网络协议栈
├── scripts/       # 构建脚本
├── tools/         # 主机端工具
├── Makefile       # 顶层Makefile
└── Kconfig        # 顶层Kconfig配置

嗯,这里要注意:arch/ 和 board/ 是U-Boot移植工作的核心区域。我刚开始做移植时,80%的时间都花在这两个目录里。

核心原则:U-Boot通过「架构 → SoC → 板级」三层抽象,实现了代码复用。你改得越少,说明你的板子越「标准」。

2.2 关键子目录功能详解

咱们挑几个最重要的目录,一个一个说清楚。

2.2.1 board/ —— 板级适配层

这个目录存放的是具体开发板的初始化代码。每个板子一个子目录,比如:

board/
├── ti/
│   ├── am335x/       # BeagleBone Black等
│   └── omap5/        # OMAP5系列
├── freescale/
│   ├── mx6ul_14x14_evk/
│   └── ls1046ardb/
└── ...

每个板级目录里通常包含:

  • board.c —— 板级初始化函数(比如DDR初始化、时钟配置)
  • Kconfig —— 板级配置选项
  • MAINTAINERS —— 维护者信息

我曾经接手过一个项目,板子用的DDR颗粒和参考设计不一样。我花了整整两天在 board.c 里调时序参数。说白了,板级代码就是你和硬件「握手」的地方。

2.2.2 cpu/ —— 但等等,现在叫 arch/

老版本的U-Boot确实有 cpu/ 目录,但新版本已经改成了 arch/。这里存放的是CPU架构相关的代码:

arch/
├── arm/           # ARM架构
│   ├── cpu/       # 具体CPU型号(armv7, armv8等)
│   ├── include/   # 架构头文件
│   └── lib/       # 架构通用库
├── x86/           # x86架构
├── riscv/         # RISC-V架构
└── ...

你想想看,ARM和RISC-V的启动流程能一样吗?当然不能。所以这部分代码是架构隔离的。我建议你只关注你用的架构,其他的先放一边。

2.2.3 drivers/ —— 设备驱动的大本营

这个目录很大,也很重要。它按设备类型分了子目录:

子目录 功能 常见文件
serial/ 串口驱动 ns16550.c, pl011.c
spi/ SPI总线驱动 spi_flash.c, fsl_spi.c
mmc/ MMC/SD卡驱动 mmc.c, sdhci.c
net/ 网络接口驱动 phy.c, designware.c
usb/ USB主机/设备驱动 ehci-hcd.c, musb.c

U-Boot的驱动模型(Driver Model)从2014年开始引入,现在已经是主流。我个人觉得,这套模型比Linux的驱动模型简单多了——你只需要实现 probe 和 remove 两个回调,剩下的框架帮你搞定。

小技巧:如果你要添加一个新驱动,先看看 drivers/ 里有没有类似的。复制粘贴再改,比从头写快得多。我经常这么干。

2.2.4 common/ —— 通用功能模块

这个目录放的是U-Boot的核心逻辑,比如:

  • main.c —— U-Boot的主循环(命令解析、执行)
  • command.c —— 命令注册与查找
  • console.c —— 控制台输入输出
  • env_common.c —— 环境变量管理

说白了,common/ 就是U-Boot的「大脑」。你敲的每条命令,最终都会经过这里处理。

2.3 Makefile与Kconfig构建系统

这部分是U-Boot编译的「指挥中心」。没有它,你连个镜像都生成不了。

2.3.1 顶层Makefile

U-Boot的构建系统基于GNU Make,顶层Makefile是整个编译过程的入口。它的核心流程是:

  1. 配置阶段:读取 .config 文件(由Kconfig生成)
  2. 依赖生成:自动生成头文件依赖(.d 文件)
  3. 编译阶段:递归进入各子目录编译
  4. 链接阶段:将所有目标文件链接成 u-boot 镜像

常用的Makefile目标:

make defconfig      # 使用默认配置
make menuconfig     # 图形化配置界面
make                # 编译U-Boot
make u-boot.bin     # 生成二进制镜像
make u-boot.img     # 生成带头部的镜像(用于SPL)

我记得有一次,我改了某个驱动文件,但编译时死活不生效。后来发现是Makefile里的依赖没更新。嗯,这里要注意:修改Makefile后,最好 make clean 再重新编译。

2.3.2 Kconfig配置系统

Kconfig是U-Boot从Linux内核借鉴来的配置系统。它用树形结构管理所有配置选项:

# 示例:drivers/spi/Kconfig
config SPI_FLASH
    bool "SPI Flash support"
    help
      Enable support for SPI Flash devices.
      选中后,U-Boot会编译SPI Flash驱动。

Kconfig的配置项会生成 .config 文件,然后被 include/config.h 引用。整个流程是:

Kconfig → .config → autoconf.mk → C代码中的宏

你想想看,如果没有Kconfig,你要手动定义几百个宏,那得多痛苦?Kconfig说白了就是帮你可视化地管理这些宏。

避坑指南:我曾经在 menuconfig 里改了一个配置,但忘了保存。结果编译出来的镜像还是老样子。记住:改完配置一定要选「Save」,或者直接 make savedefconfig。

2.4 知识体系结构图

下面这张图,是我自己整理的U-Boot源码结构关系。你看完应该能明白各个目录是怎么配合的:

U-Boot源码结构关系图 顶层Makefile Kconfig配置系统 .config / autoconf.mk arch/ (CPU架构) board/ (板级) drivers/ (驱动) common/ (通用) 编译输出:u-boot.bin / u-boot.img Makefile负责编译流程,Kconfig负责配置管理 arch/ + board/ + drivers/ + common/ 共同生成最终镜像

2.5 小结

好了,这一章的内容就这些。总结一下:

  • 顶层目录:U-Boot按功能模块划分,核心是 arch/、board/、drivers/、common/
  • 构建系统:Makefile负责编译,Kconfig负责配置,两者配合生成最终镜像
  • 移植要点:改得越少越好,尽量复用现有代码

下一章,我们会深入 arch/ 目录,看看U-Boot是如何在CPU上「活过来」的。到时候见。

一句话记住:U-Boot源码结构 = 架构隔离 + 板级适配 + 驱动复用 + 配置灵活。把这四个点吃透,你就掌握了U-Boot的「骨架」。

可可精品资料库