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

好,咱们进入第三章。这一章我打算聊聊U-Boot的编译和配置。说实话,很多刚入行的朋友觉得编译就是个“make”命令的事,其实不然。你想想看,一个Bootloader要适配几百种开发板,几十种CPU架构,它的配置系统能简单吗?

我个人习惯把这一章分成四个部分:Kconfig配置系统、图形化配置、交叉编译链设置、最后生成镜像。咱们一个一个来。

3.1 Kconfig配置系统——U-Boot的“基因开关”

Kconfig是什么?说白了,它就是一套配置管理框架。Linux内核在用,U-Boot也继承过来了。它的作用就是让你像搭积木一样,决定哪些功能编译进去,哪些不要。

我在项目中遇到过一件事:有个同事把U-Boot编译出来,烧进去发现网卡不工作。查了半天,原来是Kconfig里没选上对应的网卡驱动。嗯,这种坑踩过一次就记住了。

Kconfig的核心文件分布在U-Boot源码的各个目录下,文件名就叫Kconfig。顶层目录有一个,每个子目录也有。它们通过source指令串联起来。

Kconfig配置项示例

config CMD_BOOTM
    bool "Enable bootm command"
    default y
    help
      Bootm command is used to boot an OS image
      from memory. Say Y if you want to boot Linux.

这里我解释一下:bool表示这是个开关选项,要么Y要么N。default y就是默认开启。help后面是帮助信息。你可以在make menuconfig里看到这些文字。

Kconfig支持的数据类型有这些:

类型 说明 典型用法
bool 布尔开关,Y/N 启用某个命令或驱动
tristate 三态,Y/M/N 驱动模块化(U-Boot用得少)
int 整数 缓冲区大小、超时时间
hex 十六进制 内存地址、寄存器基址
string 字符串 默认环境变量、设备树路径

还有一个重要的概念叫依赖关系。比如你选了CMD_NET,它可能自动帮你选上NET。Kconfig用depends on和select来实现这个逻辑。

我的小技巧:当你修改了某个Kconfig文件后,记得运行make olddefconfig来同步配置。否则下次menuconfig可能会报错。

3.2 make menuconfig——图形化配置实战

好了,理论讲完,咱们上手操作。配置U-Boot最常用的命令就是:

make menuconfig

这个命令会弹出一个基于ncurses的图形界面。你可以在里面上下翻、按空格选中、按Y/N切换。我个人觉得,对于新手来说,图形界面比直接改.config文件要直观得多。

进入界面后,你会看到这些顶层菜单:

  • General setup —— 通用设置,比如版本号、本地化
  • Boot images —— 启动镜像格式支持
  • Command line interface —— 命令行命令选择
  • Device Drivers —— 设备驱动,包括串口、网卡、存储
  • Networking support —— 网络协议栈
  • Environment —— 环境变量存储方式(这个咱们后面章节重点讲)

操作快捷键也列一下:

按键 功能
↑ ↓ 移动光标
Enter 进入子菜单
Space 切换选项状态
Y 设为内置
N 取消选择
? 查看帮助
/ 搜索配置项

注意:menuconfig依赖ncurses库。如果你在服务器上编译,可能要先装一下:sudo apt install libncurses-dev。我曾经在最小化安装的Ubuntu上折腾了半天,就是因为没装这个库。

配置完成后,按Exit退出,系统会问你是否保存。选Yes,就会生成.config文件。这个文件就是所有配置的最终结果。

3.3 交叉编译工具链设置

U-Boot是跑在目标板上的,不是跑在你的PC上。所以你得用交叉编译器。什么是交叉编译器?就是在一个架构上生成另一个架构可执行文件的编译器。

设置交叉编译链,核心就是两个环境变量:

export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

CROSS_COMPILE是编译器的前缀。比如你设成arm-linux-gnueabihf-,make就会去找arm-linux-gnueabihf-gcc、arm-linux-gnueabihf-ld这些工具。ARCH指定目标架构,U-Boot用它来选择对应的架构目录。

常见的交叉编译链前缀:

目标架构 前缀示例 常见场景
ARM 32位 arm-linux-gnueabihf- STM32、i.MX、Allwinner
ARM 64位 aarch64-linux-gnu- 树莓派3/4、RK3399
RISC-V riscv64-linux-gnu- K210、SiFive
MIPS mips-linux-gnu- MT7620、MT7621

我个人习惯把这两行写到~/.bashrc里,或者针对每个项目写一个setenv.sh脚本。这样每次打开终端就不用重新设了。

验证工具链:运行${CROSS_COMPILE}gcc --version,如果能正常输出版本信息,说明工具链已经就绪。如果报错“command not found”,那就是没装或者没加到PATH里。

3.4 编译生成u-boot.bin

配置和工具链都准备好了,接下来就是编译。命令很简单:

make -j$(nproc)

-j参数指定并行编译的线程数,$(nproc)会自动获取你CPU的核心数。我建议别超过物理核心数的1.5倍,否则编译服务器可能会卡死。

编译完成后,你会得到一堆文件。最重要的几个:

  • u-boot —— ELF格式的可执行文件,用于调试
  • u-boot.bin —— 纯二进制镜像,烧录用
  • u-boot.map —— 符号映射表,分析地址用
  • u-boot.srec —— Motorola S-record格式,某些烧录器用
  • u-boot.dtb —— 设备树二进制文件(如果配置了OF_CONTROL)

这里我画了一张图,把整个流程串起来:

U-Boot编译与配置流程 第一阶段:配置 Kconfig 源码 make menuconfig .config 文件 第二阶段:编译 交叉编译链 make -j$(nproc) 目标文件 第三阶段:输出产物 u-boot.bin u-boot (ELF) u-boot.map u-boot.dtb 💡 关键点: 配置决定功能,工具链决定架构,编译生成最终镜像。三步缺一不可。

编译过程中如果报错,别慌。大部分错误就这几类:

  • 工具链找不到 —— 检查CROSS_COMPILE和PATH
  • 头文件缺失 —— 比如asm/arch找不到,说明ARCH没设对
  • Kconfig语法错误 —— 你改Kconfig时可能写错了
  • 链接错误 —— 常见于内存布局配置不对

我曾经踩过的坑:有一次编译U-Boot,报错说undefined reference to 'xxx'。我查了半天,发现是Kconfig里某个驱动依赖没选上,导致对应的源文件根本没参与编译。所以遇到链接错误,先检查配置,别急着改代码。

编译成功后,u-boot.bin就是你的最终产物。把它烧到板子上,上电看串口输出。如果能看到U-Boot的启动信息,恭喜你,这一步就完成了。