代码仓
中
IPMI接口定制指南
更新时间: 2026/08/26
在AtomGit上查看源码

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 命令时,需同步更新以下文档:

📁 文档仓库:https://gitcode.com/openUBMC/docs/tree/main/docs/zh/development/specifications/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是网络功能码(如 0x06、0x30)
cmduint8_t是命令码(如 0x09、0x93)
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 编解码格式 ​

decode 和 encode 字段使用 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.h 中 GetNetFnSupport 命令的定义:

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:8,manufacturer = {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_num、ChanType、HostId、interface_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"

验证部署 ​

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

检查服务状态:

```bash
ssh root@<BMC_IP> "systemctl status ipmi_mgmt"
```

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

查看服务日志确认命令注册:

```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通道名:bt、ipmb、edma、ipmbeth
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)
  • decode 中 0x1F: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.cpp 的 cmd_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"
  3. 验证服务状态:

    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
  2. 发送测试命令:

    bash
    ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw 0x30 0x90 0x1f
  3. 查看追踪日志:

    bash
    cat /tmp/ipmi.txt
  4. 关闭追踪:

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

常见问题排查 ​

  1. 返回 0xC1:检查 cmd_definition.h 和 cmd_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)

相关文档 ​