3. U-Boot源码结构

好,咱们今天聊聊U-Boot的源码结构。说实话,我第一次接触U-Boot源码时,也被它的目录数量吓了一跳。但别慌,摸清规律后你会发现,它其实挺有条理的。

3.1 顶层目录结构

你下载完U-Boot源码,解压后第一眼看到的,就是顶层目录。我习惯先看README文件,里面通常有编译和配置的说明。但真正干活时,我们关注的是这几个核心目录和文件:

  • board/:板级相关代码。每个支持的板子都有自己的文件夹。
  • arch/:架构相关代码。ARM、x86、RISC-V等都在这里。
  • common/:通用功能模块。命令、环境变量、启动流程等。
  • drivers/:驱动代码。网卡、串口、存储等。
  • include/:头文件。配置宏定义、数据结构等。
  • configs/:默认配置文件。每个板子的defconfig文件。
  • scripts/:编译脚本和工具。
  • Makefile:顶层Makefile,整个工程的入口。

嗯,这里要注意:board/和arch/是移植工作的主战场。我当年第一次移植U-Boot,就是在这两个目录里来回折腾。

核心思路:U-Boot的目录设计遵循「分层解耦」原则。架构相关代码在arch/,板级相关代码在board/,通用逻辑在common/。这样你换一块板子,只需要改board/和configs/,架构代码基本不动。

3.2 关键子目录详解

3.2.1 board/ 目录

这个目录下,每个板子一个子文件夹。比如board/ti/am335x/就是TI的AM335x板子。里面通常包含:

  • board.c:板级初始化函数,比如board_init_f()、board_init_r()。
  • mux.c:引脚复用配置。我踩过坑,引脚配错了,串口死活没输出。
  • Kconfig:板级配置选项。
  • Makefile:编译规则。

个人经验:我建议你在移植新板子时,先找一个参考板,把它的board/目录复制一份,然后改名字和关键配置。这样能省不少时间。

3.2.2 arch/ 目录

架构目录,按CPU架构分。以ARM为例:arch/arm/。里面又细分:

  • cpu/:CPU核心代码。比如armv7/、armv8/。
  • include/:架构相关的头文件。
  • lib/:架构相关的库函数,比如缓存操作、MMU设置。
  • mach-*/:SoC厂商相关的代码。比如mach-sunxi/是全志的。

为什么会这样分?说白了,ARMv7和ARMv8的启动流程差异很大,必须分开处理。我记得有一次调试一个ARMv8的板子,发现MMU初始化代码在arch/arm/cpu/armv8/里,找了半天才找到。

3.2.3 common/ 目录

这个目录是U-Boot的「大脑」。它包含了:

  • main.c:U-Boot的主循环,命令解析和执行。
  • command.c:命令注册和查找。
  • console.c:控制台输入输出。
  • env_*.c:环境变量管理。有env_nand.c、env_mmc.c等。
  • board_r.c:第二阶段板级初始化。

你想想看,U-Boot启动后,你敲的命令都是通过common/里的代码处理的。我调试时经常在common/main.c里加打印,看命令执行到哪一步了。

3.2.4 drivers/ 目录

驱动目录,按设备类型分:

子目录 说明 我踩过的坑
serial/ 串口驱动 波特率配错,输出乱码
net/ 网卡驱动 PHY地址不对,ping不通
mmc/ MMC/SD卡驱动 时序参数不对,读卡失败
mtd/ NAND/SPI Flash驱动 分区表配错,内核加载失败
usb/ USB驱动 —

避坑指南:我曾经在移植网卡驱动时,发现drivers/net/里有个phy/子目录,里面是PHY芯片驱动。如果你用的PHY不在支持列表里,得自己加。别傻乎乎地从头写,找个类似的改改就行。

3.3 配置文件体系

U-Boot的配置体系,说白了就是三件套:defconfig、Kconfig、config.h。我刚开始觉得这玩意儿绕,后来用熟了发现真香。

3.3.1 defconfig 文件

每个板子一个,放在configs/目录下。比如configs/am335x_evm_defconfig。它定义了:

  • 架构、CPU、板子类型
  • 需要开启的功能(如网络、USB)
  • 文本段基地址

举个例子:

CONFIG_ARM=y
CONFIG_ARCH_OMAP2PLUS=y
CONFIG_TARGET_AM335X_EVM=y
CONFIG_NET=y
CONFIG_DM_ETH=y
CONFIG_TEXT_BASE=0x80800000

嗯,这里要注意:CONFIG_TEXT_BASE是U-Boot的运行地址,配错了板子起不来。我吃过这个亏,当时把地址写成了0x80000000,结果和DDR地址冲突,死得很难看。

3.3.2 Kconfig 体系

Kconfig是配置菜单系统。你运行make menuconfig时看到的界面,就是它生成的。它分布在各个目录下:

  • arch/Kconfig:架构选项
  • board/xxx/Kconfig:板级选项
  • drivers/xxx/Kconfig:驱动选项
  • common/Kconfig:通用选项

我个人习惯是先跑一遍make defconfig,再用make menuconfig微调。这样不会漏掉依赖关系。

3.3.3 config.h 头文件

这是传统配置方式,现在逐渐被Kconfig取代。但很多老项目还在用。它放在include/configs/目录下,比如include/configs/am335x_evm.h。

里面定义的是宏:

#define CONFIG_SYS_TEXT_BASE    0x80800000
#define CONFIG_SYS_MALLOC_LEN   (16 << 20)
#define CONFIG_BOOTCOMMAND      "run distro_bootcmd"

关键点:新项目建议用Kconfig+defconfig方式。老项目维护时,才去改config.h。我见过一个项目,两种方式混用,结果宏定义冲突,编译报错查了半天。

3.4 知识体系结构图

下面这张图,是我梳理的U-Boot源码结构关系。你一看就明白:

U-Boot 源码结构体系 顶层目录结构 board/ 板级代码 arch/ 架构代码 common/ 通用模块 drivers/ 驱动 board.c mux.c cpu/ lib/ main.c command.c serial/ net/ 配置文件体系 defconfig 默认配置 Kconfig 菜单系统 config.h 头文件 三者配合:defconfig 选功能 → Kconfig 配参数 → config.h 定宏 → .config → autoconf.mk → 编译时宏

这张图把U-Boot的源码骨架画清楚了。顶层目录是入口,四个子目录是核心,配置文件体系把一切串起来。你移植时,就是在这几个框框里填自己的内容。

我的建议:刚开始别贪多,先把board/和configs/搞明白。这两个目录搞通了,U-Boot移植就完成了一半。剩下的,遇到什么问题再查什么。

好了,这一章就到这里。源码结构是基础,后面我们会深入每个模块的启动流程和调试技巧。记住,U-Boot的代码虽然多,但核心逻辑并不复杂——初始化硬件、加载内核、启动系统,就这三步。