尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

自定义Yocto Machine的eMMC启动Panic恢复实战

自定义Yocto Machine的eMMC启动Panic恢复实战 最近被一块 STM32MP135DAF 的板子折腾到凌晨一点。事情是这样的我给自己的板卡做了自定义 Yocto machineBSP 编译、打包、烧录全都过了结果从 eMMC 启动时直接 Kernel Panic更崩溃的是panic 之后我改用 STM32CubeProgrammer 想重新连目标板一直连不上。整套问题串在一起就是标题里写的Custom Yocto Machine / eMMC Boot / PANIC / CubeProgrammer unable to Reconnect。这篇我把自己踩过的坑、排查思路、恢复手段和自定义 Yocto machine 里那些容易忽视的细节完整记录下来内容偏实战适合正在做 STM32MP13/MP15 系列 Linux 产品的工程师也适合刚接触 Yocto 的嵌入式新手照着排查。1. 项目背景为什么做自定义 Yocto Machine为什么选 eMMC 启动1.1 需求与方案选型STM32MP135DAF 属于 STM32MP13 系列单核 Cortex-A7 的 MPU主频不算高但胜在成本、功耗和供货稳定很多工业控制、HMI、IoT 网关类产品选它做核心处理器。官方有 DK 评估板对应的 Yocto machine 是stm32mp135f-dk。问题在于一旦你自己画板子DDR 颗粒、PMIC、以太网 PHY、串口、SDMMC 等外设都可能有变化官方 machine 里的设备树、TF-A 配置、U-Boot defconfig 都不一定适用所以必须做自定义 Yocto machine。我这次的目标很明确根文件系统放 eMMC开机直接从 eMMC 引导不依赖 SD 卡。量产设备没人愿意插一张 SD 卡跑eMMC 焊在板上速度快、抗震动、不容易松动这是选 eMMC 作为启动介质的主要原因。eMMC 比 SD 卡复杂的地方在于它内部有独立的 boot 分区且受 eMMC 协议里的 EXT_CSD 寄存器控制启动链路里的任何一环没配对就会出现烧了也起不来甚至连不上调试工具的情况。1.2 eMMC 和 SD 卡启动的本质差异很多人以为 eMMC 就是一颗焊在板子上的 SD 卡用法一样这其实是最大的误解。虽然底层都走 MMC 协议但 eMMC 内部按照用途把 Flash 划分为以下几类区域Boot Partition 1 / Boot Partition 2专门给 SoC ROM 加载 FSBL 用的区域容量不大一般 4 MiB 或 8 MiB。RPMBReplay Protected Memory Block存放安全相关数据普通读写访问不了。UDAUser Data Area用户数据区也就是我们常说的/dev/mmcblk0整个 eMMC 设备U-Boot、kernel、rootfs 都放这里。Boot Configuration Registers通过 EXT_CSD 寄存器的BOOT_PARTITION_ENABLE字段决定 ROM 从 boot1 还是 boot2 加载 FSBL。STM32MP1 系列 ROM 启动 eMMC 时不会像 SD 卡那样直接扫描 UDA 里的分区而是先根据 BOOT 引脚选择 eMMC 作为启动源然后读取 eMMC 的 boot partition 1 中的 FSBLTF-A BL2。如果 boot partition 里没有有效 FSBL或者BOOT_PARTITION_ENABLE没有配置成对应 boot 分区就会出现完全没反应或无限重试的现象。这里也是我后续排查 CubeProgrammer 无法连接时重点关注的一个方向。提示eMMC 的 boot partition 是芯片出厂后由 Boot ROM 直接读取的很多烧写工具默认会往 UDA 里写 FSBL或者写进了 boot partition 但没有正确使能 boot partition这两种情况都会导致启动失败。烧写前必须先确认 FlashLayout 里 FSBL 的目标分区。2. 启动链路与 PANIC 现场拆解2.1 STM32MP13 上电后的完整启动链路STM32MP13 的启动过程大致可以分成这几个阶段芯片上电复位后Cortex-A7 从 ROM 代码开始执行。ROM 根据 BOOT[2:0] 引脚的电平组合选择启动介质。如果选择 eMMC则从 eMMC boot partition 中读取 FSBL。FSBL 就是 TF-A 的 BL2它负责初始化时钟、DDR、电源管理等基础硬件为后续加载做好准备。TF-A 接着从 eMMC 的 UDA 区指定位置加载 FIPFirmware Image PackageFIP 里打包了 BL32OP-TEE和 BL33U-Boot。U-Boot 启动后读取环境变量和 bootcmd加载 kernel 和设备树。Kernel 启动挂载根文件系统执行 init。整个链路里任何一个环节的镜像格式错误、加载地址错误、分区表不匹配都会导致启动失败。其中 Kernel Panic 属于最后一步也就是 U-Boot 已经成功把 kernel 加载起来了但 kernel 在挂载根文件系统时出了问题。很多人遇到 panic 第一反应是FSBL 坏了其实更常见的是 rootfs 分区对不上、设备树里 mmc 节点配置错误、或者 root 启动参数写错了。2.2 我看到的 PANIC 日志我这次 eMMC 启动时的典型输出是这样[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fc075] [ 0.000000] Linux version 5.15.25 (oe-useroe-host) (gcc version 11.3.0) ... [ 1.587234] mmc1: new high speed MMC card at address 0001 [ 1.592876] mmcblk1: mmc1:0001 8.0 GiB [ 1.597245] mmcblk1boot0: mmc1:0001 4.00 MiB [ 1.601345] mmcblk1boot1: mmc1:0001 4.00 MiB [ 1.605322] mmcblk1rpmb: mmc1:0001 8.00 MiB, chardev (239:0) [ 1.611233] mmcblk1: p1 p2 p3 p4 p5 [ 1.616782] [ 1.687234] VFS: Cannot open root device mmcblk1p6 or unknown-block(179,6): error -2 [ 1.695433] Please append a correct root boot option; here are the available partitions: [ 1.703452] 0100 3846000 ram0 ... [ 1.724348] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(179,6) [ 1.733562] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.15.25 [ 1.739245] Hardware name: STM32 (Device Tree) [ 1.743892] Call trace: [ 1.746332] dump_backtrace0x0/0x1a0 [ 1.750104] show_stack0x18/0x70 [ 1.753345] dump_stack_lvl0x60/0x78 [ 1.756896] panic0x148/0x368 [ 1.759897] mount_block_root0x1d0/0x2a4关键信息是Cannot open root device mmcblk1p6。内核明确告诉你它想挂载的根设备是mmcblk1p6但这个分区根本不存在扫描到的分区只有 p1~p5。这个问题和 Yocto 里的 WKS 分区文件、U-Boot 环境变量、设备树里的chosen/bootargs都有关系后面第 5 节会详细讲。2.3 哪些原因会触发同样的 PANIC根据我排查的经验出现VFS unable to mount root fs的 panic常见原因有以下几个U-Boot 的 root 参数写死成旧分区号。我这次就是这个原因。官方 machine 默认 U-Boot 环境变量里root/dev/mmcblk1p6但自定义 WKS 文件把 eMMC 分区调整成了 5 个分区rootfs 实际挪到了 p5导致 kernel 拿着 p6 去挂载自然失败。WKS 分区表和实际烧写内容不一致。Yocto 生成的wic镜像里分区布局是一回事STM32CubeProgrammer 按 FlashLayout 烧到 eMMC 后分区布局是另一回事两边没对齐。rootfs 镜像损坏。烧写过程中断电、工具异常、镜像文件本身不完整都会导致分区里虽然有内容但无法挂载。设备树里 mmc 节点配置错误。比如自定义板子的 eMMC 接到 SDMMC2但设备树里把这个节点 disabled 了kernel 根本扫描不到 eMMC 设备最后卡在找不到 rootfs。内核里缺少对应文件系统驱动。rootfs 是 ext4 但 kernel 没编入 ext4或者 rootfs 是 initramfs 但 initrd 没加载进去。排查顺序建议是先看串口日志里扫描到的分区列表再对照实际分区表最后检查 U-Boot 环境变量里的 bootargs。不要上来就重新烧整个镜像很多时候只是 root 参数的问题。3. 比 PANIC 更麻烦的CubeProgrammer 为什么连不上目标板3.1 下载模式是救命入口STM32MP1 系列有一个下载模式通过在复位后让 BOOT 引脚处于特定组合ROM 代码不会尝试从 eMMC/SD 卡加载程序而是直接进入 USB DFU 或 UART 下载模式。这也是 STM32CubeProgrammer 能够连接目标板、烧写镜像的原理。具体 BOOT 引脚组合不同开发板和核心板不一样比如 ST 的官方 DK 板通常有一组拨码开关需要查阅原理图和数据手册里的 BOOT 配置表。这里特别提醒看到 panic 之后千万别直接按一下复位就打开 CubeProgrammer 点连接。如果 BOOT 引脚还停在 eMMC 启动模式ROM 只会又一次去读 eMMC、又一次启动、又一次 panic根本不会进入 DFU 枚举。3.2 为什么 eMMC Boot PANIC CubeProgrammer 连不上 经常一起出现这三个现象同时出现非常典型我先按时间线梳理一下你从 eMMC 启动系统跑到 kernel然后 panic。panic 后你打开 CubeProgrammer想重新连接目标板。CubeProgrammer 一直等待 USB DFU 设备但设备列表里什么都没有。正常情况下只要 BOOT 引脚切换为下载模式再重新上电ROM 就应该枚举出 USB DFU 设备。但如果出现连不上问题几乎都出在 BOOT 引脚状态、复位行为、USB 物理链路、外部供电这几个环节。我这次实际遇到的情况很典型BOOT 拨码开关调到了下载模式但 USB 线插在了板子上一个不参与 USB DFU 的 Type-C 口上导致 CubeProgrammer 永远等不到设备。这里要特别强调一点不是所有 USB 口都连接到了 SoC 的 OTG 控制器板子上可能有两个 USB 口但只有一个支持 DFU 枚举需要对照原理图确认。还有一类情况是 FSBL 或 U-Boot 里配置了 PMIC/复位相关的操作panic 后系统处于一种半死不活的状态如果只按复位键外设和 PMIC 可能没有完全复位导致 USB 枚举异常。这时候应该彻底断电等待几秒让板上所有电容放完电再重新上电。3.3 快速判断清单遇到 CubeProgrammer 连不上目标板时我通常按下面的顺序排查优先级检查项操作方式1BOOT 引脚是否处于下载模式查阅原理图/拨码开关说明确认后断电再上电2USB 线缆和数据口换一根好的数据线插到正确的 OTG 口3目标板是否正常上电看电源指示灯、串口是否有 ROM 启动日志4设备管理器是否出现 DFU 设备Windows 下查看是否有 STM32 BOOTLOADER 设备5是否被 OTP 锁定启动介质排查 OTP 是否配置了强制 eMMC 启动6是否需要外部供电某些核心板只靠 USB 供电可能不稳定接上 5V 电源再试注意很多 STM32MP1 的板子下载模式需要把 BOOT 引脚电平设为指定组合然后断电再上电。光按复位键不一定会让 ROM 重新采样 BOOT 引脚或者即使采样了USB 枚举也可能因为之前的软件状态异常而失败。最稳妥的动作是断电 - 拨好 BOOT - 重新上电。4. 恢复流程从“变砖”到重新烧录4.1 第一步强制进入下载模式并确认设备这一步是后续一切操作的基础。我当时的具体操作是断开板子所有外部电源。把 BOOT 拨码开关拨到下载模式对应的组合。查自己板子的原理图不要凭经验猜测官方 DK 板。用 USB 线连接板子的 OTG 口确认过原理图的那个口和电脑。给板子上电。在 Windows 设备管理器里观察是否出现新的 USB 设备一般为 STM32 BOOTLOADER 或类似名称。如果使用 UART 下载模式则把板子的 UART boot 引脚对应的串口接到电脑 USB 转串口工具然后在 CubeProgrammer 里选择对应 COM 口。UART 下载模式对线缆要求低调试初期比 USB 更省心。4.2 第二步用 CubeProgrammer 连接并擦除重烧确认设备出现后我用的命令式操作如下# 列出当前 USB 接口连接的目标设备 STM32_Programmer_CLI -c portusb # 如果能看到设备再加载 FlashLayout 进行全盘烧写 STM32_Programmer_CLI -c portusb -l flashlayout.stm32如果 FlashLayout 文件里包含擦除指令CubeProgrammer 会先擦掉 eMMC 对应分区再写入新镜像。这个彻底擦除的动作非常关键因为它可以把之前自定义 machine 烧进去的错误 FSBL、错误 U-Boot 环境变量全部清掉给我一个干净的状态重新开始。烧写完成后把 BOOT 拨回 eMMC 启动模式断电再上电验证是否可以正常启动。4.3 备用路线SD 卡启动后修复 eMMC如果 CubeProgrammer 怎么都连不上或者手里暂时没有 ST-Link 工具还有一个非常实用的办法用 SD 卡启动一个正常的 Linux 系统然后从 Linux 里操作 eMMC。前提是你的板子支持 SD 卡启动并且有一张已经烧好标准 Yocto 镜像的 SD 卡。SD 卡系统起来后eMMC 会以/dev/mmcblk0或者/dev/mmcblk1的形式出现具体哪个取决于 SDMMC 节点顺序。用下面的命令可以查看ls /dev/mmcblk*你会看到类似/dev/mmcblk0、/dev/mmcblk0boot0、/dev/mmcblk0boot1这样的设备。/dev/mmcblk0boot0就是 eMMC 的 boot1 分区把 TF-A 镜像直接写进去即可# 先使能 boot0 分区写入有的内核版本需要 echo 0 /sys/block/mmcblk0boot0/force_ro # 写入 FSBL dd iftf-a-stm32mp135f-custom.elf of/dev/mmcblk0boot0 convfsync之后用fdisk或parted查看 UDA 分区确认 FIP、bootfs、rootfs 所在分区重新挂载并写入对应镜像。这个方法虽然比 CubeProgrammer 繁琐但在完全没有 USB DFU 的情况下是保住一块板子的重要手段。4.4 U-Boot 里的临时救命命令如果 SD 卡能启动到 U-Boot你还可以在 U-Boot 命令行下做一些紧急处理比如mmc dev 0 # 查看 eMMC 的分区 mmc part # 查看 boot 分区配置 mmc bootbus # 设置从 boot1 启动 mmc bootpart enable 1 0mmc bootpart enable 1 0的意思是使能 boot1 分区作为启动源。如果在之前的烧写过程中eMMC 的BOOT_PARTITION_ENABLE被意外改掉了这条命令可以把状态救回来。我见过有人烧写时把 FSBL 写到了 boot2但 eMMC 配置的是从 boot1 启动结果每次开机都失败CubeProgrammer 也连不上最后就是用 U-Boot 的mmc bootpart修好的。# 重新启动并进入 U-Boot加载统一启动镜像 load mmc 0:4 0xc0000000 bootfs/uImage load mmc 0:4 0xc0000500 bootfs/stm32mp135f-custom.dtb bootm 0xc0000000 - 0xc0000500这种方式适合临时验证不适合量产流程但排查问题的时候非常有用。4.5 重新烧录后的验证步骤恢复到能启动的状态后不要急着把板子扔一边建议做一轮完整的启动验证断电、BOOT 拨到 eMMC 模式、上电。串口观察完整启动日志确认 TF-A、OP-TEE、U-Boot、kernel 全部正常。进入系统后执行mount确认根文件系统挂载的是预期的 eMMC 分区。执行cat /proc/cmdline确认 bootargs 里 root 参数和设备树匹配。重启两三次确认每次都能稳定启动避免出现偶发性 panic。5. 自定义 Yocto Machine 配置里容易被忽略的坑5.1 machine 配置的三个关键变量自定义 Yocto machine 不是简单复制一份stm32mp135f-dk.conf改个名字就完事有几个变量需要特别关注# conf/machine/myboard.conf include conf/machine/include/stm32mp1common.inc MACHINE myboard # 指定设备树文件名必须和内核源码里的 dts 实际生成的文件一致 KERNEL_DEVICETREE stm32mp135f-myboard.dtb # 指定 U-Boot 的 defconfig UBOOT_CONFIG stm32mp135f_myboard_defconfig # 指定启动介质这个直接影响 TF-A 和 U-Boot 的编译配置 STM32MP_BOOT_MEDIA emmc # 指定 WKS 分区文件 WKS_FILE stm32mp1-emmc.wks其中STM32MP_BOOT_MEDIA是 meta-st-stm32mp 里的关键变量它会影响 FSBL 是从 eMMC 加载还是从 SD 卡加载。如果你用的是官方stm32mp1common.inc但没有显式设置这个变量默认可能不是 eMMC那后面烧到 eMMC 里也起不来甚至会出现FSBL 试图从 SD 卡找 FIP这种诡异现象。5.2 FlashLayout 文件不能直接拿来就用Yocto 构建完成后会在镜像输出目录生成对应的 FlashLayout 文件例如st-image-core-myboard-...flashlayout。里面的分区目标、镜像路径、偏移地址都是根据你设置的 machine 和 WKS 文件生成的。问题在于很多人图省事直接用官方 DK 的 FlashLayout 烧自己的板子或者自己改了几行 WKS 但没有同步更新 FlashLayout。结果是分区号对不上、rootfs 写进了错误的分区启动时就出现我在第 2 节贴的那种mmcblk1p6 not found的 panic。所以我特别建议自定义 machine 后每次都确认 FlashLayout 里的分区定义和 WKS 文件一致尤其是多分区场景下 rootfs 的 partition id。FlashLayout 文件内容类似下面的格式id 和 partition 列需要仔细核对#opt id partition binary -b 0x01 fsbl1 arm-trusted-firmware/tf-a-stm32mp135f-myboard.elf -b 0x02 fsbl2 arm-trusted-firmware/tf-a-stm32mp135f-myboard.elf -b 0x03 fip fip-stm32mp135f-myboard.bin -b 0x04 bootfs bootfs.ext4 -b 0x05 rootfs rootfs.ext4如果发现 rootfs 的 id 是 0x05但 U-Boot 环境变量里写的是/dev/mmcblk1p6那 panic 几乎跑不掉。5.3 分区表、bootargs 和设备树三者必须统一我这次 panic 的根因往深了说就是分区表、bootargs、设备树三者没有对齐。分区表由 WKS 文件决定U-Boot 和 kernel 看到的分区顺序必须一致。bootargs 里的 root 参数必须指向实际存在且包含 rootfs 的分区。设备树里chosen节点可以设置bootargs但如果 U-Boot 环境变量里也有 bootargs最终生效的可能是 U-Boot 传给 kernel 的那份。排查时可以在 U-Boot 环境里用下面的命令查看实际 bootargsprintenv bootargs printenv bootcmd然后在 kernel 起来后执行cat /proc
返回列表