网卡适配 NCSI over MCTP 协议经验分享

       常规适配网卡时,我们可根据 network_adapter/src/lualib/hardware_config 下支持的协议进行 SR 文件适配即可, 需要关注 SR 中NetworkAdapter(网卡类)中的 Model属性,即芯片型号,芯片型号决定了该网卡支持哪些协议 , 框架会根据 NetworkAdapter 的 Model 属性加载对应的 network_adapter/src/lualib/hardware_config 下的协议配置文件, 例如: 配置 Model 为 E810 即加载对应的 network_adapter/src/lualib/hardware_config/E810.lua 文件;



网卡部分 ncsi 获取部分属性流程如下


       当现有的 network_adapter/src/lualib/hardware_config 下支持协议不满足当前网卡需求时, 就需要对网卡进行新的协议适配, 当前框架支持对已有协议的拓展、新增, 以满足不同网卡的 OEM 命令需求。

如何新增一个协议配置文件?

1. 基础配置结构

       network_adapter/src/lualib/hardware_config 下新增 Test.lua 文件

local bs = require 'mc.bitstring'
local log = require 'mc.logging'
local libmgmt_protocol = require 'libmgmt_protocol'
local utils = require 'mc.utils'

local Test_ncsi = {
    protocol_dependencies = {
        ncsi_standard = {endpoint = nil}  -- 运行时注入端点信息
    },
    properties = {
        -- 属性定义区域
    }
}

return {
    smbus = nil,  -- 不使用 SMBUS 协议
    mctp = function(endpoint)
        local obj = utils.table_copy(Test_ncsi)
        obj.protocol_dependencies.ncsi_standard.endpoint = endpoint
        return obj
    end
}

1.1 协议依赖配置

protocol_dependencies = {
    ncsi_standard = {
        endpoint = nil  -- 由运行时动态注入
    }
    -- 可添加其他协议依赖
}

2. 属性基本结构

每个属性包含以下关键字段:

PropertyName = {
    protocol = 'ncsi_standard',      -- 使用的协议名称
    action = 'on_demand|on_schedule', -- 执行模式
    period_in_sec = 5,               -- 定时执行时的间隔(秒)
    request = {                       -- 请求配置
        packet_type = 0x00,          -- NCSI 数据包类型
        expect_rsp_packet_type = 0x80, -- 期望的响应包类型
        data = '\x00\x01\x02'        -- 可选:附加数据
    },
    response = function(data)        -- 响应解析函数
        -- 数据解析逻辑
        return parsed_data
    end
}

2.1 执行模式说明

按需执行 (on_demand)

action = 'on_demand'
-- 适用于初始化、控制命令等不频繁执行的操作

定时执行 (on_schedule)

action = 'on_schedule'
period_in_sec = 5  -- 执行间隔,根据监控需求调整
-- 适用于状态监控、统计信息收集等需要定期更新的数据

3. 请求配置&响应解析

3.1 标准 NCSI 命令请求

request = {
    packet_type = 0x00,              -- 命令包类型
    expect_rsp_packet_type = 0x80,   -- 响应包类型(通常为命令包类型 + 0x80)
    -- data 字段可选
}

3.2 厂商 OEM 命令请求

request = {
    packet_type = 0x50,             
    expect_rsp_packet_type = 0xD0,   
    data = '\x00\x00\x01\x57\x06'    -- 厂商特定数据
}

若 data 后仍有 padding 字段,只需填写 data 部分即可,框架会进行自动补全,且将 data 长度将 data 长度计算为 payload length。

4. 响应数据解析

4.1 基础解析模板

response = function(data)
    local r = bs.new([[
        response_code:16/big,
        reason_code:16/big,
        field1:8,
        field2:16/big,
        -- 更多字段...
    ]]]):unpack(data, true)
    
    return {
        FieldName1 = r.field1,
        FieldName2 = r.field2,
        -- 映射到输出字段
    }
end

字节序说明

  • big: 大端字节序(网络字节序)
  • little: 小端字节序
  • 默认:大端字节序

