硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1009|回复: 8
收起左侧

[问题解答] H7-Tool 使用硬件黑盒子lua脚本查找进入hardfault 异常处理

[复制链接]

5

主题

30

回帖

45

积分

新手上路

积分
45
发表于 2022-9-17 11:10:12 | 显示全部楼层 |阅读模式
硬汉哥,我的平台是:STM32F407.
开发环境:keil MDK
现象:产品老化了大概7多天的样子,出现进入hardfault 。(有2台在老化,目前一台机器出现)。
刚好前几天买了H7-Tool 工具,于是使用 H7-Tool硬件黑盒子的lua脚本捕获到hardfault 异常时的记录如下:
下载成功============================================================
=========寄存器值读取========================================
============================================================
R0 = 20000034
R1 = 20000034
R2 = 20003cbc
R3 = 00000020
R4 = 00008fec
R5 = 20020000
R6 = 20003cbc
R7 = 00000029
R8 = 0001c344
R9 = ffffffff
R10 = 00006dea
R11 = 00000000
R12 = 00000000
R13(SP) = 200135c0
R14(LR) = fffffff1
R15(PC) = 0800748a
xPSR = 61000003


------------------------------------------------------------------
Read System Handler Control and State Register SHCSR = 0x00000000
------------------------------------------------------------------
MEMFAULTACT = 0
BUSFAULTACT = 0
USGFAULTACT = 0
SVCALLACT = 0
MONITORACT = 0
PENDSVACT = 0
SYSTICKACT = 0
USGFAULTPENDED = 0
MEMFAULTPENDED = 0
BUSFAULTPENDED = 0
SVCALLPENDED = 0
MEMFAULTENA = 0
BUSFAULTENA = 0
USGFAULTENA = 0


------------------------------------------------------------------
Read HardFault Status Register HSFR Register = 0x40000000
------------------------------------------------------------------
VECTBL = 0, 中断向量表无Bus Fault
FORCED = 1, forced Hard Fault
            Indicates a forced Hard Fault, generated by escalation of a fault with configurable
            priority that cannot be handled, either because of priority or because it is disabled
            When this bit is set, the Hard Fault handler must read the other fault status registers
            to find the cause of the fault


------------------------------------------------------------------
MemManage Status Register (MMFSR) = 0x00
------------------------------------------------------------------
IACCVIOL = 0, 无指令访问冲突错误
DACCVIOL = 0,  无数据访问异常
MUNSTKERR = 0,  出栈正常
MSTKERR = 0,  入栈正常
MLSPERR = 0,  浮点lazy stacking特性保存期间未发生故障
MMARVALID = 0,  SCB->MMFAR寄存器没有记录异常地址


------------------------------------------------------------------
MemManage Address Register (MMFAR) = 0x20020000
------------------------------------------------------------------
Data address for a MemManage fault. This register is updated with the address of a location
that produced a MemManage fault. The MMFSR shows the cause of the fault. This field is valid
only when MMFSR.MMARVALID is set. In implementations without unique BFAR and MMFAR
registers, the value of this register is UNKNOWN if BFSR.BFARVALID is set


------------------------------------------------------------------
BusFault Status Register (BFSR) = 0x82
------------------------------------------------------------------
IBUSERR = 0, 指令总线正常
PRECISERR = 1, 精确的数据总线访问异常
            a data bus error has occurred, and the PC value stacked for the exception return points to
            the instruction that caused the fault.
            When the processor sets this bit, it writes the faulting address to BFAR.
IMPRECISERR = 0, 数据总线正常
UNSTKERR = 0, 中断出栈时正常
STKERR = 0, 中断入栈时正常
LSPERR = 0, 浮点lazy stacking特性保存期间未生故障
BFARVALID = 1, BFAR寄存器记录有效的异常地址
            The processor sets this bit after a BusFault where the address is known. Other faults can set this
            bit to 0, such as a MemManage fault occurring later. If a BusFault occurs and is escalated to a
            HardFault because of priority, the HardFault handler must set this bit to 0. This prevents
            problems if returning to a stacked active BusFault handler who is BFAR value has been
            overwritten.


