接口定制指南-IPMI 接口定制
更新时间: 2026/08/26
在Gitcode上查看源码

接口定制指南-IPMI 接口定制

概述

IPMI(Intelligent Platform Management Interface)是 BMC 与外部管理软件之间的重要通信接口。openUBMC 的 IPMI 协议栈由 ipmi_mgmt 组件实现,负责传输层、命令路由和核心命令处理,业务组件通过注册 bmc.kepler.CmdInfo 接口的 Process 方法实现自定义 IPMI 命令的处理逻辑。

本文以 ‘ipmi_mgmt' 组件为例,面向部件开发者和整机开发者,系统说明 IPMI 接口定制的完整步骤和调试方法,覆盖从命令定义、代码实现、编译部署到调试排障的所有流程。

定制流程总览:

  1. 定制前准备:确认代码仓、工具和环境满足要求
  2. 命令定义:在 cmd_definition.h 中通过命名空间和 mc::app::ipmi_cmd 结构体定义命令的 NetFn、Cmd、编解码格式等属性
  3. 代码实现:在 cmd_handler.h/cpp 中实现处理器函数,在 cmd_registry.cpp 中注册命令
  4. 编译部署:通过 bingo 构建并部署到 BMC
  5. 调试验证:使用 ipmitool 发送命令验证,通过日志和 TraceIpmi 排查问题

适用场景:

  • ipmi_mgmt 中新增标准 IPMI 命令(NetFn 0x06 App 等)的处理逻辑
  • 新增 OEM IPMI 命令(NetFn 0x30~0x3F)以扩展设备管理能力
  • 修改已有 IPMI 命令的请求/响应数据结构
  • 调试 IPMI 命令的交互过程和定位通信问题

IPMI 接口定制步骤

定制前准备

代码仓

代码仓说明
ipmi_mgmtIPMI 协议栈组件,包含命令定义、处理器实现、传输层和过滤器链

工具

工具用途安装方式
bingoopenUBMC 构建工具,用于编译参见《环境准备简介》
ipmitoolIPMI 命令行测试工具,用于发送和验证 IPMI 命令sudo apt install ipmitool

环境要求

  • 已完成 openUBMC 开发环境搭建
  • 已克隆 ipmi_mgmt 代码仓并能正常编译
  • 测试环境具备 BMC 管理口网络连通性(远程测试)或 BMC 本地访问权限(本地测试)

前置知识

建议阅读以下文档:

IPMI 命令定义

IPMI 命令分类

IPMI 命令按 NetFn(网络功能码)范围分为以下类别:

类别NetFn 范围说明请求中是否包含厂商 ID
标准命令0x06、0x04、0x0A、0x0C 等IPMI 规范定义的标准命令
组织扩展类(Group Extension)0x2C、0x2D请求首字节为组织标识(如 PICMG=0x00)
组织级 OEM(OEM/Group)0x2E、0x2F请求/响应前 3 字节为 IANA 号
厂商级 OEM(Vendor OEM)0x30~0x3F各厂商自定义命令可选(推荐包含)

命令号分配规则

  • 标准命令:NetFn 和 Cmd 按 IPMI 规范定义,不可自定义分配
  • OEM 命令:NetFn 在 0x30~0x3F 范围内选取,Cmd 在产品范围内分配,需确保不与已有命令冲突

注意

命令字定义必须避免与已有的 IPMI 命令冲突。复用已有命令字时,必须考虑 BMC 全场景的兼容性,包括新老板卡、周边组件、工具、生产装备、现网场景等。接口修改需遵循接口修改流程

厂商 ID

OEM 命令中使用的厂商 ID(Manufacturer ID,IANA Private Enterprise Number):

厂商 ID十六进制厂商名称
20110x7dbHUAWEI Technology Co., Ltd.
196210x4ca5Alibaba

厂商 ID 查询网址:https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers

命令定义结构

ipmi_mgmt 中,IPMI 命令通过 src/ipmi_cmd/cmd_definition.h 中的命名空间和 mc::app::ipmi_cmd 结构体定义。框架基于 BitString 编解码格式自动完成字节流与 mc::dict 的转换。

命令定义结构体字段

