2、U-Boot源码结构:顶层目录结构、关键子目录功能、Makefile与Kconfig体系

好,咱们直接切入正题。U-Boot的源码,说白了就是一个大型的嵌入式启动引导工程。我第一次接触它的时候,也被那密密麻麻的文件夹吓了一跳。但别慌,它的结构其实很有章法。你把它拆开来看,无非就是“板级配置”、“CPU相关”、“驱动”、“通用库”这几大块。

我个人习惯,拿到一个新板子,第一件事不是急着改代码,而是先把这个目录结构摸清楚。这就像你搬家,得先知道厨房在哪儿、厕所在哪儿,对吧?

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

你解压U-Boot源码后,在终端里敲个 ls,会看到一堆文件夹。我挑几个最关键的跟你说说。

目录/文件 作用 我的经验
board/ 板级相关代码,每个板子一个子目录 移植时80%的改动都在这里
cpu/ CPU架构相关代码(ARM、RISC-V、x86等) 一般不动,除非换CPU核
drivers/ 各种外设驱动(网卡、串口、MMC等) 调驱动时天天泡在这里
include/ 头文件,包括板级配置头文件 配置宏定义全在这
common/ 通用函数,比如命令行解析 一般不动,但可以学习
lib/ 库函数,比如字符串操作、加密 很少改
net/ 网络协议栈 调网络启动时有用
fs/ 文件系统支持(ext4、fat等) 需要读文件系统时才关注
tools/ 主机端工具,比如mkimage 打包镜像时用
Makefile 顶层构建文件 理解构建流程的关键
Kconfig 配置系统入口 图形化配置的基础
小提示: 别试图一次性记住所有目录。你只需要知道:board/ 是“你家”,drivers/ 是“工具箱”,include/ 是“说明书”。其他目录,用到的时候再查。

2.2 关键子目录功能:深入核心

board/ —— 你的主战场

这个目录下,每个板子都有一个专属文件夹。比如 board/ti/am335x/ 就是TI的AM335x开发板。里面通常有:

  • board.c:板级初始化函数,比如DDR初始化、时钟配置。
  • Kconfig:这个板子的配置选项。
  • Makefile:编译这个板子需要的文件列表。

我在项目中遇到过最头疼的事,就是DDR参数配错了。板子死活起不来,串口也没输出。后来我学乖了,每次改 board.c 里的DDR参数,都会先对照芯片手册,再拿示波器量一下波形。嗯,这步不能省。

cpu/ —— 芯片的“魂”

这里存放的是CPU架构相关的代码。比如 cpu/armv7/ 就是ARM Cortex-A7/A8/A9通用的代码。里面包含:

  • 异常向量表
  • MMU(内存管理单元)设置
  • Cache(缓存)操作
  • 启动汇编代码(比如 start.S)

说实话,这个目录我平时很少动。除非你要换CPU核,或者要优化启动速度。否则,保持原样就好。

drivers/ —— 驱动大本营

这个目录结构跟Linux内核很像。里面有 serial/(串口)、net/(网卡)、mmc/(SD卡)、spi/、i2c/ 等等。每个驱动都是一个独立的模块。

你想想看,U-Boot的驱动模型(Driver Model)其实挺巧妙的。它把驱动分成了“设备”和“驱动”两部分,通过设备树(Device Tree)来匹配。我刚开始移植网卡驱动时,就是照着 drivers/net/ 里一个类似的驱动改的。改完发现不工作,折腾了两天,最后发现是设备树里中断号写错了。哎,这种低级错误,犯过一次就记住了。

include/ —— 配置的“总开关”

这个目录下的 configs/ 子目录,存放着所有板子的默认配置文件。比如 include/configs/am335x_evm.h。这个文件里定义了一大堆宏,比如:

#define CONFIG_BOOTDELAY 3
#define CONFIG_BOOTCOMMAND "run mmcboot"
#define CONFIG_SYS_TEXT_BASE 0x80800000

这些宏控制了U-Boot的行为。比如 CONFIG_BOOTDELAY 就是启动倒计时秒数。我个人习惯,在调试阶段会把倒计时设成0,省得每次都要敲回车打断。

注意: 新版U-Boot(2016年以后)已经逐渐用 Kconfig 替代了传统的 #define 配置方式。但很多老项目还在用头文件配置。两种方式你都得会。

2.3 Makefile与Kconfig体系:构建与配置的艺术

这部分是U-Boot移植的“内功心法”。不懂Makefile和Kconfig,你连编译都过不了。

Makefile —— 构建的“指挥家”

U-Boot的Makefile体系非常庞大,但核心逻辑很简单:

  1. 顶层Makefile读取 .config 文件(由Kconfig生成)。
  2. 根据 .config 中的配置,决定编译哪些文件。
  3. 递归进入各个子目录,执行子Makefile。
  4. 链接生成 u-boot 和 u-boot.bin。

我曾经为了搞清楚一个编译错误,追了三个小时的Makefile。最后发现是某个驱动没选上,导致符号未定义。所以我的建议是:遇到编译问题,先检查 .config 文件,看看你想要的驱动有没有被 CONFIG_ 宏打开。

Kconfig —— 配置的“菜单”

Kconfig是Linux内核引入的配置系统。U-Boot从2014年开始逐步迁移。你运行 make menuconfig 时,看到的那个图形界面,就是Kconfig生成的。

每个子目录下都有一个 Kconfig 文件,定义了该目录下的配置选项。比如 drivers/net/Kconfig 里会有:

config NET_TI_CPSW
    bool "TI CPSW Ethernet driver"
    help
      This driver supports the TI CPSW Ethernet controller.

你选中这个选项后,.config 里就会多一行 CONFIG_NET_TI_CPSW=y,然后Makefile就会编译对应的驱动文件。

核心要点: Kconfig负责“选什么”,Makefile负责“怎么编”。两者通过 .config 文件沟通。移植新板子时,你需要在 board/你的板子/Kconfig 里添加你自己的配置选项,然后在顶层Kconfig里引用它。

2.4 知识体系结构图

下面这张图,是我自己总结的U-Boot源码结构关系。你看一眼,就能明白各个模块是怎么协作的。

U-Boot 源码结构关系图 顶层 Makefile Kconfig 配置系统 .config 配置文件 核心模块 (common/lib/) 板级模块 (board/) CPU架构 (cpu/) 驱动模块 (drivers/) 头文件 (include/) u-boot.bin 图例 构建入口 配置系统 配置文件 功能模块

这张图你看懂了吗?顶层Makefile是总指挥,它从 .config 里读取配置,然后决定编译哪些模块。Kconfig是配置的入口,你通过 menuconfig 选好功能后,它会生成 .config。各个模块(board、cpu、drivers等)根据配置编译,最后链接成 u-boot.bin。

嗯,到这里,U-Boot的源码结构你应该有个整体印象了。说白了,它就是一个高度模块化的嵌入式系统。你移植新板子,就是在 board/ 里加一个新文件夹,然后在Kconfig里加上你的板子选项,再配置好DDR、时钟、串口这些基础外设。剩下的,就是调试和填坑了。

可可精品资料库