3、Yocto中U-Boot的集成方式:meta层结构、u-boot_%.bbappend文件编写、配置U-Boot的defconfig与设备树

好,咱们进入正题。前面两章我们把U-Boot的启动流程和Yocto的基础概念过了一遍。这一章,我要带你亲手把U-Boot塞进Yocto的构建体系里。说白了,就是搞清楚三件事:meta层怎么搭、bbappend文件怎么写、defconfig和设备树怎么配。

我在项目里见过不少新手,直接把U-Boot源码扔进meta层,然后改几个变量就以为完事了。结果编译出来要么启动卡死,要么设备树对不上。嗯,这里面的坑,咱们一个一个填。

3.1 meta层的标准结构

先看一个典型的meta层长什么样。你打开任何一个成熟的BSP(板级支持包),目录结构基本都是这个套路:

meta-custom-bsp/
├── conf/
│   ├── layer.conf
│   └── machine/
│       └── myboard.conf
├── recipes-bsp/
│   └── u-boot/
│       ├── u-boot_%.bbappend
│       ├── files/
│       │   ├── myboard_defconfig
│       │   └── myboard.dts
│       └── patches/
│           └── 0001-fix-sdmmc-timing.patch
├── recipes-kernel/
│   └── linux/
│       └── linux-mainline_%.bbappend
└── README

这里有几个关键点:

  • conf/layer.conf:声明这个层的优先级和依赖关系。优先级数字越大,越优先被采用。
  • conf/machine/:定义你的板子型号,比如CPU架构、存储分区、串口参数等。
  • recipes-bsp/u-boot/:U-Boot的配方目录,核心战场。
  • files/:存放你自定义的defconfig、设备树源文件(.dts)、以及一些杂项配置文件。
  • patches/:补丁文件,用于修复bug或添加新功能。
我的习惯: 我会在meta层根目录放一个README,记录这个层支持的板子列表、依赖的Yocto版本、以及一些特殊的构建说明。别小看这个文件,半年后你自己回来看,能省不少事。

3.2 u-boot_%.bbappend 文件编写

bbappend文件是Yocto里最灵活的东西。它不会覆盖原始的bb文件,而是在其基础上追加或修改内容。这个%是通配符,匹配任何版本的U-Boot配方。

来看一个我实际用过的例子:

# u-boot_%.bbappend
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += " \
    file://myboard_defconfig \
    file://myboard.dts \
    file://0001-fix-sdmmc-timing.patch \
"

do_configure:append() {
    # 强制使用我们自己的defconfig
    cp ${WORKDIR}/myboard_defconfig ${S}/configs/
}

do_compile:prepend() {
    # 编译前打个标记,方便调试
    bbnote "Building U-Boot for ${MACHINE} with custom defconfig"
}

这里有几个变量你得搞明白:

变量/函数 作用 我踩过的坑
FILESEXTRAPATHS 告诉BitBake去哪里找额外的文件 路径写错,导致defconfig找不到,编译报错
SRC_URI 列出所有需要下载或本地引入的文件 补丁顺序不对,打不上
do_configure:append 在配置阶段之后执行自定义操作 忘记加${WORKDIR},文件路径不对
do_compile:prepend 在编译阶段之前执行 用来加调试日志,别乱改编译参数
注意: FILESEXTRAPATHS的赋值方式一定要用:=(立即赋值)或者+=,并且要prepend。否则BitBake可能找不到你的文件。我曾经因为这个折腾了一下午,最后发现是赋值时机的问题。

3.3 配置U-Boot的defconfig

defconfig是U-Boot的配置蓝图。它决定了哪些功能被编译进U-Boot,哪些被砍掉。Yocto里,你通常会在files/目录下放一个自定义的defconfig。

生成defconfig的方法很简单:

  1. 先下载U-Boot源码,用make <厂商默认配置>_defconfig生成一个基础配置。
  2. 然后用make menuconfig调整你需要的选项。
  3. 最后用make savedefconfig导出精简版的defconfig。

