2. U-Boot源码结构解析:目录、关键文件与编译系统

拿到一份U-Boot源码,第一反应是什么?我当年第一次解压出来,看到满屏目录,说实话有点懵。但别怕,它的结构其实很有规律。今天我们就把它拆开看看。

2.1 顶层目录结构

U-Boot的顶层目录,说白了就几大类:板级相关、CPU架构相关、通用驱动、工具脚本。我习惯先看board/和arch/,因为移植工作主要跟这两个目录打交道。

目录/文件作用我关注的点
arch/CPU架构相关代码(ARM、x86、RISC-V等)ARM下重点关注arch/arm/
board/板级初始化、DDR配置、GPIO等移植时主要修改的地方
configs/默认配置文件(xxx_defconfig)从这里开始配置你的板子
drivers/外设驱动(串口、网卡、MMC、USB等)调试时经常要翻
include/头文件、宏定义、板级配置头文件configs/目录下的头文件是关键
dts/设备树源文件(.dts、.dtsi)新板子必须自己写
tools/镜像打包、签名等工具mkimage常用
scripts/Kbuild、Makefile辅助脚本编译系统核心
fs/文件系统支持(ext4、fat等)启动时加载内核需要
net/网络协议栈tftp下载用
cmd/命令行命令实现可以自己加命令
common/通用函数和初始化流程启动流程入口在这里
我的习惯:拿到一个新板子,先复制一份相近的board/目录,再改configs/下的defconfig。这样最快能跑起来。

2.2 关键文件说明

有几个文件,你移植时几乎一定会动到。我列出来,你心里有个数。

2.2.1 板级配置头文件

位置:include/configs/。比如include/configs/rpi.h。这里面定义了DDR大小、时钟频率、环境变量分区等。嗯,这里要注意:不要乱改宏定义,尤其是CONFIG_SYS_TEXT_BASE,改错了U-Boot起不来。

2.2.2 设备树文件

位置:arch/arm/dts/。现在U-Boot也大量使用设备树。你写的.dts文件,编译后会生成.dtb,U-Boot用它来知道板子上有什么外设。

2.2.3 链接脚本

位置:arch/arm/cpu/u-boot.lds。这个文件决定了U-Boot各段在内存中的布局。我曾经因为改了链接脚本,导致U-Boot在DDR初始化前就访问了全局变量,直接死机。避坑指南:链接脚本不要轻易动,除非你很清楚自己在做什么。

2.2.4 默认配置文件

位置:configs/。比如configs/stm32mp15_defconfig。这是编译的入口,里面通过CONFIG_*开关控制哪些功能被编译进去。

注意:U-Boot的配置系统已经从旧版include/configs/头文件方式,逐步迁移到Kconfig方式。新板子建议直接用Kconfig,别走回头路。

2.3 编译系统简介:Kbuild/Makefile

U-Boot的编译系统,说白了就是Linux内核那套Kbuild。你如果编译过内核,上手会很快。如果没接触过,也别慌,我带你捋一遍核心流程。

2.3.1 编译三步走

  1. 配置:make xxx_defconfig —— 生成.config文件
  2. 编译:make —— 根据.config编译源码
  3. 打包:make自动生成u-boot.bin、u-boot.img等镜像

举个例子,我移植STM32MP157板子时,第一步就是:

make stm32mp15_defconfig
make -j4

2.3.2 Kconfig体系

Kconfig文件分布在各个目录,比如drivers/serial/Kconfig。它定义了哪些驱动可以选。你执行make menuconfig时看到的界面,就是Kconfig生成的。

我个人习惯,在board/st/xxx/Kconfig里添加板级特有的配置选项。这样代码更干净。

2.3.3 Makefile的层次

  • 顶层Makefile:负责整体编译流程,调用各子目录Makefile
  • 子目录Makefile:比如drivers/serial/Makefile,指定哪些文件被编译
  • Kbuild:真正的编译规则,定义obj-y、obj-$(CONFIG_XXX)等变量

你想想看,如果你要添加一个新驱动,只需要在对应目录的Makefile里加一行:

obj-$(CONFIG_MY_UART) += my_uart.o

然后在Kconfig里定义CONFIG_MY_UART,就搞定了。

核心理解:U-Boot的编译系统,本质就是配置驱动编译。你通过Kconfig开关,决定哪些代码被编进去。Makefile负责把选中的源文件编译链接成最终镜像。

2.4 知识结构图:U-Boot源码与编译流程

下面这张图,把U-Boot的源码结构、关键文件、编译流程串起来了。你保存下来,以后移植时对照着看。

U-Boot源码结构与编译流程 U-Boot 源码结构与编译流程 源码结构 arch/arm/ — CPU架构代码 board/ — 板级初始化 drivers/ — 外设驱动 dts/ — 设备树源文件 关键文件 include/configs/xxx.h arch/arm/dts/xxx.dts arch/arm/cpu/u-boot.lds configs/xxx_defconfig 编译系统 顶层Makefile 子目录Kconfig/Makefile Kbuild编译规则 scripts/ — 辅助脚本 make xxx_defconfig 生成 .config make 编译源码 生成镜像 u-boot.bin 核心:Kconfig控制编译开关,Makefile负责编译,设备树描述硬件 移植时重点关注: 1. board/ 目录下板级初始化代码 2. dts/ 目录下设备树文件 3. configs/ 目录下默认配置文件

2.5 避坑指南与个人经验

最后,分享几个我踩过的坑,你遇到了能少走弯路。

  • 编译报错找不到头文件:检查include/configs/里的宏定义是否和Kconfig冲突。我遇到过CONFIG_SYS_TEXT_BASE在两个地方定义,编译直接挂。
  • 设备树编译不过:语法错误最常见。用make dtbs单独编译设备树,看具体报错行。我曾经漏了一个分号,查了半小时。
  • U-Boot启动卡死:先确认串口驱动是否选对了。我建议在board_init_f里加个GPIO点灯,确认代码执行到了哪里。
  • 链接脚本问题:如果你改了u-boot.lds,记得检查段地址是否在DDR范围内。我见过有人把.bss段放到未初始化区域,结果全局变量全是乱的。
小技巧:编译时加V=1可以看到详细编译命令,调试很有用。比如:make V=1。