------------------------------------------------------------------
BusFault Address Register (BFAR) = 0x20020000
------------------------------------------------------------------
Data address for a precise BusFault. This register is updated with the address of a location that
produced a BusFault. The BFSR shows the reason for the fault. This field is valid only when
BFSR.BFARVALID is set. In implementations without unique BFAR and MMFAR registers, the
value of this register is UNKNOWN if MMFSR.MMARVALID is set


------------------------------------------------------------------
UsageFault Status Register (UFSR) = 0x0000
------------------------------------------------------------------
UNDEFINSTR = 0, 处理器访问指令正常
INVSTATE = 0, 没有无效状态
INVPC = 0, PC加载正常
NOCP = 0, 访问协处理正常
UNALIGNED = 0, 内存对齐访问正常
DIVBYZERO = 0,  无除数为0的异常, 或者没有使能除数为0的异常


============================================================
=========异常进一步分析======================================
============================================================
一直使用MSP, 返回Handler模式, 进入中断前没有使用硬件浮点单元
进入硬件异常前, 寄存器数值, 如果出现非精确异常, 这些值是不准确的:
R0 = 00008f81
R1 = 20012b28
R2 = 20003cbc
R3 = 00000020
R12 = 00000000
LR = 0800bb77
PC = 0800d4c8
PSR = 01000035

首先查找 R15(PC) = 0800748a ,然后查找此处的PC指针指向的代码:
image.png
刚好是hardfault里面的代码。

然后查找hardfault异常前的代码,PC = 0800d4c8 的代码:
image.png
该代码是用于CRC校验用的,用的是 网上开源的CRC库【https://www.lammertbies.nl/comm/info/crc-calculation
代码如下:
[C] 纯文本查看 复制代码
static void             init_crc16_tab( void );

static bool             crc_tab16_init          = false;
static uint16_t         crc_tab16[256];

/*
 * uint16_t crc_16( const unsigned char *input_str, size_t num_bytes );
 *
 * The function crc_16() calculates the 16 bits CRC16 in one pass for a byte
 * string of which the beginning has been passed to the function. The number of
 * bytes to check is also a parameter. The number of the bytes in the string is
 * limited by the constant SIZE_MAX.
 */

uint16_t crc_16( const unsigned char *input_str, size_t num_bytes ) {

	uint16_t crc;
	uint16_t tmp;
	uint16_t short_c;
	const unsigned char *ptr;
	size_t a;

	if ( ! crc_tab16_init ) init_crc16_tab();

	crc = CRC_START_16;
	ptr = input_str;

	if ( ptr != NULL ) for (a=0; a<num_bytes; a++) {

		short_c = 0x00ff & (uint16_t) *ptr;
		tmp     =  crc       ^ short_c;
		crc     = (crc >> 8) ^ crc_tab16[ tmp & 0xff ];

		ptr++;
	}

	return crc;

}  /* crc_16 */


感觉这里没什么毛病啊。而且也用了这么久。。并且当时我的上位机应该是没有开启的,也就是不会进行232通信,而CRC校验是必须串口格式帧接收正常以后才会进入判断。因此感觉好奇怪。
完全没头绪。特此来求教硬汉哥。













回复

使用道具 举报

5

主题

97

回帖

112

积分

初级会员

积分
112
发表于 2022-9-17 13:41:28 | 显示全部楼层
我之前遇到过一次CRC校验异常的,是串口接收干扰,接收一个或两个字节。进行CRC校验就会出现这个情况。不知道你这个是不是这个情况。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107049
QQ
发表于 2022-9-17 17:24:31 | 显示全部楼层
从异常错误来的,这个是个精确的数据总线访问异常,不出意外确实是CRC16有点问题。
楼主这个是Modbus CRC16吗,可以试试我们这个

[C] 纯文本查看 复制代码
    // CRC 高位字节值表
static const uint8_t s_CRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
} ;
// CRC 低位字节值表
const uint8_t s_CRCLo[] = {
	0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
	0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
	0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
	0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
	0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
	0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
	0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
	0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
	0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
	0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
	0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
	0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
	0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
	0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
	0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
	0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
	0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
	0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
	0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
	0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
	0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
	0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
	0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
	0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
	0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
	0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};

