4. U-Boot配置与编译:从Kconfig到u-boot.bin

U-Boot的配置与编译,说白了就是告诉它“你要跑在哪个硬件上”,然后把它变成可执行的二进制文件。这个过程看似简单,但里面坑不少。我这些年调试下来,发现很多问题其实都出在配置阶段——配置不对,后面全是白费功夫。

4.1 Kconfig配置系统:U-Boot的“大脑”

Kconfig是Linux内核引入的配置系统,U-Boot把它继承了过来。它的作用是什么?就是让你像搭积木一样,选择要支持的功能、驱动、文件系统等等。

Kconfig的核心文件是Kconfig和Makefile。每个目录下都有这两个文件,它们定义了该目录下的配置选项。比如你想支持某个网卡驱动,就去对应的驱动目录下找Kconfig。

Kconfig配置树结构:U-Boot的Kconfig是分层的。顶层Kconfig在u-boot/根目录,它会source各个子目录的Kconfig。子目录再source更细的模块。这样一层层展开,最终形成完整的配置选项。

我刚开始接触Kconfig时,总觉得它很复杂。后来发现,你只需要记住几个关键点:

  • config:定义一个配置选项,比如config CONFIG_ETH
  • bool/tristate:选项类型,bool是二选一(Y/N),tristate是三选一(Y/M/N)
  • depends on:依赖关系,比如某个驱动依赖某个总线
  • select:反向依赖,选中这个选项会自动选中另一个

举个例子,假设你要添加一个自定义的LED驱动:

config LED_CUSTOM
    bool "Custom LED driver support"
    depends on LED
    help
      This enables support for custom LED driver on board XYZ.

这段代码的意思是:在LED菜单下,增加一个“Custom LED driver support”选项,它依赖于LED功能被启用。

4.2 make menuconfig:图形化配置界面

配置U-Boot最常用的命令就是make menuconfig。它会打开一个基于ncurses的图形界面,让你用键盘上下左右键来选择配置项。

我个人习惯先用make <board>_defconfig加载默认配置,然后再用make menuconfig微调。比如:

make stm32mp157d_atk_defconfig
make menuconfig

这样能保证基础配置是对的,你只需要关注自己需要修改的部分。

小技巧:在menuconfig界面中,按/键可以搜索配置项。比如你想找“USB”相关的配置,直接按/输入USB,所有包含USB的选项都会列出来。这个功能在调试时特别有用。

menuconfig的操作其实很简单:

  • 方向键:上下移动光标
  • Enter:进入子菜单
  • 空格:切换选项状态(Y/M/N)
  • Esc+Esc:返回上一级
  • ?:查看当前选项的帮助信息

配置完成后,选择Save,配置会写入.config文件。这个文件就是U-Boot编译时的依据。

4.3 交叉编译链设置:让代码在目标板上跑

交叉编译链,说白了就是一套工具,让你在PC上编译出能在ARM、RISC-V等架构上运行的代码。U-Boot编译时必须指定正确的交叉编译链。

设置交叉编译链有两种方式:

  1. 环境变量:在终端中设置CROSS_COMPILE
  2. Makefile参数:编译时直接传入

我建议用环境变量,因为这样不容易出错。比如:

export CROSS_COMPILE=arm-linux-gnueabihf-
make stm32mp157d_atk_defconfig
make

这里要注意,CROSS_COMPILE的值必须包含最后的-。为什么?因为Makefile会把它和gcc、ld等工具名拼接起来,形成完整的工具链命令。

我曾经踩过的坑:有一次我忘了设置CROSS_COMPILE,结果U-Boot用PC的gcc编译,生成了x86架构的代码。烧到板子上当然跑不起来。折腾了半天才发现是编译链没设对。所以,编译前一定要检查CROSS_COMPILE是否正确。

常见的交叉编译链前缀:

目标架构 编译链前缀 示例
ARM (32位) arm-linux-gnueabihf- arm-linux-gnueabihf-gcc
ARM64 (AArch64) aarch64-linux-gnu- aarch64-linux-gnu-gcc
RISC-V (64位) riscv64-linux-gnu- riscv64-linux-gnu-gcc
MIPS mips-linux-gnu- mips-linux-gnu-gcc

4.4 编译生成u-boot.bin

配置和编译链都搞定后,就可以编译了。直接执行make即可:

make -j4

-j4表示用4个线程并行编译,能加快速度。具体数字根据你电脑的CPU核心数来定。

编译完成后,会在u-boot/目录下生成多个文件:

  • u-boot:ELF格式的可执行文件,用于调试
  • u-boot.bin:纯二进制文件,用于烧录
  • u-boot.map:符号映射表,调试时很有用
  • u-boot.srec:S-Record格式,用于串口下载

我们最常用的是u-boot.bin。它会被烧录到板子的Flash中,上电后CPU从复位向量开始执行。

编译流程:U-Boot的编译过程大致是:先根据.config生成include/autoconf.mk,然后编译各个目录下的源文件,最后链接成u-boot,再通过objcopy转换成u-boot.bin。

如果你用的是某些特定平台(比如STM32MP1),可能还需要生成u-boot.stm32这样的带头部格式的文件。这通常由make自动完成,或者需要额外执行make u-boot.stm32。

4.5 配置与编译流程总览

下面这张图展示了从配置到生成u-boot.bin的完整流程:

U-Boot配置与编译流程 make xxx_defconfig make menuconfig .config make CROSS_COMPILE=... u-boot.bin 关键说明 • 默认配置来自 configs/目录 • menuconfig修改后 保存到.config 编译产物 • u-boot (ELF调试) • u-boot.bin (烧录) • u-boot.map (符号表)

4.6 常见问题与避坑指南

配置编译过程中,我遇到过不少问题。这里列几个典型的:

问题1:编译报错“No rule to make target”

这通常是因为.config中启用了某个功能,但对应的源码文件不存在。比如你选了CONFIG_LED_CUSTOM,但没写对应的led_custom.c。解决办法:检查Kconfig和Makefile是否匹配。

问题2:编译链版本不兼容

U-Boot对编译链版本有要求。太老的gcc可能不支持某些语法,太新的gcc可能产生警告导致编译失败。我一般用Linaro提供的编译链,比较稳定。

问题3:配置选项冲突

有时候你选了A,但A依赖B,而B又和C冲突。menuconfig会提示你,但如果你用make oldconfig批量处理,可能会忽略这些冲突。建议每次修改配置后,都重新make menuconfig检查一遍。

嗯,配置编译这块就这些。记住一个原则:配置要精确,编译链要对齐,编译过程要耐心。只要这三步走对了,u-boot.bin就能顺利生成。