【求助】mctp over msbus 响应报文格式问题

问题描述

在米尔开发板进行网卡的smbus接口适配时,创建eid阶段网卡侧收到了set eid报文,按照 mctp over msbus 报文格式返回了响应(129字节,不足部分补0)

逻辑分析仪抓取波形正常,openubmc的读请求与网卡侧写数据波形如下:

响应报文发送完毕

openubmc侧也打印也显示收到了响应报文,但是实际收到的数据长度和内容与发送过去的不一致

想请教下大家,是不是我返回的数据格式不对?正确的格式应该是什么?

环境信息

  • 操作系统:Ubuntu 24.04

  • 软件版本:OpenUBMC2603

  • 硬件配置:米尔开发板,I2C_8

答案内容由AI生成,仅供参考:

:white_check_mark: 结论与建议

项目 建议
响应长度问题 Byte Count 应为 MCTP payload 长度(如 128),不包含自身和 PEC;总响应长度为 1 + N + 1
为何 len=12? 是 BMC 协议栈试探性读取,说明前期通信失败
为何数据错乱? 可能因 PEC 错误、MCU 未准备好、或总线竞争导致数据损坏
最佳实践 增加延时、正确计算 PEC、使用逻辑分析仪对比每帧
调试工具 使用 tracemctp start + inject 捕获完整 MCTP 通信流

:white_check_mark: 最终结论:您返回的数据格式可能存在 Byte Count 或 PEC 错误,建议严格按照 MCTP over SMBus 协议封装响应帧,并保证从设备在 BMC 发起 Read 时已完全准备好数据。

点击此处查看详细分析解答

MCTP over SMBus 响应报文格式问题分析与解答

根据您提供的详细现象、日志信息和逻辑分析仪抓包数据,我们对“MCTP over SMBus 响应报文格式”问题进行了全面分析。您遇到的问题核心在于 OpenUBMC 平台在接收 SMBus 响应时,对接收流程和数据解析存在严格的机制控制,尤其是 len=12 的错误和后续的 129 字节读取不匹配问题


:magnifying_glass_tilted_left: 问题现象总结

现象 描述
:white_check_mark: 网卡侧 正确接收到 Set Endpoint ID 请求,并按 MCTP over SMBus 协议返回了 129 字节的完整响应(含补零)。逻辑分析仪波形正常。
:white_check_mark: BMC 侧 发起了正确的 Read 请求(Slave Address 0x5D,Command Code 0x1F)。
:cross_mark: BMC 日志 多次出现 len=12, result=62 错误;最终一次读取显示长度为 129,但前几个字节与预期不符。
:red_question_mark: 疑问 是不是响应数据格式不对?为何 BMC 一开始只尝试读 12 字节?最终读取的数据为何不匹配?

:puzzle_piece: 核心原因分析

1. result=62len=12 是典型的 I2C/SMBus 回应异常

  • result=62 是 Linux 内核 I2C 子系统定义的错误码 EPROTO(Protocol error),通常表示:
    • 从设备未正确响应地址。
    • 接收过程中出现 NACK。
    • 时序不满足要求(如 clock stretching 超时)。
  • len=12 的读操作,极可能是 BMC 的驱动或中间件(如 component_driversdevmon)预设了一个 最小固定长度读取试探,用于解析响应头。

:white_check_mark: 推断:您的网卡设备在首次被读取时未能及时准备好全部数据,导致 I2C 通信协议层面失败,故返回了 EPROTO


2. MCTP over SMBus 的响应机制依赖于“Block Read Process Call”

SMBus 上的 MCTP 控制消息(如 Set EID)遵循的是 SMBus Block Read - Block Write Process Call 的事务模型:

主设备 (BMC):
  Step 1: [Write] Start + Addr(W) + Cmd(0x1F) → 触发从设备准备响应
  Step 2: [Read]  Restart + Addr(R) → 从设备返回:[Len] + [Data...] + [PEC]

关键点是:BMC 在发起读操作前, 必须确保从设备(网卡 MCU)已经完成响应数据的组织并处于可读状态

您逻辑分析仪中看到 Address write: 5CAddress read: 5D 的波形是标准流程,但如果从设备在 Read 阶段尚未准备好,就会发生如下情况:

  • BMC 尝试读取前 N 字节(如 12 字节,作为 header 解析)→ 失败 → result=62
  • 后续某次尝试才成功读取完整的 129 字节

3. 为何成功读取时数据不匹配?

根据日志:

1970-01-02 01:28:51.176344 daemon ERROR: 12c.cpp (146): xxxx normal_read: 0x32 0x31 0x12 0x93 0x1 0x8 0x30 0x1930x0 0x4 0x1 0x0

这些值(如 0x32, 0x31)明显与您期望的 MCTP 响应不一致。

可能原因如下

原因 说明
PEC 校验失败导致驱动丢弃数据并缓存脏数据 如果 PEC 计算错误,Linux I2C 驱动会认为数据无效,可能导致后续读取行为异常。
Slave 设备没有释放总线或状态未重置 成功一次通信后,MCU 未清除中断或状态寄存器,导致下一次响应复用旧数据或错位。
BMC 层协议栈(mctpd)缓存了错误状态 mctpd 一旦在 Set EID 阶段失败,不会自动重试,必须重启或重新触发发现流程。

:white_check_mark: 正确的 MCTP over SMBus 响应格式要求

:white_check_mark: 完整帧结构(以 Set EID Response 为例)