举个例子,假设你要为一块基于i.MX8M的板子配置U-Boot:

# myboard_defconfig
CONFIG_ARM=y
CONFIG_ARCH_IMX8M=y
CONFIG_SYS_TEXT_BASE=0x40200000
CONFIG_NR_DRAM_BANKS=1
CONFIG_SYS_SDRAM_BASE=0x40000000
CONFIG_DM_MMC=y
CONFIG_MMC_IMX_ESDHC=y
CONFIG_DM_ETH=y
CONFIG_FEC_MXC=y
CONFIG_CMD_BOOTZ=y
CONFIG_CMD_EXT4=y
CONFIG_CMD_FAT=y
CONFIG_OF_CONTROL=y
CONFIG_DEFAULT_DEVICE_TREE="myboard"

注意最后一行CONFIG_DEFAULT_DEVICE_TREE,它指定了U-Boot默认加载的设备树文件名。这个必须跟你后面提供的.dts文件名一致。

关键点: defconfig里不要写死所有选项。只写那些你需要修改的、或者厂商默认配置里没有的。Yocto的构建系统会自动合并基础配置和你提供的配置。这样维护起来更清爽。

3.4 设备树的集成

设备树(Device Tree)是U-Boot和内核之间的硬件描述语言。U-Boot本身也需要设备树来初始化硬件,比如DDR、MMC、网卡等。

在Yocto里集成设备树,通常有两种方式:

  • 方式一: 把.dts文件放在files/目录下,通过bbappend引入。适合小改动。
  • 方式二: 在meta层里创建一个独立的recipes-kernel/linux/配方,专门管理设备树。适合复杂项目。

我个人更推荐方式一,因为简单直接。来看一个.dts文件的片段:

// myboard.dts
/dts-v1/;

#include "imx8mm.dtsi"

/ {
    model = "My Custom Board";
    compatible = "my,myboard", "fsl,imx8mm";

    chosen {
        stdout-path = &uart1;
    };

    memory@40000000 {
        device_type = "memory";
        reg = <0x0 0x40000000 0 0x80000000>;  // 2GB DDR
    };
};

&uart1 {
    status = "okay";
};

&usdhc2 {
    status = "okay";
    bus-width = <4>;
    non-removable;
};

这个文件定义了板子的内存大小、串口、SD卡等外设。U-Boot在启动时会解析这个设备树,然后初始化对应的硬件。

避坑指南: 我曾经在设备树里把reg属性写错了,导致U-Boot只能识别1GB内存,但板子上焊了2GB。排查了整整两天,最后用bdinfo命令才发现内存大小不对。所以,设备树里的地址和大小一定要跟硬件原理图对一遍。

3.5 整体集成流程

把上面这些串起来,整个集成流程就是:

  1. 创建meta层,配置layer.conf和myboard.conf。
  2. 在recipes-bsp/u-boot/下编写u-boot_%.bbappend。
  3. 在files/下放置自定义的defconfig和设备树源文件。
  4. 运行bitbake u-boot,观察编译输出。
  5. 如果编译失败,检查FILESEXTRAPATHS和SRC_URI的路径。
  6. 如果编译成功,用flash.bin或u-boot.bin烧录到板子,验证启动。

下面这张图展示了整个集成流程的核心逻辑:

Yocto中U-Boot集成流程 Yocto U-Boot 集成流程 创建meta层 编写bbappend 配置defconfig 设备树集成 bitbake编译 烧录验证 说明: defconfig和设备树是并行配置的 最终在bitbake阶段合并编译

这张图把整个流程串起来了。你从创建meta层开始,然后编写bbappend,接着并行配置defconfig和设备树,最后通过bitbake编译并烧录验证。每一步都有对应的文件操作和变量设置,缺一不可。

总结一下: meta层是骨架,bbappend是血肉,defconfig和设备树是灵魂。这三者配合好了,U-Boot就能在你的板子上跑起来。如果遇到问题,先检查路径,再检查配置,最后检查设备树。按这个顺序排查,90%的问题都能解决。