响应解析函数bs.new():unpack(data, true)中构造字段的总字节数应与对应命令返回的长度一致,否则会导致 bs.new 解析失败对应属性无法获取数据

4.2 复杂数据解析示例

response = function(data)
    local r = bs.new([[
        rsp_code:16,
        reason_code:16,
        manu_id:32,
        intel_cmd:8,
        mac_addr:1/MAC_ADDRESS,  
        padding:8
    ]]], libmgmt_protocol.common_bs_helper):unpack(data, true)

    return r.mac_addr.mac_address
end

框架供了通用的bitstring格式和通用的协议响应数据解析函数,用于解析协议返回的响应数据。

  • 通用bitstring格式

       通用bitstring格式存于include/libmgmt_protocol/common/init.lua, 使用时调用libmgmt_protocol.common_bs_helper

       目前支持的格式:

名称 bitstring名称 返回格式
Mac地址 MAC_ADDRESS {mac_address = '00:11:22:33:44:55'}
Ipv4地址 IPV4 {ipv4 = '192.168.1.255'}
  • 通用响应数据解析函数

       目前支持的函数:

名称 介绍 入参 返回格式
create_array_parser 用于解析bitstring数组 (单个bitstring定义字符串, 单个数据长度) 表数组

5. 调试和日志

5.1 response 中添加日志打印

​       response = function(data) 函数中我们可以添加相关 lua 代码进行数据转换和逻辑处理,当然我们在其中加入 log 打印查看命令响应是否正确、以及 bs 解析是否正常

示例:

log:error("DEBUG>>> default mac address byte length is: %d", #data)
                local byteArray = {}    
                for i = 1, #data do
                    local byte = data:byte(i)
                    table.insert(byteArray, byte)
                end
                log:error("DEBUG>>> default mac address bytes: %s", table.concat(byteArray, " "))
    DefaultMacAddrNCSI = {
            protocol = 'ncsi_standard',
            action = 'on_demand',
            request = {
                -- 运行时需传入channel_id(也就是port id)
                packet_type = 0x50,
                expect_rsp_packet_type = 0xD0,
                data = '\x00\x00\x01\x57\x06\x00\x00\x00'
            },
            response = function(data)
                log:error("DEBUG>>> default mac address byte length is: %d", #data)
                local byteArray = {}    
                for i = 1, #data do
                    local byte = data:byte(i)
                    table.insert(byteArray, byte)
                end
                log:error("DEBUG>>> default mac address bytes: %s", table.concat(byteArray, " "))
                local r = bs.new([[<<
                    rsp_code:16,
                    reason_code:16,
                    manu_id:32,
                    intel_cmd:8,
                    mac_addr1:1/MAC_ADDRESS,
                    mac_addr2:1/MAC_ADDRESS
                >>]], libmgmt_protocol.common_bs_helper):unpack(data, true)
                log:error("DEBUG>>> default Macaddr1: %s", r.mac_addr1.mac_address)
                return r.mac_addr1.mac_address
            end
        },

建议可在 bs 前将 data 的每个字节通过日志打印,避免 bs 解析失败导致无日志输出。

该方法只能查看响应的数据,无法查看具体下发的命令

5.2 代码注入

1. 新建 test_mctp_capture.lua 放到机器 /tmp 目录下

local c_mctp_engine = require 'mctp_engine'
local mctp_engine = c_mctp_engine.new()
mctp_engine:enable_pcie_capture()

2. 查寻 hardware 端口

netstat -anp | grep 0.0.0.0:400

3. telnet 进端口注入代码

telnet ip 40030

inject :0000000e //test_mctp_capture.lua

参考链接:link:

  1. 南向硬件协议库 | 文档中心 | openUBMC
  2. mctpd使用说明 | 文档中心 | openUBMC
  3. 鲲鹏bmc调试网卡部件,如何开启打印协议通信报文 - Hardware SIG - openUBMC 论坛
  4. mctpd打印MCTP over SMBus通信报文 - 交流互助区 - openUBMC 论坛
1 个赞