字段 长度(字节) 示例值 说明
Byte Count 1 0x80 (128) 表示后续数据长度为 128 字节(MCTP header + body),不包含自身和 PEC
MCTP Reserved 1 0x00 固定为 0
Hdr Version 1 0x01 MCTP 头版本
Dest EID 1 0x08 BMC 的 EID(配置为 8)
Src EID 1 0x01 网卡端点的 EID
SOM/EOM/Pkt Seq/Msg Tag 1 0xC1 SOM=1, EOM=1, Pkt Seq=0, Msg Tag=1
!C/Msg Type 1 0x00 Control Message Type: 0x00
Command Code 1 0x80 Set EID Command → 0x80
Completion Code 1 0x00 成功
Message Data ≤124 视命令而定 Set EID 无额外数据,可用 0 填充
Integrity Check 0 或 1 可选,MCTP over SMBus 通常不用
PEC 1 C0 CRC-8 of all prior bytes

:white_check_mark: 总长度 = 1 (Byte Count) + n (Data) + 1 (PEC) = 实际 I2C 返回长度

:warning: 重点:Byte Count 应为有效 payload 长度(即 MCTP 数据部分),不能包含 PEC 和 Byte Count 自身。


:white_check_mark: 推荐解决方案

:white_check_mark: 1. 确保 Set EID 响应长度与协议一致

  • 您返回了 129 字节,推测结构为:
    • 1 字节 Len(=128)
    • 128 字节 Data(含 MCTP Header + Body + 补零)
    • 1 字节 PEC?
    • ⇒ 总长 130 字节? → :cross_mark: 不符合 SMBus Block Read 格式

:white_check_mark: 正确做法

uint8_t response[130]; // 1(byte count) + 128(data) + 1(PEC)
response[0] = 128;     // byte count = 128
memcpy(&response[1], mctp_data, 128); // MCTP header + body (padded)
response[129] = calculate_pec(response, 0, 129); // PEC over first 129 bytes

然后由 I2C 从设备硬件或固件自动发送此 buffer。


:white_check_mark: 2. 增加延迟响应机制(针对 FPGA/MCU 性能慢)

在接收到 Set EID Request 后:

// 设置一个标志,启动后台任务准备响应
set_eid_pending = true;

// 延迟 50~100ms 再允许 SMBus Read 成功返回数据
while (!response_ready) {
    wait_us(1000);
}

否则,BMC 在 Request 后立即发起 Read,MCU 来不及处理 → 导致协议错误。


:white_check_mark: 3. 验证 PEC 计算是否正确

使用标准 CRC-8 (Poly: 0x07) 对 Slave Address (Write) + Command Code + Data (含 Byte Count) 进行计算。

参考代码:

uint8_t crc8_smbus(const uint8_t *data, size_t len) {
    uint8_t crc = 0;
    for (size_t i = 0; i < len; ++i) {
        crc ^= data[i];
        for (int j = 0; j < 8; ++j)
            crc = (crc << 1) ^ ((crc & 0x80) ? 0x07 : 0x00);
    }
    return crc;
}

传入:[0x5C << 1], 0x1F, response[0]...response[128]


:white_check_mark: 4. 使用 tracemctp 工具验证通信

# 登录 BMC 终端
telnet <bmc_ip> 40030
> inject :0000000e "require'mctp_engine'.new():enable_pcie_capture()"
> tracemctp start

然后复现过程,查看 /var/log/app.log 中是否有完整的 [RECV] 数据。


:white_check_mark: 5. 检查硬件拓扑与 I2C 地址配置

确认以下配置匹配:

"Chip_SmbusChip": {
    "Address": 92,  // 0x5C
    ...
},
"MctpBinding_1": {
    "BmcSMBusEid": 8,
    "BmcSMBusPhyAddr": 16
}
  • BMC 物理地址 = 16 (0x10) → 写入到 SMBus 中的源地址
  • Device I2C 地址 = 0x5C (Write), 0x5D (Read)

:white_check_mark: 结论与建议

项目 建议
响应长度问题 Byte Count 应为 MCTP payload 长度(如 128),不包含自身和 PEC;总响应长度为 1 + N + 1
为何 len=12? 是 BMC 协议栈试探性读取,说明前期通信失败
为何数据错乱? 可能因 PEC 错误、MCU 未准备好、或总线竞争导致数据损坏
最佳实践 增加延时、正确计算 PEC、使用逻辑分析仪对比每帧
调试工具 使用 tracemctp start + inject 捕获完整 MCTP 通信流

:wrench: 附加建议

  1. 升级至 OpenUBMC 25.12 LTS 或更高版本
    早期版本(如 25.06)存在 MCTP over SMBus endpoint 创建 bug(见《南向适配网迅网卡经验分享》)。

  2. 参考官方文档

  3. 联系社区贡献者
    百信-罗昊 等用户在 OpenUBMC 论坛中多次解决类似问题,可参考其讨论帖 #5087


:white_check_mark: 最终结论:您返回的数据格式可能存在 Byte Count 或 PEC 错误,建议严格按照 MCTP over SMBus 协议封装响应帧,并保证从设备在 BMC 发起 Read 时已完全准备好数据。

你的normal_read是怎么打印的

我是判断接收完成后使用elog打印了data.out_buffer中的值

我感觉是打印方法的问题,我建议你用ai帮你写一个

那再请教下如果用printf打印会保存到那个log文件文件中?之前用了下没找到打印位置

printf是打印到BMC串口的