2、U-Boot源码结构:顶层目录结构、关键子目录功能、配置文件体系

好,咱们直接进入正题。上一章聊了U-Boot能干什么,这一章咱们把它的“骨架”拆开看看。你拿到一份U-Boot源码,第一眼肯定懵——这么多文件夹,从哪下手?

我刚开始接触U-Boot的时候,也对着目录发过呆。后来发现,它的结构其实很有规律。说白了,就是一套“硬件抽象 + 板级定制 + 通用逻辑”的框架。搞懂了这套框架,你往里面加板子、改驱动,心里就有底了。

2.1 顶层目录结构:一眼看穿全局

先看顶层。你解压U-Boot之后,会看到类似这样的布局:

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

嗯,这里要注意:arch/ 和 board/ 是移植工作的主战场。其他目录虽然也重要,但大部分时候你不需要动它们。

核心原则:U-Boot的目录划分遵循“分层隔离”思想。CPU架构相关代码放在 arch/,板级硬件差异放在 board/,通用逻辑放在 common/ 和 lib/。这样你换一块板子,只需要改 board/ 和 configs/,不用动核心代码。

2.2 关键子目录功能详解

咱们挑几个最常用的目录,一个一个说。我按实际开发中接触的频率来排。

2.2.1 arch/ —— CPU架构的“家”

这个目录下面按CPU架构分文件夹:

  • arch/arm/ —— ARM架构,嵌入式绝对主力
  • arch/x86/ —— x86架构,少数场景用
  • arch/riscv/ —— RISC-V,新贵
  • arch/mips/ —— MIPS,老牌选手

每个架构下面又细分:

arch/arm/
├── cpu/          # CPU初始化、异常处理
├── include/      # 架构相关头文件
├── lib/          # 架构通用函数(如cache操作)
├── mach-xxx/     # 具体SoC厂商的定制代码
└── dts/          # 设备树文件

我记得有一次移植一个新款ARM芯片,发现启动就卡死。查了两天,最后发现是 arch/arm/cpu/ 里某个cache初始化宏没开。你看,这种底层细节,全藏在这个目录里。

2.2.2 board/ —— 你的板子,你的代码

这是你最常打交道的目录。每个板子一个子文件夹:

board/
├── ti/
│   ├── am335x/       # BeagleBone Black
│   └── dra7xx/       # TI DRA7系列
├── freescale/
│   ├── imx6q_sabre/  # i.MX6Q开发板
│   └── ls1046a/      # Layerscape系列
├── rockchip/
│   ├── evb_rk3399/   # RK3399开发板
│   └── nanopi4/      # NanoPi4
└── mycompany/        # 你自己的板子
    └── myboard/

每个板级目录里通常有:

  • board.c —— 板级初始化函数(board_init、dram_init等)
  • Kconfig —— 板级配置选项
  • Makefile —— 编译规则
  • imximage.cfg —— 镜像头配置(i.MX系列特有)

我的习惯:拿到一块新板子,先找board/下有没有参考设计。比如你用TI AM335x,直接复制board/ti/am335x/,改个名字,再改改内存参数和引脚配置。能省一半时间。

2.2.3 drivers/ —— 外设驱动的“大仓库”

这个目录按设备类型分:

子目录 功能 常见文件
drivers/net/ 网络接口驱动 phy.c, designware.c, fec_mxc.c
drivers/mmc/ SD/eMMC驱动 sdhci.c, dw_mmc.c
drivers/spi/ SPI总线驱动 spi_flash.c, fsl_qspi.c
drivers/i2c/ I2C总线驱动 i2c_core.c, mvtwsi.c
drivers/usb/ USB主机/设备驱动 ehci-hcd.c, dwc2.c
drivers/video/ 显示驱动 simple_panel.c, rockchip_display.c

你想想看,U-Boot的网络启动,核心就在 drivers/net/ 里。PHY芯片初始化、MAC控制器配置、MDIO总线通信,全在这。我调试网络启动时,经常要加打印看phy_read()的返回值,确认link状态。

2.2.4 common/ —— 通用功能“瑞士军刀”

这个目录放的是U-Boot的通用模块:

  • main.c —— U-Boot主循环(命令行解析入口)
  • command.c —— 命令注册框架
  • console.c —— 控制台输入输出
  • env_common.c —— 环境变量管理
  • bootm.c —— 启动内核的核心函数
  • autoboot.c —— 自动启动逻辑

