点击此处查看详细分析解答
MCTP over SMBus 响应报文格式问题分析与解答
根据您提供的详细现象、日志信息和逻辑分析仪抓包数据,我们对“MCTP over SMBus 响应报文格式”问题进行了全面分析。您遇到的问题核心在于 OpenUBMC 平台在接收 SMBus 响应时,对接收流程和数据解析存在严格的机制控制,尤其是 len=12 的错误和后续的 129 字节读取不匹配问题。
问题现象总结
| 现象 |
描述 |
网卡侧 |
正确接收到 Set Endpoint ID 请求,并按 MCTP over SMBus 协议返回了 129 字节的完整响应(含补零)。逻辑分析仪波形正常。 |
BMC 侧 |
发起了正确的 Read 请求(Slave Address 0x5D,Command Code 0x1F)。 |
BMC 日志 |
多次出现 len=12, result=62 错误;最终一次读取显示长度为 129,但前几个字节与预期不符。 |
疑问 |
是不是响应数据格式不对?为何 BMC 一开始只尝试读 12 字节?最终读取的数据为何不匹配? |
核心原因分析
1. result=62 且 len=12 是典型的 I2C/SMBus 回应异常
result=62 是 Linux 内核 I2C 子系统定义的错误码 EPROTO(Protocol error),通常表示:
- 从设备未正确响应地址。
- 接收过程中出现 NACK。
- 时序不满足要求(如 clock stretching 超时)。
len=12 的读操作,极可能是 BMC 的驱动或中间件(如 component_drivers 或 devmon)预设了一个 最小固定长度读取试探,用于解析响应头。
推断:您的网卡设备在首次被读取时未能及时准备好全部数据,导致 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: 5C 和 Address 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 阶段失败,不会自动重试,必须重启或重新触发发现流程。 |
正确的 MCTP over SMBus 响应格式要求
完整帧结构(以 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 |
总长度 = 1 (Byte Count) + n (Data) + 1 (PEC) = 实际 I2C 返回长度
重点:Byte Count 应为有效 payload 长度(即 MCTP 数据部分),不能包含 PEC 和 Byte Count 自身。
推荐解决方案
1. 确保 Set EID 响应长度与协议一致
- 您返回了 129 字节,推测结构为:
- 1 字节 Len(=128)
- 128 字节 Data(含 MCTP Header + Body + 补零)
- 1 字节 PEC?
- ⇒ 总长 130 字节? →
不符合 SMBus Block Read 格式
正确做法:
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。
2. 增加延迟响应机制(针对 FPGA/MCU 性能慢)
在接收到 Set EID Request 后:
// 设置一个标志,启动后台任务准备响应
set_eid_pending = true;
// 延迟 50~100ms 再允许 SMBus Read 成功返回数据
while (!response_ready) {
wait_us(1000);
}
否则,BMC 在 Request 后立即发起 Read,MCU 来不及处理 → 导致协议错误。
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]
4. 使用 tracemctp 工具验证通信
# 登录 BMC 终端
telnet <bmc_ip> 40030
> inject :0000000e "require'mctp_engine'.new():enable_pcie_capture()"
> tracemctp start
然后复现过程,查看 /var/log/app.log 中是否有完整的 [RECV] 数据。
5. 检查硬件拓扑与 I2C 地址配置
确认以下配置匹配:
"Chip_SmbusChip": {
"Address": 92, // 0x5C
...
},
"MctpBinding_1": {
"BmcSMBusEid": 8,
"BmcSMBusPhyAddr": 16
}
- BMC 物理地址 = 16 (0x10) → 写入到
SMBus 中的源地址
- Device I2C 地址 = 0x5C (Write), 0x5D (Read)
结论与建议
| 项目 |
建议 |
| 响应长度问题 |
Byte Count 应为 MCTP payload 长度(如 128),不包含自身和 PEC;总响应长度为 1 + N + 1 |
| 为何 len=12? |
是 BMC 协议栈试探性读取,说明前期通信失败 |
| 为何数据错乱? |
可能因 PEC 错误、MCU 未准备好、或总线竞争导致数据损坏 |
| 最佳实践 |
增加延时、正确计算 PEC、使用逻辑分析仪对比每帧 |
| 调试工具 |
使用 tracemctp start + inject 捕获完整 MCTP 通信流 |
附加建议
-
升级至 OpenUBMC 25.12 LTS 或更高版本
早期版本(如 25.06)存在 MCTP over SMBus endpoint 创建 bug(见《南向适配网迅网卡经验分享》)。
-
参考官方文档
-
联系社区贡献者
百信-罗昊 等用户在 OpenUBMC 论坛中多次解决类似问题,可参考其讨论帖 #5087。
最终结论:您返回的数据格式可能存在 Byte Count 或 PEC 错误,建议严格按照 MCTP over SMBus 协议封装响应帧,并保证从设备在 BMC 发起 Read 时已完全准备好数据。