字段类型必填说明
netfnuint8_t网络功能码(如 0x060x30
cmduint8_t命令码(如 0x090x93
nameconst char*命令名称字符串
decodeconst char*请求 BitString 编解码格式,空字符串表示无请求体
encodeconst char*响应 BitString 编解码格式
filterconst char*OEM 子命令过滤模式,空字符串表示不过滤
roleint16_t用户角色要求
privilegeuint32_t权限要求
priorityint16_t命令优先级
sys_locked_policyconst char*系统锁定策略:"Allowed""Forbidden"
sensitivebool是否为敏感命令(追踪时掩码处理)
restricted_channels初始化列表受限通道列表,空列表表示无限制
manufacturerint[2]厂商 ID 在 payload 中的字节索引,{-1, -1} 表示无厂商 ID

BitString 编解码格式

decodeencode 字段使用 BitString DSL 描述 IPMI 报文字段布局:

语法含义示例
<<FieldName:ByteCount/unit:BitWidth>>固定长度字段<<ChannelNum:4/unit:1, Reserved:4/unit:1>>
FieldName/string变长字节序列<<NetFnSupport/string>>
0xNN:1/unit:8固定值字节(编解码时校验/填充)<<0x1F:1/unit:8>>
  • ByteCount:字段占用的字节数
  • unit:BitWidth:每个字节的位宽(unit:8 表示整字节,unit:1 表示按位拆分)
  • 多个字段用逗号分隔,整体用 <<>> 包裹

role 可选值

角色说明
OEM5OEM 权限
Administrator4管理员权限
Operator3操作员权限
User2普通用户权限
Callback1回调权限
Unspecified0未指定

privilege 可选值

权限说明
ReadOnly只读权限,适用于查询类命令
DiagnoseMgmt诊断管理权限
SecurityMgmt安全管理权限
BasicSetting基础设置权限
UserMgmt用户管理权限
PowerMgmt电源管理权限
VMMMgmt虚拟媒体管理权限
KVMMgmtKVM 管理权限
ConfigureSelf自配置权限

priority 可选值

优先级说明
Max50最高优先级
EndUser40终端用户
OBM35OBM 厂商
ODM30ODM 厂商
OEM20OEM 厂商
Default10默认优先级

filter 字段格式

OEM 命令共用相同 NetFn + Cmd 时,通过 filter 字段区分不同的子命令:

filter 值含义
""不过滤
"1F"首字节必须为 0x1F
"20"首字节必须为 0x20
"21,08"前两字节必须为 0x21, 0x08
"*,*,*,3f"第 4 字节必须为 0x3F(前 3 字节通配)
"*,*,*,7A"第 4 字节必须为 0x7A

manufacturer 字段格式

对于包含厂商 ID 的 OEM 命令,manufacturer 指定厂商 ID 在 payload 中的起始和结束字节索引:

manufacturer 值含义
{-1, -1}请求中无厂商 ID
{0, 1}厂商 ID 从字节 0 开始,占 3 字节(索引 0~2),路由结果中记录请求/响应厂商 ID 索引
{1, -1}厂商 ID 从字节 1 开始(索引 1~3),仅请求侧包含厂商 ID

标准命令定义示例

以下为 cmd_definition.hGetNetFnSupport 命令的定义:

cpp
namespace GetNetFnSupport {
inline mc::app::ipmi_cmd cmd{
    .netfn               = 0x06,
    .cmd                 = 0x09,
    .name                = "GetNetFnSupport",
    .decode              = "<<ChannelNum:4/unit:1, Reserved:4/unit:1>>",
    .encode              = "<<CompletionCode:1/unit:8, LUNSupport:1/unit:8, NetFnSupport/string>>",
    .filter              = "",
    .role                = role::User,
    .privilege           = privilege::ReadOnly,
    .priority            = priority::Default,
    .sys_locked_policy   = "Allowed",
    .sensitive           = false,
    .restricted_channels = {},
    .manufacturer        = {-1, -1},
};

// 构造带默认值的响应字典
inline mc::dict rsp(uint8_t completion_code = 0)
{
    mc::dict rsp;
    rsp["CompletionCode"] = completion_code;
    rsp["LUNSupport"]     = static_cast<uint32_t>(0);
    rsp["NetFnSupport"]   = "";
    return rsp;
}
} // namespace GetNetFnSupport

每个命令命名空间还包含 rsp() 辅助函数,用于构造带默认值的响应字典。

OEM 命令定义示例

以下为 BWListAddDel 命令的定义,展示了 OEM 命令的典型模式:

cpp
namespace BWListAddDel {
inline mc::app::ipmi_cmd cmd{
    .netfn     = 0x30,
    .cmd       = 0x93,
    .name      = "BWListAddDel",
    .decode    = "<<ManufactureId:3/unit:8, 0x3f:1/unit:8, Parameter:1/unit:8, Option:1/unit:8, ReadWrite:1/unit:8,\
                 Netfn:1/unit:8, Cmd:1/unit:8, Chan:1/unit:8, Datas/string>>",
    .encode    = "<<CompletionCode:1/unit:8, ManufactureId:3/unit:8>>",
    .filter    = "*,*,*,3f",
    .role      = role::Administrator,
    .privilege = privilege::SecurityMgmt,
    .priority  = priority::OEM,
    .sys_locked_policy   = "Forbidden",
    .sensitive           = false,
    .restricted_channels = {},
    .manufacturer        = {0, 1},
};

// 构造带默认值的响应字典
inline mc::dict rsp(uint8_t completion_code = 0)
{
    mc::dict rsp;
    rsp["CompletionCode"] = completion_code;
    rsp["ManufactureId"]  = static_cast<uint32_t>(0);
    return rsp;
}
} // namespace BWListAddDel

OEM 命令定义要点:

  1. 厂商 ID 字段decode 中前 3 字节为 ManufactureId:3/unit:8manufacturer = {0, 1} 指示厂商 ID 位于字节 0~2
  2. 子命令区分filter = "*,*,*,3f" 表示第 4 字节必须为 0x3F,用于区分共用 NetFn 0x30/Cmd 0x93 的不同 OEM 子命令
  3. 固定值字节decode 中的 0x3f:1/unit:8 定义固定值字段,编解码时自动校验和填充
  4. 厂商 ID 字节序:厂商 ID 在 IPMI 报文中为 3 字节小端序(如 0x7db 对应字节序列 db 07 00

代码实现

命令处理器开发

命令处理器(handler)是 IPMI 命令的业务处理入口,遵循统一的函数签名。

处理器函数签名

cpp
mc::dict handler_name(mc::dict req, mc::dict ctx);
  • req:BitString 解码后的请求字典,键名与 decode 中定义的字段名一致
  • ctx:IPMI 上下文字典,包含通道信息(chan_numChanTypeHostIdinterface_id 等)
  • 返回值:响应字典,键名与 encode 中定义的字段名一致

处理器实现步骤

  1. 创建响应对象:调用 cmd_def::CommandName::rsp() 构造带默认值的响应字典
  2. 设置 CompletionCode:成功为 0x00,异常使用对应完成码
  3. 处理业务逻辑:从请求字典中读取字段,执行业务操作
  4. 设置响应字段:填充响应数据
  5. 返回响应字典

标准命令处理器示例

以下为 reset_watchdog_timer 处理器的实现:

cpp
mc::dict reset_watchdog_timer(mc::dict req, mc::dict ctx)
{
    // 1. 创建响应对象
    auto rsp = cmd_def::ResetWatchdogTimer::rsp();

    // 2. 检查通道合法性
    if (is_multihost_lan_channel(ctx)) {
        rsp["CompletionCode"] = static_cast<uint8_t>(0xca);
        return rsp;
    }

    // 3. 执行业务逻辑并设置响应
    uint8_t system_id     = get_system_id_from_ctx(ctx);
    rsp["CompletionCode"] = watchdog2_mgr::instance().reset_wd_timer(system_id);

    // 4. 返回响应
    return rsp;
}

OEM 命令处理器示例

以下为 add_del_bw_list 处理器的实现,展示了 OEM 命令中厂商 ID 校验的典型模式:

cpp
mc::dict add_del_bw_list(mc::dict req, mc::dict ctx)
{
    // 1. 创建响应对象
    auto     rsp     = cmd_def::BWListAddDel::rsp();

    // 2. 校验厂商 ID
    uint32_t manu_id = req["ManufactureId"].as_uint32();
    if (manu_id != MANUFACTURE_ID) {
        elog("invalid manufacture id: ${manu_id}", ("manu_id", manu_id));
        rsp["CompletionCode"] = static_cast<uint8_t>(0xcc);
        return rsp;
    }

    // 3. 执行业务逻辑
    uint8_t mode  = req["Parameter"].as_uint8();
    uint8_t option = req["Option"].as_uint8();
    if (option == BW_LIST_ADD) {
        bw_list_mgr::instance().add_entry(mode, req["Netfn"].as_uint8(),
            req["Cmd"].as_uint8(), req["Chan"].as_uint8(), req["Datas"].as_string());
    } else if (option == BW_LIST_DEL) {
        bw_list_mgr::instance().remove_entry(mode, req["Netfn"].as_uint8(),
            req["Cmd"].as_uint8(), req["Chan"].as_uint8(), req["Datas"].as_string());
    }

    // 4. 设置响应字段
    rsp["ManufactureId"] = manu_id;
    return rsp;
}

OEM 命令处理器要点:

  1. 厂商 ID 校验:使用 req["ManufactureId"].as_uint32() 读取解码后的厂商 ID,与 MANUFACTURE_ID(0x0007db)比对,不匹配返回 0xCC
  2. 请求字段访问:通过 req["FieldName"].as_uint8() / as_uint32() / as_string() 按类型访问
  3. 响应字段设置:通过 rsp["FieldName"] = value 设置响应字段
  4. 操作日志:使用 operation_log() 记录关键操作,使用 elog() 记录错误

声明处理器

src/ipmi_cmd/cmd_handler.h 中声明处理器函数:

cpp
namespace ipmi_mgmt::cmd_handler {

mc::dict get_netfn_support(mc::dict req, mc::dict ctx);
mc::dict add_del_bw_list(mc::dict req, mc::dict ctx);
// 新增命令的处理器声明
mc::dict my_new_command(mc::dict req, mc::dict ctx);

} // namespace ipmi_mgmt::cmd_handler

注册命令

src/ipmi_cmd/cmd_registry.cpp 中将命令定义与处理器绑定。ipmi_mgmt 有两条命令处理路径:

注册表处理路径适用场景
cmd_registry[]经过滤器链和路由后,通过 D-Bus RPC 分发到外部业务组件非本组件处理的命令
quick_cmd_registry[]ipmi_mgmt 进程内直接执行 decode/handler/encode,不经 RPCSDR/SEL 等需本组件直接处理的命令

注册到标准路径:

cpp
static cmd_entry cmd_registry[] = {
    {cmd_def::GetNetFnSupport::cmd, cmd_handler::get_netfn_support},
    {cmd_def::BWListAddDel::cmd, cmd_handler::add_del_bw_list},
    // 新增命令注册
    {cmd_def::MyNewCommand::cmd, cmd_handler::my_new_command},
};

注册到快捷路径:

cpp
static cmd_entry quick_cmd_registry[] = {
    {cmd_def::ReserveDeviceSDR::cmd, cmd_handler::ipmi_reserve_device_sdr},
    // 需本进程直接处理的命令
};

两条路径的注册均在 register_cmds() 函数中完成,于 on_start() 阶段调用:

cpp
void register_cmds(mc::app::service& service)
{
    // 注册标准路径命令
    for (auto& entry : cmd_registry) {
        service.register_ipmi_cmd(entry.cmd, entry.handler);
    }
    // 注册快捷路径命令
    for (auto& entry : quick_cmd_registry) {
        service.register_ipmi_cmd(entry.cmd, entry.handler);
    }
}

IPMI 命令处理流程

理解 IPMI 命令从接收到响应的完整处理流程,有助于正确实现处理器:

[通道驱动] 收到原始字节(BT/IPMB/IPMB-Eth/EDMA)
       |
       v
[service_manager] 路由队列调度
       |
       v
[过滤器链] 依次执行:
  ├─ firewall_filter(黑白名单检查,拦截返回 0xD4)
  ├─ lockdown_filter(系统锁定检查,拦截返回 0xD5)
  └─ quick_cmd 查询(命中则本进程直接处理)
       |
       v
[routing_filter] SHM 路由表匹配
       |
       v
[RPC 分发] D-Bus timeout_call 到目标业务组件
       |
       v
[业务组件 Process 方法] 解码请求 → 业务处理 → 编码响应
       |
       v
[响应返回] 经原通道返回给请求方

说明

RPC 分发的超时时间为 5 秒,业务组件的 Process 方法应在此时间内完成处理并返回响应,否则 ipmi_mgmt 会返回超时完成码。

编译部署

组件的通用编译和部署操作请参考《组件的构建与发布》和《自动部署功能》。以下仅说明 ipmi_mgmt 组件特有的编译选项和部署注意事项。

编译选项

ipmi_mgmt 使用 Meson 构建系统,编译选项定义在 meson_options.txt 中:

选项默认值说明
teststrue构建测试用例
enable_coveragefalse启用代码覆盖率
enable_dfttrue启用 DFT 产测能力
chipv2_enablefalse启用 chip v2
enable_bttrue启用 BT 传输装配
enable_ipmbtrue启用 IPMB 传输装配
enable_ipmb_ethtrue启用 IPMB-Eth 传输装配
enable_edmatrue启用 EDMA 传输装配

部署注意事项

新增或修改 IPMI 命令后,需重启 ipmi_mgmt 服务以刷新 SHM 路由表使命令注册生效:

bash
ssh root@<BMC_IP> "systemctl restart ipmi_mgmt"

验证部署

部署完成后,通过以下方式验证命令是否注册成功:

  1. 检查服务状态:
bash
ssh root@<BMC_IP> "systemctl status ipmi_mgmt"

预期输出中服务状态为 active (running)

  1. 查看服务日志确认命令注册:
bash
ssh root@<BMC_IP> "journalctl -u ipmi_mgmt | grep 'register ipmi commands'"

预期输出中包含 register ipmi commands 日志条目,表示命令注册完成。

调试方法

IPMI 命令测试工具

ipmitool

远程测试命令格式:

bash
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw <netfn> <cmd> [data...]

本地测试命令格式:

bash
ipmitool raw <netfn> <cmd> [data...]

参数说明:

参数说明
-I lanplus使用 IPMI 2.0 LAN 接口(推荐)
-I open本地 KCS 或 BT 接口
-HBMC 的 IP 地址
-U用户名
-P密码
-C加密算法套件(推荐 17)
raw发送原始 IPMI 命令
<netfn>网络功能码,需使用请求 NetFn(偶数),十六进制加 0x 前缀
<cmd>命令码,十六进制加 0x 前缀
[data...]命令数据字节,十六进制加 0x 前缀,空格分隔

详细语法参见: 命令使用方式

常见调试命令和日志查看方法

检查命令是否已注册

通过 GetNetFnSupport(0x06/0x09)命令查询已注册的 NetFn:

bash
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> raw 0x06 0x09 0x0e 0x00

通过 GetCommandSupport(0x06/0x0A)查询指定 NetFn 下已注册的命令:

bash
# 查询 NetFn 0x30 下已注册的命令
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> raw 0x06 0x0a 0x0e 0x00 0x30 0x00 0x00

查看 ipmi_mgmt 服务日志

bash
# 查看最近日志
cat /var/log/app.log | grep ipmi_core

使用南向 IPMI 追踪(TraceIpmi)

TraceIpmi 是 ipmi_mgmt 提供的南向报文追踪能力,可以按通道、NetFn、Cmd 等条件抓取 BT、IPMB 等介质上的原始报文。

追踪对象路径: /bmc/kepler/Debug/IpmiCore/TraceIpmi接口: bmc.kepler.Debug.IpmiCore.TraceIpmi

开启追踪

通过 D-Bus 调用 Trace 方法开启追踪:

bash
# 追踪 BT 通道所有命令(NetFn=0xFF, Cmd=0xFF 表示不过滤)
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace true bt 255 255 "" file

追踪指定命令

bash
# 仅追踪 NetFn=0x30, Cmd=0x93 的命令
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace true bt 48 147 "" file

Trace 方法参数:

参数类型说明
EnableTracebool是否开启追踪
Channelstring通道名:btipmbedmaipmbeth
Netfnuint8目标 NetFn,0xFF 表示匹配任意 NetFn
Cmduint8目标 Cmd,0xFF 表示匹配任意 Cmd
Filterstring原始过滤字节流(预留)
LogTypestring日志输出方式:file 写文件,local 本地日志

查看追踪输出

追踪日志输出到 /tmp/ipmi.txt

bash
cat /tmp/ipmi.txt

输出格式:

[2026-06-06 10:15:30.123]bt-0-send ---- 20 18 c8 81 04 01 02 03
[2026-06-06 10:15:30.456]ipmb-0-recv--0x20->0x2c ---- 2c 18 06 00 20 00 00 00
  • 前缀格式:<通道>-<接口ID>-<方向>,方向为 send(BMC 发出)或 recv(BMC 收到)
  • 日志文件超过 5MB 时自动截断重写

关闭追踪

bash
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace false bt 0 0 "" ""

注意

追踪功能默认关闭,仅在调试时开启。开启后会产生日志开销,调试完成后请及时关闭。

命令超时等常见问题的排查步骤

命令返回 0xC1(非法命令)

现象: 发送 IPMI 命令后返回完成码 0xC1

可能原因及排查步骤:

  1. 命令未注册:检查 cmd_definition.h 是否正确定义,cmd_registry.cpp 是否已注册

  2. 路由表未刷新:重启 ipmi_mgmt 服务以刷新 SHM 路由表:

    bash
    systemctl restart ipmi_mgmt
  3. 业务组件未运行:检查处理该命令的业务组件服务状态:

    bash
    systemctl status <component_name>
  4. NetFn/Cmd 不匹配:确认发送命令的 NetFn 和 Cmd 与定义一致

命令返回 0xC3(执行超时)

现象: 发送 IPMI 命令后返回完成码 0xC3

可能原因及排查步骤:

  1. 业务处理器执行超时:RPC 分发超时为 5 秒,检查处理器是否存在阻塞操作

  2. 业务组件无响应:检查业务组件是否正常处理 D-Bus 请求:

    bash
    journalctl -u <component_name> -f
  3. D-Bus 连接异常:检查 D-Bus 通信是否正常:

    bash
    dbus-send --system --dest=<component_service> --print-reply <object_path> <interface>.<method>

命令返回 0xD4(权限不足)

现象: 发送 IPMI 命令后返回完成码 0xD4

可能原因及排查步骤:

  1. 防火墙拦截:命令被黑白名单拦截,检查防火墙策略:

    bash
    # 查询防火墙状态
    ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> raw 0x30 0x93 0xdb 0x07 0x00 0x4c
  2. 用户权限不足:当前登录用户的角色或权限不满足命令定义中的 role/privilege 要求

  3. 命令被加入黑名单:检查命令是否被添加到自定义黑名单

命令返回 0xD5(命令不可用)

现象: 发送 IPMI 命令后返回完成码 0xD5

可能原因及排查步骤:

  1. 系统锁定:BMC 处于系统锁定状态,部分命令被限制执行
  2. 命令的 sys_locked_policy 设置为 Forbidden:确认该命令在系统锁定时是否允许执行

命令返回 0xCC(请求数据字段无效)

现象: 发送 IPMI 命令后返回完成码 0xCC

可能原因及排查步骤:

  1. 厂商 ID 不匹配:OEM 命令中厂商 ID 与 BMC 实际厂商 ID 不一致
  2. 请求数据格式错误:检查发送的数据字节序列是否与 decode 定义的结构一致
  3. 子命令标识错误:OEM 命令中子命令字节与 filter 定义不匹配

命令返回 0xC7(请求数据长度非法)

现象: 发送 IPMI 命令后返回完成码 0xC7

可能原因及排查步骤:

  1. 请求字节长度不正确:确认发送的数据字节数与 decode 定义的结构总长度一致
  2. 缺少必要字段:检查是否遗漏了厂商 ID 或子命令等必要字段

命令无响应

现象: 发送 IPMI 命令后无任何响应

可能原因及排查步骤:

  1. 通道不通:检查网络连通性(远程)或 KCS/BT 驱动状态(本地)

  2. ipmi_mgmt 服务异常:检查服务是否运行:

    bash
    systemctl status ipmi_mgmt
  3. 消息长度超限:IPMB 最长 254 字节,BT 最长 252 字节,确认请求消息未超限

IPMI命令示例

以下以一个典型的 IPMI OEM 命令为例,展示从定义到调试的完整流程。

需求描述:ipmi_mgmt 中新增一个 OEM 命令 GetDftMode,用于查询 DFT(产测)模式的启用状态和模式值。

步骤一:定义 IPMI 命令

src/ipmi_cmd/cmd_definition.h 中新增命令命名空间:

cpp
namespace GetDftMode {
inline mc::app::ipmi_cmd cmd{
    .netfn               = 0x30,
    .cmd                 = 0x90,
    .name                = "GetDftMode",
    .decode              = "<<0x1F:1/unit:8>>",
    .encode              = "<<CompletionCode:1/unit:8, Enabled:1/unit:8, Mode:1/unit:8>>",
    .filter              = "1F",
    .role                = role::Operator,
    .privilege           = privilege::DiagnoseMgmt,
    .priority            = priority::Default,
    .sys_locked_policy   = "Allowed",
    .sensitive           = false,
    .restricted_channels = {},
    .manufacturer        = {-1, -1},
};

// 构造带默认值的响应字典
inline mc::dict rsp(uint8_t completion_code = 0)
{
    mc::dict rsp;
    rsp["CompletionCode"] = completion_code;
    rsp["Enabled"]        = static_cast<uint8_t>(0);
    rsp["Mode"]           = static_cast<uint8_t>(0);
    return rsp;
}
} // namespace GetDftMode

定义说明:

  • NetFn 为 0x30,属于厂商级 OEM 命令
  • Cmd 为 0x90,与 SetDftMode 等命令共用,通过 filter = "1F" 区分(首字节为 0x1F)
  • decode0x1F:1/unit:8 为固定值字段,框架自动校验请求首字节为 0x1F
  • encode 中定义响应包含 CompletionCode、Enabled 和 Mode 三个字段
  • 此命令不含厂商 ID,因此 manufacturer = {-1, -1}

步骤二:声明处理器

src/ipmi_cmd/cmd_handler.h 中声明:

cpp
mc::dict get_dft_mode(mc::dict req, mc::dict ctx);

步骤三:实现处理器

src/ipmi_cmd/cmd_handler.cpp 中实现:

cpp
mc::dict get_dft_mode(mc::dict req, mc::dict ctx)
{
    // 1. 创建响应对象
    auto rsp = cmd_def::GetDftMode::rsp();

    // 2. 获取 DFT 对象
    auto* dft_obj = get_dft_object();
    if (dft_obj == nullptr) {
        rsp["CompletionCode"] = static_cast<uint8_t>(0xFF);
        return rsp;
    }

    // 3. 设置响应字段
    rsp["Enabled"] = dft_obj->get_dft_enabled() ? IPMI_DFT_ENABLE : IPMI_DFT_DISABLE;
    rsp["Mode"]    = dft_obj->get_dft_mode();

    // 4. 返回响应
    return rsp;
}

步骤四:注册命令

src/ipmi_cmd/cmd_registry.cppcmd_registry[] 数组中添加:

cpp
static cmd_entry cmd_registry[] = {
    // 已有命令
    {cmd_def::GetDftMode::cmd, cmd_handler::get_dft_mode},
};

步骤五:编译部署

  1. 编译和部署操作请参考《组件的构建与发布》和《自动部署功能

  2. 重启 ipmi_mgmt 服务使命令注册生效:

bash
ssh root@<BMC_IP> "systemctl restart ipmi_mgmt"
  1. 验证服务状态:
bash
ssh root@<BMC_IP> "systemctl status ipmi_mgmt"

预期服务状态为 active (running)

步骤六:调试验证

发送测试命令

bash
# 远程测试(首字节 0x1F 为子命令标识)
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw 0x30 0x90 0x1f

预期响应:

 00 01 00

响应解读:

偏移字节含义
000CompletionCode(0x00 = 成功)
101Enabled(0x01 = 已启用)
200Mode(0x00 = 默认模式)

使用追踪功能调试

如果命令未按预期响应,开启 TraceIpmi 追踪:

  1. 开启追踪:
bash
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace true bt 48 255 "" file
  1. 发送测试命令:
bash
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw 0x30 0x90 0x1f
  1. 查看追踪日志:
bash
cat /tmp/ipmi.txt
  1. 关闭追踪:
bash
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace false bt 0 0 "" ""

常见问题排查

  1. 返回 0xC1:检查 cmd_definition.hcmd_registry.cpp 是否正确,重启 ipmi_mgmt 服务
  2. 返回 0xCC:检查子命令字节是否正确(GetDftMode 首字节应为 0x1F)
  3. 返回 0xC3:检查业务组件是否正常运行,处理器是否有阻塞操作
  4. 返回 0xD4:检查当前用户权限是否满足 DiagnoseMgmt 要求

附录

完成码速查表

完成码描述
0x00命令执行成功
0xC1非法命令(未注册或不支持)
0xC3执行超时
0xC7请求数据长度非法
0xCC请求数据字段无效
0xD4权限不足
0xD5命令不可用(系统锁定等)
0xFF未指定的错误

更多完成码参见 完成码定义

消息长度约束

通道最大消息长度
IPMB254 字节(消息头 6 + 消息体 247 + 校验 1)
BT252 字节(消息头 4 + 消息体 247 + 校验 1)

相关文档