说白了,你在U-Boot命令行敲的每个命令,背后都是common/里的代码在支撑。比如你敲 bootm 0x82000000,最终会调用到 common/bootm.c 里的 do_bootm() 函数。

2.2.5 其他重要目录

  • net/ —— 网络协议栈。TFTP、NFS、DHCP的客户端实现都在这里。网络启动时,这个目录和drivers/net/配合工作。
  • fs/ —— 文件系统支持。ext4、fat、ubifs等。你从TFTP下载内核后,如果要从eMMC的ext4分区读取设备树,就靠它。
  • dts/ —— 设备树源文件。现代U-Boot几乎必用设备树,板级硬件描述全在这里。
  • configs/ —— 板级默认配置。这个咱们下面重点说。

2.3 配置文件体系:U-Boot的“基因”

U-Boot的配置体系,我当年学的时候觉得挺绕。其实就三层:

  1. 板级默认配置(configs/xxx_defconfig)
  2. Kconfig菜单系统(各目录下的Kconfig文件)
  3. 头文件配置(include/configs/xxx.h)—— 老版本方式,新版本逐渐淘汰

咱们重点说前两个,因为这是现在的主流做法。

2.3.1 defconfig —— 板子的“身份证”

每个板子对应一个 defconfig 文件,放在 configs/ 目录下。比如:

# configs/myboard_defconfig
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MYBOARD=y
CONFIG_DM=y
CONFIG_DM_ETH=y
CONFIG_ETH_DESIGNWARE=y
CONFIG_PHYLIB=y
CONFIG_CMD_DHCP=y
CONFIG_CMD_TFTP=y
CONFIG_BOOTCOMMAND="tftp 0x82000000 zImage; tftp 0x83000000 myboard.dtb; bootz 0x82000000 - 0x83000000"
CONFIG_BOOTARGS="console=ttymxc0,115200 root=/dev/nfs rw"

这个文件决定了:

  • 用什么CPU架构
  • 用什么板级代码
  • 开启哪些驱动
  • 开启哪些命令
  • 默认启动命令是什么

我曾经踩过的坑:有次我改了defconfig,加了CONFIG_CMD_PING=y,但编译后ping命令还是不能用。查了半天,发现Kconfig里这个选项依赖CONFIG_NET,而我没开。所以改defconfig时,一定要看依赖链。

2.3.2 Kconfig —— 配置的“菜单树”

Kconfig文件定义了所有可配置选项的层次结构。比如你想知道“为什么我的板子没有TFTP命令”,就去查:

# net/Kconfig
config CMD_TFTP
    bool "tftp command"
    depends on NET
    help
      TFTP download command for network boot

配置流程是这样的:

make myboard_defconfig   # 加载默认配置
make menuconfig          # 进入图形化菜单,微调配置
make                     # 编译

menuconfig界面里,你可以按空格开关功能,按Y/N选择。我个人习惯是先加载defconfig,再用menuconfig微调,最后 make savedefconfig 生成新的defconfig。

2.4 一张图看懂U-Boot源码结构

下面这张SVG图,把U-Boot源码的核心层次和配置体系串起来了。你盯着看一分钟,比读十页文档管用。

U-Boot源码结构层次图 U-Boot 源码结构层次图 硬件层 (Hardware) CPU / SoC / DDR / Flash / 网卡 / 串口 / GPIO 对应目录:arch/ (CPU架构) 、board/ (板级硬件) 驱动层 (Drivers) 网卡驱动 / MMC驱动 / SPI驱动 / I2C驱动 / USB驱动 对应目录:drivers/net/ 、drivers/mmc/ 、drivers/spi/ 等 核心层 (Core) 命令解析 / 环境变量 / 启动逻辑 / 网络协议 / 文件系统 对应目录:common/ 、net/ 、fs/ 、lib/ 配置层 (Configuration) defconfig / Kconfig / 设备树 (dts) 对应目录:configs/ 、各目录下的Kconfig 、dts/ 配置流程 make xxx_defconfig → make menuconfig → make 移植核心 改 board/ + configs/ + dts/ ,不动 arch/ 和 common/

2.5 小结:你该记住什么

这一章信息量不小,我帮你划重点:

  • arch/ 和 board/ 是移植的核心,其他目录尽量别动
  • drivers/ 是外设驱动的集合,网络启动重点关注 drivers/net/
  • common/ 是U-Boot的大脑,命令和启动逻辑都在这里
  • configs/ 里的defconfig是板子的“基因”,决定了U-Boot的功能集
  • 配置体系是 defconfig → Kconfig → 编译 的三步走