主芯片:STM32F407ZG
产品软件架构:
- bootloader + upgrader / application (upgrader 是一个系统升级器,升级系统专用)
- 在bootloader模式下可以加载upgrader或application并启动
- 在upgrader模式下可以更新bootloader和整个application.
- upgrader和appplication同时只存在一个,由bootloader从文件系统载入STM32F407ZG应用区.
问题描述:两台故障产品在使用过程中意外死机,无法开机,分别编号为1号和2号。
故障机分析处理:
1号已经被重刷程序后修复,故障无法再复现,怀疑是STM32 flash程序区被改,所以首先分析了操作flash的代码,经查擦写flash只发生在成功开机进入系统或者在"系统升级"选项中进行操作时,其他使用过程中不涉及flash擦写。同时编写程序,每隔1秒擦除flash一次,一共执行2万次擦写, 在擦写期间进行各种操作,均无发现死机现象。
2号机拆机检查: 系统上电,STM32 3V3电压正常,但串口无法检测到系统log输出,无法正常开机。
尝试用Jlink Command测试:
撤掉USB和电池供电,飞线连接3V3,SWDIO,SWCLK,GND至JLink Command
执行connect指令,成功连接STM32F407ZG主控
执行halt停机指令,成功停机
J-Link>halt
PC = 08000294, CycleCnt = 00000000
R0 = 00000000, R1 = 00000000, R2 = 00000000, R3 = 00000000
R4 = 00000000, R5 = 00000000, R6 = 00000000, R7 = 00000000
R8 = 00000000, R9 = 00000000, R10= 00000000, R11= 00000000
R12= 00000000
SP(R13)= 20013378, MSP= 20013378, PSP= 00000000, R14(LR) = FFFFFFFF
XPSR = 01000000: APSR = nzcvq, EPSR = 01000000, IPSR = 000 (NoException)
CFBP = 00000000, CONTROL = 00, FAULTMASK = 00, BASEPRI = 00, PRIMASK = 00
FPU regs: FPU not enabled / not implemented on connected CPU.
执行go全速运行,指令响应OK,但是产品仍无法成功进开机流程,再次执行halt指令:
J-Link>halt
PC = 0800022E, CycleCnt = 06EA7AC4
R0 = 0805A76C, R1 = 20001E78, R2 = 00011500, R3 = 00000000
R4 = 00000000, R5 = 00000000, R6 = 00000000, R7 = 08059AAF
R8 = 00000000, R9 = 00000000, R10= 08059AD0, R11= 08059AD0
R12= 00000000
SP(R13)= 20013378, MSP= 20013378, PSP= 00000000, R14(LR) = 0800019F
XPSR = 21000000: APSR = nzCvq, EPSR = 01000000, IPSR = 000 (NoException)
CFBP = 00000000, CONTROL = 00, FAULTMASK = 00, BASEPRI = 00, PRIMASK = 00
FPS0 = 00000000, FPS1 = 00000000, FPS2 = 00000000, FPS3 = 00000000
FPS4 = 00000000, FPS5 = 00000000, FPS6 = 00000000, FPS7 = 00000000
FPS8 = 00000000, FPS9 = 00000000, FPS10= 00000000, FPS11= 00000000
FPS12= 00000000, FPS13= 00000000, FPS14= 00000000, FPS15= 00000000
FPS16= 00000000, FPS17= 00000000, FPS18= 00000000, FPS19= 00000000
FPS20= 00000000, FPS21= 00000000, FPS22= 00000000, FPS23= 00000000
FPS24= 00000000, FPS25= 00000000, FPS26= 00000000, FPS27= 00000000
FPS28= 00000000, FPS29= 00000000, FPS30= 00000000, FPS31= 00000000
FPSCR= 00000000
初步发现PC和MSP数值正常,但PC停留在代码很靠前的部分,怀疑是反复重启,并且是指在很小一段代码内反复执行,尝试用savebin指令dump芯片内部flash,成功读取1MB的flash数据,经过对flash数据的分析,发现如下信息:
1. app程序区起始地址为0x8080000, 关键字符串upgrader,确定app区域存储的是升级程序upgrader, 版本未定
2. bootloader区起始地址为0x8000000, 经匹配,bootloader程序为xx.x.x版本,为产品发布的第一个正式版本。其中bootloader偏移0x4000-0x8000一共16KB的数据被改写,解析偏移16KB前64字节发现刚好与系统保存的参数格式匹配,但部分参数无法解析
3. 根据上述信息得知upgrader程序是新版本,参数区使用了flash偏移0x4000-0x8000的区域,那么原来的参数在偏移0x60000出原因该会有保留。尝试读取解析:
5E 4F B5 63 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 0F 00 00 00 38 30 30 31 30 31 59 44 33 31 30 36 30 39 38 37 00 AA AA
解析得到SN号:xxxxxxxxxxxxxxxx,其中生产日期为xxxx.xx.xx,可知原来app区域运行的是xxxx.xx.xx版本,现在已被upgrader程序代替.
4. 根据第3步得到的版本信息,查找对应版本的upgrader文件,与芯片dump出来的upgrader完全匹配得到上述信息后,查看该upgrader的MSP地址(0-3)和程序入口地址(4-7)分别为: 0x2000EA, 0x808029d,使用setPC指令重置MCU PC指针,然后执行go指令,成功跳过bootloader直接开机运行upgrader代码,成功进入upgrader后,通过串口可以登录系统,查看文件系统和关键文件,可以佐证前面步骤的信息
经过上述分析得知: 不开机的原因是bootloader被非法改写。
根据flash镜像的分析推测故障发生的场景: 用户版本为xxxx.xx.xx,用户想要升级系统到xxxx.xx.xy版本,其中upgrader程序已经成功刷到了MCU的应用区,新版本的upgrader会将参数保存在0x4000处,此处会破坏旧版本bootloader, 但是后续没有升级成功,新版本的bootloader没有被写入,此时用户关机、断电、重启等操作将会导致系统完全变砖。联想到最近由于软件架构调整,各个模块的flash地址偏移发生了很大变化,xxxx.xx.xx升级到xxxx.xx.xy及以上版本时存在参数区地址调整,存在此问题,xxxx.xx.xy及以上系统升级不存在这个问题。
解决办法: 修改upgrader程序,在新版本的bootloader成功升级前,不可操作新的参数区,避免旧版本bootloader被非法改写
总结:
- 针对这种需要修改底层bootloader和系统更新的系统,还是需要完善的测试
- flash空间的划分一定要慎重,避免在中途调整,本案例就是由于前期bootloader和application的地址空间分配不合理,application空间过小,需要缩小bootloader的空间来增大application空间,涉及到底层程序的更新,这给对已量产的产品带来很大风险。
- 善于使用工具,jlink command是个好东西
- 遇事不慌,仔细分析,芯片一般不会有问题,不稳定的外围电路和软件bug带来问题的可能性更大