/*
*********************************************************************************************************
*	函 数 名: CRC16_Modbus
*	功能说明: 计算CRC。 用于Modbus协议。
*	形    参: _pBuf : 参与校验的数据
*			  _usLen : 数据长度
*	返 回 值: 16位整数值。 对于Modbus ,此结果高字节先传送,低字节后传送。
*
*   所有可能的CRC值都被预装在两个数组当中,当计算报文内容时可以简单的索引即可;
*   一个数组包含有16位CRC域的所有256个可能的高位字节,另一个数组含有低位字节的值;
*   这种索引访问CRC的方式提供了比对报文缓冲区的每一个新字符都计算新的CRC更快的方法;
*
*  注意:此程序内部执行高/低CRC字节的交换。此函数返回的是已经经过交换的CRC值;也就是说,该函数的返回值可以直接放置
*        于报文用于发送;
*********************************************************************************************************
*/
uint16_t CRC16_Modbus(uint8_t *_pBuf, uint16_t _usLen)
{
	uint8_t ucCRCHi = 0xFF; /* 高CRC字节初始化 */
	uint8_t ucCRCLo = 0xFF; /* 低CRC 字节初始化 */
	uint16_t usIndex;  /* CRC循环中的索引 */

    while (_usLen--)
    {
		usIndex = ucCRCHi ^ *_pBuf++; /* 计算CRC */
		ucCRCHi = ucCRCLo ^ s_CRCHi[usIndex];
		ucCRCLo = s_CRCLo[usIndex];
    }
    return ((uint16_t)ucCRCHi << 8 | ucCRCLo);
}



回复

使用道具 举报

5

主题

30

回帖

45

积分

新手上路

积分
45
 楼主| 发表于 2022-9-17 17:48:45 | 显示全部楼层
是的,是Modbus CRC16 ,我试试你这个运行一下看下,主要是这个东西,运行个7天复现,有点难搞。。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107049
QQ
发表于 2022-9-17 17:52:41 | 显示全部楼层
xdh873939316 发表于 2022-9-17 17:48
是的,是Modbus CRC16 ,我试试你这个运行一下看下,主要是这个东西,运行个7天复现,有点难搞。。

多开几个机子,我一般是开5个机子做测试。
回复

使用道具 举报

747

主题

1049

回帖

3295

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3295
发表于 2022-9-17 22:09:26 | 显示全部楼层
RS232通信并未启动。估计是某个函数内缓冲区溢出,覆盖了堆栈中保存的函数返回地址,错误的地址指向CRC校验函数中间,访问未被初始化的ptr指针变量,导致内存访问异常。
回复

使用道具 举报

5

主题

30

回帖

45

积分

新手上路

积分
45
 楼主| 发表于 2022-9-19 13:52:25 | 显示全部楼层
armfly 发表于 2022-9-17 22:09
RS232通信并未启动。估计是某个函数内缓冲区溢出,覆盖了堆栈中保存的函数返回地址,错误的地址指向CRC校验 ...

大佬,如果按照你说的这种情况。我应该怎么排查处理。现在机器在老化了。可以排查看下其他问题。。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107049
QQ
发表于 2022-9-20 04:01:21 | 显示全部楼层
xdh873939316 发表于 2022-9-19 13:52
大佬,如果按照你说的这种情况。我应该怎么排查处理。现在机器在老化了。可以排查看下其他问题。。

如果CRC没问题,看看有办法检测下input_str和num_bytes有效性没。
回复

使用道具 举报

5

主题

30

回帖

45

积分

新手上路

积分
45
 楼主| 发表于 2022-9-20 11:19:43 | 显示全部楼层
eric2013 发表于 2022-9-20 04:01
如果CRC没问题,看看有办法检测下input_str和num_bytes有效性没。

我的,感谢硬汉哥,我在crc中添加 input_str 指针指向的地址判断和 num_bytes 的大小判断。我在试试效果。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-16 02:18 , Processed in 0.261961 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表