
这周我在这块 STM32MP257F-EV1 上被一个“build 完却起不来”的问题卡了两天。事情本身不复杂从 ST 官方 distribution package 拉下来的 6.6.116 版本按 README 的步骤把镜像烧进一张 32GB SD 卡插到板子上电串口没有输出板子像死了一样。一开始我以为是内核编译坏了后来又怀疑是 TF-A 的 FIP 没烧对折腾一圈才发现真正的问题远没有想象中玄学而是“分区布局”和“启动载体”没有对上。这篇文章就是这次排障的完整记录给打算在 STM32MP257F-EV1 上用 SD 卡启动原厂 distribution package 的朋友一份可复现的检查清单顺便把启动链路里最容易踩的几个坑一次性说清楚。1. 先别急着改代码SD 卡启动失败的物理链路和逻辑链路1.1 BootROM 到 U-Boot 之间到底发生了什么STM32MP257F 属于 STM32MP2 系列内部是一颗双核 Cortex-A35 加上一颗 Cortex-M33 的组合启动链路和之前大家更熟悉的 STM32MP15 不太一样。MP15 时代是 BootROM - FSBL (TF-A BL2) - U-Boot中间没有强制性的 FIP 概念。到了 MP25 时代ST 把 TF-A 的可执行文件、OP-TEE、U-Boot 全部打成一个 FIPFirmware Image Package包BL2 阶段需要从固定分区里把这个 FIP 读出来验证通过后再跳进 EL3、OP-TEE最后才到 U-Boot。从 SD 卡启动的完整链条大致是BootROM 根据启动引脚电平选中 SDMMC 接口开始扫描卡里的固定位置。BootROM 加载第一个 FSBL通常是 tf-a-stm32mp257f-ev1.stm32也就是 TF-A BL2。BL2 跑起来后从 FIP 分区读取 fip 文件里面包含了 BL31 / BL32 (OP-TEE) / BL33 (U-Boot)。U-Boot 启动后读取 extlinux 配置或 boot.scr再加载内核和 DTB。内核解压后挂载 rootfs进入 Linux 用户空间。任何一个环节断了现象都不一样。因此排障第一步永远不是重新编译一遍而是先确认“现在到底卡在哪一步”。这是整个排障逻辑的起点也是我这次踩坑后最深的体会。1.2 6.6.116 distribution package 构建产物到底是什么ST 官方 distribution package 构建系统底层是 Yocto OpenEmbedded版本号 6.6.116 实际指的是它发布的分支和构建编号内核大概率是基于 Linux 6.6 的长期维护分支整体打包逻辑由 meta-st 层控制。整个构建过程会产生很多中间产物但真正需要烧到 SD 卡里的其实只有几样tf-a-stm32mp257f-ev1.stm32FSBL也就是 TF-A BL2会被放在 sd 卡的 fsbl1/fsbl2 分区。fip-stm32mp257f-ev1.binFIP 固件包里面包含 OP-TEE 和 U-Boot。bootfs一个 ext4 分区里面包含 extlinux 配置文件、内核fitImage或Image、设备树 dtb、可能还有 boot.scr。rootfs根文件系统包含 OpenSTLinux Weston、开发工具、用户空间程序。如果你构建时用的是st-image-weston这类目标最终 Yocto 会生成一个.wic整卡镜像和一个/boot目录集合。很多人在这一步会犯一个错误只关心“.wic 镜像 dd 进 SD 卡”忽略了 distribution package 版本更新后分区布局可能已经变化。尤其是 6.6.116 这一版我在实际查看构建输出时发现它默认把 bootfs 和 rootfs 分成了两个独立分区而不是像某些老教程那样把内核和 dtb 直接塞进 rootfs 的/boot目录。这个细节直接导致了后面 U-Boot 找不到启动配置。1.3 从 SD 卡启动的三个前提从 SDK 卡启动要想成功必须同时满足三个前提引脚电平正确、分区布局正确、镜像文件完整。前提作用失败时的典型现象启动拨码/引脚电平决定 BootROM 去扫描哪个外设上电后串口完全无输出USB 也没有枚举分区布局让 BootROM/BL2/U-Boot 在正确偏移找到文件有 TF-A 输出但停在 BL2或 U-Boot 没有起来镜像文件完整FIP、FSBL、内核、dtb 都不缺失且校验通过U-Boot 可以起来但找不到内核/extlinux或 Kernel panic这三个前提是有先后顺序的。我这次的问题出在第二个“分区布局”上属于最常见但也最容易自己造成的一类错误。如果你也遇到类似情况建议先从这三条里选一条切入不要一上来就怀疑代码。2. 把现象拆开看从拨码开关到串口日志2.1 EV1 板上决定“哪个介质先启动”的开关STM32MP257F-EV1 这块评估板设计得比较规整板载 eMMC、SD 卡槽、USB DFU 和 ST-LINK 虚拟串口。板子默认支持从多种介质启动但具体从哪个介质启动取决于 boot 引脚和拨码开关状态。上电之前先看板子丝印上标了 BOOT 切换区域通常是一排红色或者白色的拨码开关旁边会印着BOOT1、BOOT2之类的编号。我在第一次上电失败后第一反应是检查这里。实际上我并没有改过拨码默认位置应该已经是 SD 卡启动但后面排查时发现一个容易忽略的细节有些 EV1 板子出厂默认跳线是指向板载 eMMC 的。如果你用的是从 eMMC 启动过系统的板子又随手把 SD 卡插进去BootROM 可能还是按 eMMC 路径走导致你烧的 SD 卡压根没被扫描。官方手册里关于启动模式会给出一个真值表BOOT0/1/2 三位组合对应不同介质。对我这块板子SD 卡启动的组合是需要把 BOOT switch 的某一位拨到 ON另外两位拨到 OFF。具体哪几位不同批次可能微调建议直接查板子 user manual 的 “Boot mode configuration” 章节不要凭记忆。2.2 串口日志怎么读STM32MP257F-EV1 板载 ST-LINK插上 USB 后会出现一个虚拟串口一般是/dev/ttyACM0或/dev/ttyUSB0波特率固定 115200。Linux 下直接用 minicom 或 screen 就能看sudo dmesg | grep tty # 找到 ttyACM0 或 ttyUSB0 sudo minicom -D /dev/ttyACM0 -b 115200Windows 下用串口助手打开对应 COM 口波特率同样 115200。如果完全看不到输出不要急着判板子死掉先确认串口设备是否被系统识别然后检查板子供电是否正常最后再检查拨码。串口无输出时我们无法判断 BootROM 是否在扫描 SD 卡后续排障会很难受。正常启动时日志大致会有几个明显阶段TF-A / BL2 的版本信息比如“BL2: v2.8-stm32”。OP-TEE 的输出。U-Boot 的启动 banner像U-Boot 2023.10-stm32mp。extlinux 加载信息比如Loading /boot/extlinux/extlinux.conf。Kernel 输出最后出现VFS: Mounted root ...。如果你看到某一行后不再继续问题基本就锁定在那一步和上一步之间。2.3 通过日志把问题阶段锁定在十分钟内串口日志是最直观的定位工具花几分钟就能确定启动失败的具体环节。我整理了一个快速对照表适合在任何一块 STM32MP2 板子上排查 SD 卡启动问题日志现象说明问题阶段优先检查项完全无输出BootROM 没起来或没选中 SD卡拨码开关、供电、串口线、SD卡是否插入有 BL2 输出但停在ERROR或Image ID相关位置TF-A 读取 FIP 失败FIP 分区位置、FIP 文件是否完整BL2 正常U-Boot banner 未出现FIP 内的 U-Boot 未加载成功FIP 内镜像是否损坏、TF-A 与U-Boot 版本是否匹配U-Boot 起来但提示No boot device available或No extlinux.confU-Boot 找不到启动配置bootfs 分区、extlinux.conf 路径、环境变量kernel 起来但 panic 在挂载 rootfs内核和 dtb 已经加载但 rootfs 不对分区表、bootargs 中的 root 参数我这次看到的正是中间偏后的一种U-Boot 能启动但扫描扇区后找不到 extlinux 配置。这说明 FSBL 和 FIP 都没问题问题出在 U-Boot 之后的文件读取阶段。3. 最大嫌疑SD 卡分区表与镜像物件的排布3.1 STM32CubeProgrammer 烧写时 flashlayout 在做什么很多人习惯用 STM32CubeProgrammer 烧写 STM32MP2 系列因为 ST 提供了一套 TSV 格式的 flash layout 文件例如flashlayout_st-image-weston/FlashLayout_sdcard_stm32mp257f-ev1.tsv。这个文件的内容本质上是在告诉下载器SD 卡上每个偏移量放什么文件、分区多大、类型是什么。用图形界面或命令行加载 tsv 后CubeProgrammer 会按偏移把 FSBL、FIP、bootfs、rootfs 依次写入 SD 卡。TSV 文件看起来大概是这样的结构opt id name binary offset partition - 0x01 fsbl1 tf-a-stm32mp257f-ev1.stm32 0x0 binary - 0x03 fip fip-stm32mp257f-ev1.bin 0x0 binary - 0x04 bootfs st-image-bootfs.ext4 0x0 system - 0x05 rootfs st-image-weston.ext4 0x0 system不同版本可能字段不同但含义都是“把哪个文件写到 SD 卡的哪个偏移”。这个文件由构建系统自动生成和 distribution package 版本严格绑定。如果你的镜像是在 6.6.116 上构建的就千万不要拿老版本比如 5.0 或 6.1的 TSV 文件来烧写。锁版本这一点在嵌入口罩机领域极其重要很多人启动失败就是在这里开始跑偏的。3.2 如果不信官方脚本手动分区怎么排我这次没有用 CubeProgrammer 的烧录流程而是选择手动分区后用dd写 FSBL/FIP再用mkfs.ext4创建 bootfs 和 rootfs结果踩了坑。手动分区方案本身完全可行但必须和构建产物对应。针对 6.6.116 构建输出合理的 SD 卡分区应该是 GPT 格式至少包含下面几个分区分区1FSBL建议 16MB写入tf-a-stm32mp257f-ev1.stm32。分区2FSBL 备份建议 16MB写入同一个 FSBL 文件。分区3FIP建议 64MB写入fip-stm32mp257f-ev1.bin。分区4bootfs建议 256MBext4存放/boot或 extlinux 目录。分区5rootfs占满剩余空间ext4。在 Linux 下创建分区可以用sgdisk或fdisk关键是要先清掉卡上的旧分区表sudo wipefs -a /dev/sdX sudo sgdisk -Z /dev/sdX随后创建分区。注意分区大小基准使用当前 SD 卡实际容量不要照抄别人的数值否则最后 rootfs 空间可能不够用。创建完分区后把 FSBL 和 FIP 分别写入对应分区sudo dd iftf-a-stm32mp257f-ev1.stm32 of/dev/sdX1 bs512 convfdatasync sudo dd iftf-a-stm32mp257f-ev1.stm32 of/dev/sdX2 bs512 convfdatasync sudo dd iffip-stm32mp257f-ev1.bin of/dev/sdX3 bs512 convfdatasyncbootfs 和 rootfs 则用 mkfs 创建文件系统后挂载拷贝内容sudo mkfs.ext4 -L bootfs /dev/sdX4 sudo mkfs.ext4 -L rootfs /dev/sdX5分区布局和手动烧写并不复杂但每一步都要检查。尤其是“把 bootfs 当成了 rootfs 唯一分区”这种错误极容易发生在用旧脚本复用的情况下。3.3 为什么 FSBL/FIP 错位会表现为“完全没有启动输出”如果手动 dd 时把 FSBL 写错到了别的分区偏移BootROM 可能根本扫描不到合法镜像表现为串口完全无输出。更隐蔽的是FSBL 写对了但 FIP 偏移错了这时 BL2 会有输出但会报找不到 FIP。看起来像是“有部分输出但启动不完整”其实根源都在分区布局上。STM32MP25 的 BootROM 并不像桌面电脑那样扫描分区表它是在指定外设上按固定偏移去读数据。因此如果你用fdisk创建了分区却没有把 FSBL 写到第一个分区起始位置那么 BootROM 读到的就是空数据。这是很多人误解最深的地方以为有了分区表BootROM 就会自动去“寻找”可启动分区。实际上 BootROM 的工作方式是“盲读”固定位置而不是解析文件系统。所以手动布局时分区顺序和写入偏移必须严格匹配 BootROM 的期望。同样BL2 在加载 FIP 时也依赖于一个固定的分区匹配机制通过分区名或者 ID 来找 FIP。如果 FIP 文件被截断、或者分区尺寸小于文件尺寸BL2 会在校验阶段报错。这也是为什么我强烈建议优先使用构建系统自动生成的 TSV 文件或者整卡.wic镜像而不是手动一个文件一个文件去摆。4. 卡在“No boot device”之后我的排查记录和修复步骤4.1 我在 6.6.116 上实际看到的日志特征这次失败的具体日志我先贴出来方便你对照。U-Boot 正常启动后最后几行是这样的switch to partitions #0, OK mmc0 is current device Scanning mmc 0:1... No extlinux.conf found Booting from disk... No bootable device看到No extlinux.conf found的那一刻我就知道FSBL 和 FIP 都过关了问题集中在 U-Boot 读取 bootfs 这一步。bootfs 分区是 ext4 文件系统而 U-Boot 扫描的是/boot/extlinux/extlinux.conf如果这个文件不存在或者路径不对U-Boot 就会认为没有可启动介质。结合我手动分区的操作我立即怀疑是自己把 bootfs 和 rootfs 合并了。我先前在创建分区时只分了 4 个分区fsbl1、fsbl2、fip、rootfs然后把内核、dtb、extlinux.conf 全部放进了 rootfs 的/boot目录。这个布局在老版本里可行因为老版本的 U-Boot 会去 rootfs 分区里的/boot找配置。但 6.6.116 的 U-Boot 环境变量似乎更倾向去扫描独立 bootfs 分区导致它没有主动去 rootfs 的/boot里翻找。4.2 修复步骤重新分区并正确放置 bootfs确认根因后修复并不复杂。我使用sgdisk将整张 SD 卡重新分成 5 个分区前面 4 个分区严格按照官方 TSV 定义fsbl1、fsbl2、fip、bootfs最后 1 个分区作为 rootfs。操作顺序大致如下sudo wipefs -a /dev/sdX sudo sgdisk -Z /dev/sdX # 分区1FSBL 16MB sudo sgdisk -n 1:0:16M -c 1:fsbl1 /dev/sdX # 分区2FSBL 备份 16MB sudo sgdisk -n 2:0:16M -c 2:fsbl2 /dev/sdX # 分区3FIP 64MB sudo sgdisk -n 3:0:64M -c 3:fip /dev/sdX # 分区4bootfs 256MB sudo sgdisk -n 4:0:256M -c 4:bootfs /dev/sdX # 分区5rootfs 剩余空间 sudo sgdisk -n 5:0:0 -c 5:rootfs /dev/sdX分区创建完成后写入 FSBL 和 FIPsudo dd iftf-a-stm32mp257f-ev1.stm32 of/dev/sdX1 bs512 convfdatasync sudo dd iftf-a-stm32mp257f-ev1.stm32 of/dev/sdX2 bs512 convfdatasync sudo dd iffip-stm32mp257f-ev1.bin of/dev/sdX3 bs512 convfdatasync然后是 bootfs 和 rootfs 的格式化与内容拷贝sudo mkfs.ext4 -L bootfs /dev/sdX4 sudo mkfs.ext4 -L rootfs /dev/sdX5 sudo mkdir -p /mnt/bootfs /mnt/rootfs sudo mount /dev/sdX4 /mnt/bootfs sudo mount /dev/sdX5 /mnt/rootfs # 把构建输出里的 bootfs 内容原样拷进去 sudo tar xf st-image-bootfs-*.tar.gz -C /mnt/bootfs # 把 rootfs 内容原样拷进去 sudo tar xf st-image-weston-*.tar.xz -C /mnt/rootfs拷贝完成后检查 bootfs 里的目录结构是否包含 extlinux 配置文件find /mnt/bootfs -name extlinux.conf如果文件不存在说明 bootfs 内容没有正确解包需要回到 Yocto 构建目录重新确认st-image-bootfs目标是否完整。4.3 验证启动和排查 extlinux 加载错误完成重新分区、重新烧写后上电再看串口日志。这次 U-Boot 的输出应该变成switch to partitions #0, OK mmc0 is current device Scanning mmc 0:4... Found /boot/extlinux/extlinux.conf Loading /boot/extlinux/extlinux.conf如果 extlinux.conf 能正常加载接下来就是内核启动、挂载 rootfs。看到VFS: Mounted root (ext4 filesystem)的日志基本就说明 SD 卡启动链路已经通了。如果你在修复后仍然提示找不到 extlinux.conf可以手动进入 U-Boot 命令行用ls mmc 0:4 /看看 bootfs 分区下到底有哪些文件。如果文件确实存在但 U-Boot 没有自动加载检查 U-Boot 环境变量里的boot_targets和distro_bootcmd有可能被之前烧写的旧环境变量污染了。在执行saveenv之前可以先用env default -a恢复默认环境变量再saveenv。5. 比修复更重要几个容易踩的构建与烧写配置5.1 版本锁定别让 TF-A、U-Boot、Kernel 各玩各的distribution package 里有个很重要的设计everything is locked。Yocto 会通过 manifest 锁死每个组件的 commit 和版本。我在这次排障时一度怀疑是 U-Boot 的问题想手动拉一个最新主线的 U-Boot 替换掉 FIP 里的 U-Boot结果被同事拦住了。原因很简单ST 在 6.6.116 上发布的 TF-A、U-Boot、内核以及设备树是有配套关系的手动替换某一个很容易出现 DTB 和内核不匹配、外设驱动 probe 失败甚至 BL2 验签失败的问题。如果你是刚接触 STM32MP2 的新手建议始终使用repo init -u https://github.com/STMicroelectronics/...指定的 manifest 分支来拉取代码不要自作聪明地替换组件。即使你操作很熟练也要确保替换后能提供完整的验证记录。多套版本并存的组合排障成本会成倍上升。5.2 烧写前先把 SD 卡痕迹清干净SD 卡是一个很容易被忽略的变量。我见过太多“反复烧写失败”的情况最后发现是卡里残留了旧的 GPT 分区、MBR 引导扇区或者其他签名导致 BootROM 或 U-Boot 识别混乱。手动格式化不能完全清除这些痕迹必须先wipefs -a把所有文件系统签名和分区表擦掉再重新分区。另外SD 卡的品牌和等级也值得注意。STM32MP257F-EV1 的 SDMMC 控制器对低速卡兼容性不太好有些杂牌卡虽然容量正确但上电后初始化时序不稳定BootROM 会直接放弃。我实际测试下来SanDisk Extreme、Kingston Canvas 这类标称 UHS-I 的卡比较稳妥。如果已经烧了好几次还是无输出可以换一张卡试试这个成本很低却常常能省下半天时间。5.3 串口没输出时的自救清单如果你遇到“完全没有任何输出”的场景按下面这个顺序排查通常能在十分钟内定位确认串口线接在 ST-LINK 虚拟串口上且驱动正常Linux 下能看到/dev/ttyACM0或/dev/ttyUSB0。确认串口终端设置为 115200、8N1没有启用硬件流控。确认板子供电正常EV1 板供电跳线选择正确电流表是否出现异常大电流。确认拨码开关处于 SD 卡启动模式并且换了 SD 卡后重新上电而不是在系统运行时热插拔。如果以上都没问题把 SD 卡里的 FSBL 检查一遍确认它是用当前板子配置文件构建出来的而不是另一个 board 的镜像。这份清单我打印出来贴在了工作台旁边。嵌入式调试最大的敌人不是技术难而是“你以为你知道了其实有一个变量没确认”。SD 卡方向、拨码状态、串口波特率这三件小事看起来太基础却最容易在紧张排障时被忽略。最后再分享一个实用小习惯不管是 build 出来的.wic整卡镜像还是手动分区用的那些二进制文件我都会单独建一个目录把构建时间、构建机环境、distribution package 版本号、镜像 SHA256 全部写进一个BUILD_INFO.txt。真到排障的时候看一眼这个文件就能立刻确认自己烧的哪一版东西。这个习惯在这次 STM32MP257F-EV1 的问题里帮了我大忙当我发现自己用的还是 6.1 时代的烧写脚本时一切不合理都有了答案。如果你也被这类启动问题卡住先别急着改代码从头梳理一遍烧写链路大概率比重新编译更能解决问题。