【求助】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=62 且 len=12 是典型的 I2C/SMBus 回应异常

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

: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: 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 阶段失败,不会自动重试,必须重启或重新触发发现流程。

: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串口的