cortex-M55 hardfault实例分析

180 阅读2分钟

一. 问题

  1. 用whs_hal_test测试稳定复现一次hardfault

image.png

分析

  1. 死机直接原因

执行vldr.32 s0,[r2]

将r2寄存器内容读到s0

image.png

对应代码就是*(float*)offset

static sns_rc
sns_health_offload_set_metric_offset(sns_sensor *const this,
                                     sns_metric_tracker_t* metric_tracker,
                                     void const* offset,
                                     size_t offset_len)

offset参数是void const* offset. 传入的是float值

强转float* 取值,看起来很正常的调用,而且r2地址也是合法地址.

  1. 查看system_ns.per

image.png

发现有处错误,叫

image.png

Unaligned access error.

未对齐访问

offset地址在main heap中但是没有4byte对齐

修改sns main heap定义加上attribute((aligned(4)));

发现也是对不齐

pb_buffer_arg sensor_config = {NULL,0};
        pb_buffer_arg req_payload = {NULL,0};
        pb_buffer_arg data_payload = {NULL,0};
        sns_health_request offload_req =
        {
            .msg_id = _sns_health_request_type_MIN,
            .sensor_config_data = {
                .funcs.decode = pb_decode_string_cb,
                .arg = &sensor_config,
            },
            .req_payload = {
                .funcs.decode = pb_decode_string_cb,
                .arg = &req_payload,
            },
            .data = {
                .value_type = _sns_health_value_type_MIN,
                .data = {
                    .funcs.decode = pb_decode_string_cb,
                    .arg = &data_payload,
                },
            }
        };
        pb_simple_cb_arg arg =
        {
            .decoded_struct = &offload_req,
            .fields = sns_health_request_fields,
        };
        sns_std_request decoded_request = sns_std_request_init_default;
        decoded_request.payload = (struct pb_callback_s)
        {
            .funcs.decode = &pb_decode_simple_cb,
            .arg = &arg,
        };

这个地址是通过nanopb decode来填充的.

不排除是nanopb的bug所致.

解决

  1. 验证情况, 未对齐的地址,强转uint32类型时不会导致unaligned error

对应的汇编是

*(uint32_t *)goal_threshold;

ldr r3,[r10]

可能是未使用到浮点寄存器

  1. 只有强转float型才会

*(float *)goal_threshold;

vldr.32 s0,[r10]

使用到了 s0 寄存器

vldr会触发不对齐错误

  1. 暂时规避方法,在decode出payload数据后,用本地临时变量再memcpy一次,使地址对齐.

#if 0

req_data = offload_req->data.data.arg;

rc = sns_health_offload_set_metric_offset( this, metric_tracker,

req_data->buf, req_data->buf_len ); //buf地址未对齐,导致强转float* 会crash

#else

req_data = offload_req->data.data.arg;

if(req_data->buf_len != sizeof(uint32_t))

{

rc = SNS_RC_INVALID_VALUE;

}else

{

uint8_t buffer[sizeof(uint32_t)]={0};

sns_memscpy(buffer,sizeof(uint32_t),req_data->buf,req_data->buf_len); //memcpy一次再传参

rc = sns_health_offload_set_metric_offset( this, metric_tracker,

buffer, sizeof(uint32_t) );

}

#endif