3. U-Boot源码结构解析:顶层目录结构、board与arch目录、configs与include/configs目录、scripts与tools目录

U-Boot的源码,说实话,第一次打开的时候确实会让人有点懵。我当年刚接触时,面对那一堆文件夹,心里直犯嘀咕:「这玩意儿从哪看起?」但只要你摸清了它的骨架,就会发现——其实它设计得相当清晰。

这一章,我就带你把这棵「源码树」从上到下捋一遍。咱们不追求面面俱到,而是抓住那些你移植时真正会碰到的目录。

3.1 顶层目录结构:一眼看穿U-Boot的家底

先看根目录。你执行 ls 后,会看到几十个文件夹和文件。别慌,我帮你把它们分成几类:

类别 目录/文件 一句话说明
核心代码 arch/, board/, common/, drivers/ 架构、板级、通用逻辑、驱动——移植主要动这里
配置体系 configs/, include/configs/, Kconfig 板级默认配置与头文件宏定义
构建工具 scripts/, tools/, Makefile 编译脚本、打包工具、镜像生成器
文档与示例 doc/, examples/, api/ 参考手册、API示例、Standalone程序
测试与维护 test/, .gitignore, MAINTAINERS 单元测试、版本管理、维护者列表
我的习惯:拿到一个新板子的U-Boot源码,我第一件事就是看 board/ 和 configs/ 这两个目录。它们能最快告诉你这个BSP支持哪些硬件、默认配置长什么样。

3.2 board与arch目录:板级与架构的「双核」

这两个目录是移植工作的主战场。说白了,arch/ 管CPU的事,board/ 管板子的事。

3.2.1 arch/:架构相关代码

进去看看,里面按CPU架构分文件夹:

arch/
├── arm/          # ARM架构(最常用)
│   ├── cpu/      # 具体CPU核:armv7, armv8, cortex-m...
│   ├── include/  # 架构级头文件
│   ├── lib/      # 架构公共函数
│   └── mach-xxx/ # SoC厂商相关:mach-sunxi, mach-imx...
├── riscv/        # RISC-V架构
├── x86/          # x86架构
└── sandbox/      # 模拟调试用

我记得有一次移植一个新款i.MX芯片,死活起不来。折腾了两天,最后发现是 arch/arm/mach-imx/ 下的时钟初始化代码里,PLL配置参数写错了。嗯,从那以后我养成了一个习惯——先看 mach-xxx/ 目录下的 Kconfig 和 Makefile,确认SoC型号有没有被正确选中。

3.2.2 board/:板级专属代码

这个目录的结构通常是 board/<厂商>/<板子名>/。比如:

board/
├── freescale/
│   ├── mx6ul_14x14_evk/
│   └── ls1046ardb/
├── rockchip/
│   ├── evb_rk3399/
│   └── tinker_board/
└── mycompany/          # 你自己的板子
    └── myboard_v1/

每个板子目录下一般有这些文件:

  • Kconfig:板级配置选项
  • MAINTAINERS:谁负责维护这块板
  • Makefile:编译哪些文件
  • board.c:板级初始化函数(board_init_f, board_init_r)
  • dram.c:DDR初始化(这块最容易出问题)
  • imximage.cfg 或 u-boot.lds:链接脚本或镜像头配置
避坑指南:我曾经在移植时,直接复制了另一块板子的 board.c,结果GPIO初始化顺序不对,导致eMMC死活识别不了。后来花了半天逐行对比原理图才找到问题。所以——board目录下的代码,一定要对着你的原理图逐行确认,别偷懒。

3.3 configs与include/configs目录:配置的「双保险」

U-Boot的配置体系,说白了有两套机制在并行工作。理解它们,你才能知道改配置时该动哪里。

3.3.1 configs/:默认配置(defconfig)

这个目录下全是 xxx_defconfig 文件。每个文件对应一块板子的默认配置。比如:

configs/
├── mx6ul_14x14_evk_defconfig
├── rpi_3_defconfig
└── myboard_v1_defconfig

文件内容长这样:

CONFIG_ARM=y
CONFIG_TARGET_MYBOARD_V1=y
CONFIG_DM_MMC=y
CONFIG_BAUDRATE=115200
# CONFIG_SPL is not set

这里每一行都是一个Kconfig选项。你执行 make myboard_v1_defconfig 时,U-Boot就会根据这个文件生成完整的 .config。

3.3.2 include/configs/:传统头文件配置

这是U-Boot的「老派」配置方式。虽然现在Kconfig逐渐成为主流,但很多板子仍然保留着头文件配置。比如:

include/configs/
├── mx6ul_14x14_evk.h
└── myboard_v1.h

头文件里定义的是C语言的宏:

#define CONFIG_SYS_TEXT_BASE      0x87800000
#define CONFIG_SYS_MALLOC_LEN     (16 << 20)
#define CONFIG_BOOTCOMMAND        "run distro_bootcmd"
#define CONFIG_BAUDRATE           115200
关键区别:
  • configs/ 里的defconfig:控制「编译哪些模块」——用Kconfig的 y/n/m 语法。
  • include/configs/ 里的头文件:控制「参数值是多少」——用C语言的 #define 语法。

你想想看,如果你要改DDR的基地址,该改哪里?对,头文件里的 CONFIG_SYS_TEXT_BASE。如果你要关掉某个驱动,该改哪里?defconfig里的 # CONFIG_XXX is not set。

3.4 scripts与tools目录:幕后英雄

这两个目录平时你可能不怎么直接碰,但编译和打包时,它们一直在默默干活。

3.4.1 scripts/:构建脚本与规则

这里存放的是Kconfig解析器、构建辅助脚本、DTB编译规则等。几个重要的:

  • scripts/kconfig/:Kconfig前端(menuconfig 的图形界面就靠它)
  • scripts/Makefile.*:各种编译规则片段
  • scripts/dtc/:设备树编译器(dtc)的包装脚本
  • scripts/basic/:fixdep等依赖生成工具

说实话,我很少直接改这里的东西。但有一次我需要给U-Boot加一个自定义的镜像签名步骤,就是在 scripts/Makefile.spl 里加了一段post-processing规则。

3.4.2 tools/:主机端工具集

这个目录下的工具,是在你的PC上运行的,用来处理镜像文件。常用的有:

工具 用途 我什么时候用过它
mkimage 制作U-Boot可识别的镜像头 每次烧写内核时都要用
fdtgrep 过滤/裁剪设备树 SPL中为了减小体积,只保留必要节点
imx_mkimage 生成i.MX系列专用的启动镜像 做i.MX板子时,这步绕不开
sunxi-tools 全志系列的工具链 调试全志芯片时配合使用
envtools 在PC上读写U-Boot环境变量 批量生产时,用脚本预设环境变量
一个小技巧:如果你在移植时发现 mkimage 报错说「bad magic number」,八成是你传给它的镜像类型参数不对。用 mkimage -l <镜像文件> 可以查看现有镜像头信息,对照着改就行了。

3.5 本章知识结构图

下面这张图,帮你把U-Boot源码的「骨架」串起来。你可以把它当作一张地图,以后移植时,按图索骥就行。

U-Boot 源码结构总览 U-Boot 源码结构总览 U-Boot 根目录 arch/ board/ configs/ tools/ arm/ riscv/ x86/ sandbox/ cpu/ mach-xxx/ lib/ include/ freescale/ rockchip/ mycompany/ board.c dram.c Kconfig Makefile xxx_defconfig 文件 include/configs/xxx.h mkimage fdtgrep imx_mkimage scripts/ kconfig/ dtc/ Makefile.* 移植时重点关注:board/(板级逻辑) + arch/(架构代码) + configs/(配置体系) tools/ 和 scripts/ 是辅助工具,平时不用动,但关键时刻能救命

嗯,这张图基本把U-Boot源码的「骨架」画清楚了。你以后做移植时,可以把它当作一张地图——从 configs/ 找到入口,到 board/ 和 arch/ 里改代码,最后用 tools/ 里的工具打包验证。每一步该去哪,心里就有数了。