ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32MP135 eMMC启动内核Panic后的恢复实战

STM32MP135 eMMC启动内核Panic后的恢复实战 1. 项目背景与问题现象还原1.1 这是一个什么项目STM32MP135DAF是ST推出的单核Cortex-A7处理器主频最高1GHz定位在入门级工业MPU。相比STM32MP151/153这些双核兄弟MP135砍掉了Cortex-M4协处理器但保留了完整的多媒体外设、千兆以太网、CAN-FD、LCD控制器等性价比很高我看中它主要是因为单核A7足够跑轻量级Linux业务叠加ST长期供货承诺做中小型边缘计算网关和工业HMI很合适。我这次要做的是基于这颗芯片设计一块定制核心板板载eMMC作为唯一启动介质系统镜像用Yocto构建machine层完全从ST官方stm32mp1派生做了大量裁剪和定制。项目整体不复杂但在eMMC启动环节踩了一个很深很典型的坑内核PANIC之后CubeProgrammer彻底无法重连目标设备。这个问题在ST社区里反复出现但大部分回复都停留在“按住复位键重试”或者“检查USB线”这种浅层建议。实际上这里的连不上不是线缆问题而是启动链路的失联需要用一套系统性的方法去恢复。这篇文章就把我的整个排查过程、恢复步骤和背后的原理完整记录下来。1.2 现场现象从PANIC到失联的完整过程先说现象方便各位对照是不是同款问题。我通过CubeProgrammer把Yocto镜像烧进了eMMC包含FSBLTrusted Firmware-A、U-Boot、内核、设备树和rootfs。第一轮启动很顺U-Boot起来之后把内核拉起来但内核在挂载rootfs时直接崩了串口打印了经典的内核PANIC[ 2.315846] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(179,2) ]--- [ 2.325531] ---[ end trace 0000000000000000 ]---看到这行日志我的第一反应是rootfs分区格式或root参数不对这是常规排查路径也没太担心。但等我拔掉串口、想通过USB重新用CubeProgrammer烧一版修正后的镜像时发现工具一直报No STM32 device in DFU mode connected重新插拔USB、换了线缆、换电脑端口全部无效目标板像是彻底死透了。这里要特别强调一点这个PANIC本身很好解决难的是PANIC之后CubeProgrammer为什么不认设备。两块问题交织在一起才是这个项目的真正挑战。1.3 为什么会选择eMMC启动简单交代一下方案背景。STM32MP135支持从SD卡、eMMC、NAND、NOR Flash等多种介质启动但我最终选了eMMC原因有三个容量与成本平衡eMMC 8GB版本在工业温度范围内成本完全可以接受且容量足够放双rootfs做A/B升级。可靠性eMMC内置坏块管理、擦写均衡对比裸NAND软件栈省掉了一大堆MTD处理逻辑。启动速度eMMC的随机读性能远超SD卡Linux系统启动时间能控制在3秒以内。ST官方评估板的默认启动配置是SD卡EMMC交叉配合SD卡启动用于开发调试、eMMC用于固化量产。我是直接把SD卡槽砍掉了所有启动内容和rootfs全部塞进eMMC。这个做法对量产很友好但给调试恢复带来的麻烦就是这篇文章要展开的核心内容。2. 为什么eMMC启动失败会让CubeProgrammer失联2.1 先搞懂STM32MP13的启动链路要理解“为什么连不上”得先把STM32MP13的BootROM启动流程讲透。芯片上电后运行在芯片内部ROM中的一段固化代码BootROM会先读取OTPOne-Time Programmable区域中的配置再根据BOOT引脚的当前电平状态决定从哪个外设加载FSBLFirst Stage Boot Loader。在STM32MP13系列上FSBL就是Trusted Firmware-ATF-A。BootROM把TF-A从启动介质中读取到内部SRAM并跳转执行TF-A随后初始化DDR把U-BootSSBLSecond Stage Boot Loader加载到DDR中运行U-Boot再加载内核和设备树最终挂载rootfs进入Linux。这整条链路中每一级都有独立的加载地址、数据格式和校验方式。任何一个环节异常都不会继续往下走。特别注意BootROM是所有启动方式的根只要BootROM能运行、引脚配置没被锁死芯片就永远有救。2.2 Boot引脚与启动源的关系STM32MP135有三根BOOT引脚BOOT0、BOOT1、BOOT2。它们在上电复位时被采样组合出不同的启动源。对于我这种去掉SD卡的板子最关键的三种组合是BOOT2BOOT1BOOT0启动源010eMMC串行接口SDMMC2011eMMC串行接口SDMMC2备用001USB DFU用于烧录和恢复开发板通常用拨码开关或跳线帽切换这三个引脚方便在“正常启动”和“USB烧录”之间切换。我在自己的核心板上用了0欧电阻选焊的方式拨码开关太占空间但调试期这么做其实有点激进——这个决定为后面CubeProgrammer无法重连埋下了伏笔。2.3 CubeProgrammer的“救命通道”为什么断了CubeProgrammer通过USB连接芯片的原理是这样的目标板上电后如果启动引脚配置为USB DFU模式BootROM会枚举出一个DFU USB设备此时CubeProgrammer通过USB DFU协议与BootROM通信读写Flash、执行烧录。这里最关键的认知是BootROM就是最终兜底的恢复通道。CubeProgrammer能连上本质就是BootROM在等你发指令。那为什么会失联答案是BootROM根本没能进入USB DFU模式。我的板子引脚固定在eMMC启动模式BootROM上电后永远先去eMMC读取TF-A不会理会USB接口。正常情况下这没问题因为eMMC里烧了好的固件启动链路跑得通。但现在eMMC里的固件损坏了——更确切地说eMMC中TF-A或U-Boot区域的数据本身并没有坏而是后续环节出了问题启动永远走不完。BootROM每次复位后都去eMMC读、执行、失败、再复位、再尝试陷入一个死循环根本轮不到USB DFU。这不是芯片锁死了也不是eMMC物理损坏了纯粹是启动链条卡死在错误状态。明白这一点恢复思路就清晰了要么让BootROM去USB DFU模式要么让eMMC里的固件恢复可启动状态。两条路总得走一条。3. PANIC根因排查不是所有内核崩溃都一个样3.1 先从PANIC日志说起内核崩溃Kernel Panic是Linux内核遇到无法恢复的错误时主动停机的一种保护机制。串口那行VFS: Unable to mount root fs on unknown-block(179,2)信息量很大我来拆解一下。unknown-block(179,2)表示内核试图挂载一个它不认识的块设备。设备号机制里主设备号179代表eMMC设备驱动次设备号2代表/dev/mmcblk0p2。也就是说内核知道有个eMMC也识别出了分区2但挂载失败。这种“认识但挂不上”的情况最直接的原因通常有三个rootfs分区本身没有格式化为内核支持的文件系统。内核配置里缺少该文件系统的驱动比如没编入ext4。设备树给eMMC的配置不对导致分区表读取异常。3.2 第一类根因rootfs挂载失败我在Yocto里用的是ext4格式的rootfs。检查内核配置时发现了一个典型的坑用ST官方stm32mp1机器配置做基线时内核默认把ext4编成了模块CONFIG_EXT4_FSm。模块形式的文件系统驱动在rootfs还没挂载时根本无法加载——鸡生蛋问题内核需要从rootfs加载ext4模块去挂载rootfs。这种问题在SD卡启动时一般不明显因为ST的默认initramfs会处理模块加载。但我裁剪了initramfs走的是直接挂载rootfs的路径模块驱动就彻底失效了。解决方法是把关键驱动直接编译进内核CONFIG_EXT4_FSy CONFIG_EXT4_USE_FOR_EXT2y改完配置重新编译内核挂载问题就消失了。3.3 第二类根因设备树eMMC节点配置错误我在做自定义机器配置时基于官方stm32mp135f-dk.dts修改了自己的设备树。这中间有个容易出错的地方eMMC和SD卡在STM32MP13上通常共用SDMMC2控制器但eMMC需要额外打开8线模式。如果设备树里只配了4线SD模式或者non-removable属性没设置Linux的MMC子系统可能会把eMMC当成可移动设备处理导致分区扫描异常。我的设备树里相关部分最终是这样的sdmmc2 { pinctrl-names default, opendrain, sleep; pinctrl-0 sdmmc2_b4_pins_a sdmmc2_d47_pins_a; pinctrl-1 sdmmc2_b4_od_pins_a sdmmc2_d47_pins_a; pinctrl-2 sdmmc2_b4_sleep_pins_a sdmmc2_d47_sleep_pins_a; non-removable; no-sd; no-sdio; st,neg-edge; bus-width 8; vmmc-supply v3v3; vqmmc-supply vddflash; status okay; };关键属性逐个说non-removable告诉内核这是焊死的设备不是插拔卡避免触发热插拔检测逻辑。no-sd和no-sdio强制控制器走eMMC协议避免启动时去探测SD和SDIO设备浪费事件。bus-width 8eMMC跑8线高速模式这是eMMC性能的关键。vqmmc-supply配置信号电压域。eMMC在HS200/HS400等高速模式下需要1.8V信号电压这个供电必须正确。如果这些属性有遗漏eMMC初始化可能不稳定轻则速度跑不上去重则分区读到一半失败直接PANIC。3.4 第三类根因U-Boot环境变量与启动参数错误U-Boot传递启动参数给内核时bootargs环境变量是核心。我的Yocto机器配置里写过一段带默认值的环境变量但U-Boot实际运行时eMMC里的环境变量分区残留了旧值覆盖了编译时写入的默认值。现场查到的bootargs如下root/dev/mmcblk0p2 rootwait rw看上去没问题但问题出在U-Boot的mmc设备编号上。U-Boot里SDMMC1和SDMMC2的编号顺序、以及实际板子上eMMC挂在哪一路控制器必须保持一致。如果U-Boot认为eMMC在mmc 1而内核设备树里对应sdmmc2设备映射错位内核就会用错误的root设备参数去挂载。排查这类问题时可以在U-Boot命令行手动确认mmc list输出的控制器列表应当与设备树一一对应。再看mmc dev 1 ls mmc 1:2如果能列出rootfs分区内容说明U-Boot这边完全正常问题只在bootargs传递环节。4. 恢复流程把半砖状态的板子救回来4.1 先确认板子还活着排查完内核PANIC原因理论上我只需要重新编译、重新烧录就能解决。但CubeProgrammer连不上所有常规烧录路径都断了。恢复操作开始前务必先做三件事串口连接是否正常串口能看到BootROM、TF-A或U-Boot的输出说明芯片核心、电源、时钟都活着。测量关键电源域特别是eMMC的VCC和VCCQ电压。如果eMMC供电异常BootROM读取永远失败但不是芯片死了。USB线缆和接口排查换主机、换线、直接插主板后置USB口排除最基础的物理故障。这里多说一句我始终强调串口的重要性。很多工程师在开发阶段挂个串口只是为了看日志但在恢复阶段串口是判断板子生死的唯一标准。一个能输出字符的串口意味着芯片在运行问题一定有解。4.2 强制进入Engineering模式针对我的引脚固定问题恢复思路是让BootROM绕过eMMC启动强制进入USB DFU。STM32MP135的参考手册里有一种方式在TF-A和U-Boot中启用Engineering Mode运行时可以通过USB强制引导到DFU。但这里有个逻辑陷阱如果eMMC里的TF-A或U-Boot本身已经损坏到无法运行Engineering Mode根本不会被执行。我的情况是TF-A和U-Boot能运行坏的只是后续内核环节所以理论可行但我不想赌还是走最粗暴但最可靠的路改引脚电平。如果板子的BOOT引脚有跳线或拨码直接拨到USB DFU组合我的板子上是BOOT20, BOOT10, BOOT01上电后CubeProgrammer就能重新发现设备。如果没有物理开关那就需要用电线短接PCB上的焊盘或割线改造这取决于你的板子设计。我自己在做板子时预留了0欧电阻位置焊下来、换一个方向的电阻就能强制进入DFU模式。操作顺序板子完全断电。修改BOOT引脚组合为USB DFU模式。连接USB线到目标板的USB-OTG口注意一定是连接到芯片USB-OTG外设的那个口不是USB Host口。给板子上电观察主机的设备管理器或dmesg应当出现未知设备或DFU设备。打开CubeProgrammer选择USB模式点击连接。等待CubeProgrammer界面上出现芯片信息一切就恢复了。4.3 清除eMMC中的误导痕迹连上CubeProgrammer后按捺住直接烧新镜像的冲动。首先要做的是把闪存里导致反复启动失败的遗留数据清理干净。在CubeProgrammer的存储器显示界面里eMMC会以扇区形式呈现。启动引导数据通常存放在eMMC的前几个MB区域包含TF-A、U-Boot和环境变量分区。我的做法是先不用整片擦除只把前4MB区域清零0x00000000 0x00400000 0x00这个操作会把eMMC的bootloader区域全部写入0x00。执行后eMMC里不再有可启动的代码BootROM读取eMMC会失败但这不是坏事——失败之后BootROM会等待USB DFU指令芯片进入纯粹的“烧录等待”状态后续操作非常安全。此时再做一次断电重启确认CubeProgrammer依然能连接。如果能连接说明芯片和Flash的物理链路完全健康之前只是启动逻辑死锁。如果不能连接再检查USB PHY和时钟配置但概率极低。4.4 恢复烧录并验证启动链路清理干净后把修正过的镜像完整烧录进去。我习惯用CubeProgrammer的ExternalLoader配合分区表文件操作。对于eMMC不依赖外部loader流程如下在CubeProgrammer中选择eMMC存储介质。加载分区表文件通常是flashlayout.tsv。按分区表顺序烧录FSBL、U-Boot、环境变量、内核、设备树和rootfs。烧录完成后断电把BOOT引脚恢复到eMMC启动模式。重新上电串口观察启动流程。烧录时有个细节STM32MP13的eMMC启动分为boot partitions和user area。TF-A和U-Boot必须烧录到eMMC的boot1分区或位于用户分区起始位置的FSBL区。具体使用哪种方式取决于你在烧写工具中定义的分区表。如果TF-A烧错了区域BootROM同样找不到可执行代码。用CubeProgrammer时Binary类型为FIP_MMC的件默认会写入boot1分区千万别把FIP文件当普通分区镜像烧到user area。boot加载过程中应该完整看到NOTICE: BL2: v2.8-stm32mp1-r1 NOTICE: BL2: Built : 11:36:10, May 10 2024 NOTICE: BL2: Booting BL32 NOTICE: BL2: Booting BL33最终串口进入Linux登录提示符整个恢复流程才算是真正走完。5. 常见问题速查与避坑记录5.1 问题速查表我把整个排查过程中遇到的和预判同行会遇到的问题整理成一张表方便快速对照现象直接原因恢复/规避方法内核PANICVFS: Unable to mount root fsrootfs文件系统驱动未编入内核、root参数错误检查CONFIG_EXT4_FSy、确认root/dev/mmcblk0p2内核PANIC卡在Waiting for root deviceeMMC控制器初始化失败、设备树节点错误核对bus-width、non-removable、vqmmcU-Boot能启动但内核完全跑不起来设备树与bootargs不匹配串口在U-Boot下执行printenv bootargs确认参数CubeProgrammer报No DFU device启动引脚不在USB DFU模式、BootROM死循环强制BOOT引脚为DFU组合清除eMMC前4MBCubeProgrammer能连接但烧录失败eMMC分区表错误、FIP文件位置不对核对flashlayout.tsv确保FSBL写到boot1分区板子断电重启后仍无法从eMMC启动eMMC初始化时序不稳、供电不足检查eMMC的VCC上电时序必要时加延时5.2 几条用板子换来的经验第一开发板要保留启动介质切换能力。不管量产方案是什么开发阶段的板子一定要留BOOT引脚切换手段。引脚物理上可以焊接选择但设计上务必留有测试点或0欧电阻位置。我这次恢复之所以费劲核心原因就是在引脚设计上太激进没有给自己留后路。第二Yocto定制machine时内核关键驱动尽量直接编入内核而非模块。根文件系统所在的存储介质驱动、文件系统驱动、块设备驱动都应该是y而不是m。这个原则对所有嵌入式Linux项目都成立不仅限于STM32MP1。第三任何一次eMMC烧录都要先想好“烧坏了怎么还原”。在烧录之前先用CubeProgrammer做一个整片eMMC的备份Read功能或者至少备份前16MB。别嫌麻烦一块板子等救援的时间成本远超备份操作花掉的几分钟。6. 给同样在搞自定义machine的同行一些建议6.1 自定义machine的底线检查基于ST官方机器配置做定制时有几个地方必须逐项确认否则很容易出现我遇到的这类问题MACHINE配置中的PREFERRED_PROVIDER_virtual/kernel是否明确。内核配置片段*.cfg中是否包含rootfs驱动、eMMC驱动、USB DFU相关配置。U-Boot配置中定义了哪些默认环境变量bootcmd是否适配eMMC启动。设备树是否引用了正确的eMMC控制器节点pinctrl引脚是否与硬件原理图一致。Yocto的可复现性很强但定制越深、坑越隐秘。我的做法是建立一份自己的checklist每次修改machine配置都从头到尾过一遍启动链路。6.2 建议的调试工具链除了标配的串口和CubeProgrammer我强烈建议准备以下工具逻辑分析仪或示波器排查eMMC时钟和CMD/DATA线时序问题时这比瞎猜快得多。USB分析工具对比正常板子和异常板子枚举DFU设备的差异性。多通道供电模块确认eMMC上电瞬间的电压跌落很多诡异问题其实是供电不足。SD卡槽转接板就算量产不用SD卡调试期保留一个SD卡启动选项能大幅降低恢复难度。6.3 后续扩展思路这块板子最终是要进入量产的eMMC启动方案还需要完善几个方向基于U-Boot的冗余启动设计TF-A和U-Boot做双备份这样即使一次升级失败备份分区还能兜底增加OTA升级机制在rootfs中集成更新脚本配合U-Boot的distro_bootcmd实现A/B分区切换还有就是量产阶段的烧录工艺用ST提供的OTA烧录工具或自制产测脚本替代手工CubeProgrammer烧录效率和防错能力完全不同。这些扩展方向的核心思路是一致的把恢复能力前置到设计阶段而不是等设备出问题后再想办法。开发者板子可以随便折腾但产品不行。回到这次的问题本身内核PANIC只是表面现象真正的教训在于启动链路的设计必须保留“逃生通道”。CubeProgrammer连不上不可怕可怕的是没有预案。希望这篇记录能给正在做STM32MP1系列BSP定制和eMMC启动方案的朋友一些参考少走一点弯路。
返回列表