接口定制指南-IPMI 接口定制
概述
IPMI(Intelligent Platform Management Interface)是 BMC 与外部管理软件之间的重要通信接口。openUBMC 的 IPMI 协议栈由 ipmi_mgmt 组件实现,负责传输层、命令路由和核心命令处理,业务组件通过注册 bmc.kepler.CmdInfo 接口的 Process 方法实现自定义 IPMI 命令的处理逻辑。
本文以 ‘ipmi_mgmt' 组件为例,面向部件开发者和整机开发者,系统说明 IPMI 接口定制的完整步骤和调试方法,覆盖从命令定义、代码实现、编译部署到调试排障的所有流程。
定制流程总览:
- 定制前准备:确认代码仓、工具和环境满足要求
- 命令定义:在
cmd_definition.h中通过命名空间和mc::app::ipmi_cmd结构体定义命令的 NetFn、Cmd、编解码格式等属性 - 代码实现:在
cmd_handler.h/cpp中实现处理器函数,在cmd_registry.cpp中注册命令 - 编译部署:通过
bingo构建并部署到 BMC - 调试验证:使用 ipmitool 发送命令验证,通过日志和 TraceIpmi 排查问题
适用场景:
- 在
ipmi_mgmt中新增标准 IPMI 命令(NetFn 0x06 App 等)的处理逻辑 - 新增 OEM IPMI 命令(NetFn 0x30~0x3F)以扩展设备管理能力
- 修改已有 IPMI 命令的请求/响应数据结构
- 调试 IPMI 命令的交互过程和定位通信问题
IPMI 接口定制步骤
定制前准备
代码仓
| 代码仓 | 说明 |
|---|---|
ipmi_mgmt | IPMI 协议栈组件,包含命令定义、处理器实现、传输层和过滤器链 |
工具
| 工具 | 用途 | 安装方式 |
|---|---|---|
bingo | openUBMC 构建工具,用于编译 | 参见《环境准备简介》 |
ipmitool | IPMI 命令行测试工具,用于发送和验证 IPMI 命令 | sudo apt install ipmitool |
环境要求
- 已完成 openUBMC 开发环境搭建
- 已克隆
ipmi_mgmt代码仓并能正常编译 - 测试环境具备 BMC 管理口网络连通性(远程测试)或 BMC 本地访问权限(本地测试)
前置知识
建议阅读以下文档:
- IPMI 的 OEM 命令格式:了解 OEM 命令分类和厂商 ID 规则
- 命令使用方式:了解 ipmitool 命令语法
- 命令约束:了解消息长度和性能限制
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 | 十六进制 | 厂商名称 |
|---|---|---|
| 2011 | 0x7db | HUAWEI Technology Co., Ltd. |
| 19621 | 0x4ca5 | Alibaba |
厂商 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 的转换。
命令定义结构体字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
netfn | uint8_t | 是 | 网络功能码(如 0x06、0x30) |
cmd | uint8_t | 是 | 命令码(如 0x09、0x93) |
name | const char* | 是 | 命令名称字符串 |
decode | const char* | 是 | 请求 BitString 编解码格式,空字符串表示无请求体 |
encode | const char* | 是 | 响应 BitString 编解码格式 |
filter | const char* | 是 | OEM 子命令过滤模式,空字符串表示不过滤 |
role | int16_t | 是 | 用户角色要求 |
privilege | uint32_t | 是 | 权限要求 |
priority | int16_t | 是 | 命令优先级 |
sys_locked_policy | const char* | 是 | 系统锁定策略:"Allowed" 或 "Forbidden" |
sensitive | bool | 是 | 是否为敏感命令(追踪时掩码处理) |
restricted_channels | 初始化列表 | 是 | 受限通道列表,空列表表示无限制 |
manufacturer | int[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 可选值
| 角色 | 值 | 说明 |
|---|---|---|
| OEM | 5 | OEM 权限 |
| Administrator | 4 | 管理员权限 |
| Operator | 3 | 操作员权限 |
| User | 2 | 普通用户权限 |
| Callback | 1 | 回调权限 |
| Unspecified | 0 | 未指定 |
privilege 可选值
| 权限 | 说明 |
|---|---|
| ReadOnly | 只读权限,适用于查询类命令 |
| DiagnoseMgmt | 诊断管理权限 |
| SecurityMgmt | 安全管理权限 |
| BasicSetting | 基础设置权限 |
| UserMgmt | 用户管理权限 |
| PowerMgmt | 电源管理权限 |
| VMMMgmt | 虚拟媒体管理权限 |
| KVMMgmt | KVM 管理权限 |
| ConfigureSelf | 自配置权限 |
priority 可选值
| 优先级 | 值 | 说明 |
|---|---|---|
| Max | 50 | 最高优先级 |
| EndUser | 40 | 终端用户 |
| OBM | 35 | OBM 厂商 |
| ODM | 30 | ODM 厂商 |
| OEM | 20 | OEM 厂商 |
| Default | 10 | 默认优先级 |
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 命令的定义:
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 命令的典型模式:
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 BWListAddDelOEM 命令定义要点:
- 厂商 ID 字段:
decode中前 3 字节为ManufactureId:3/unit:8,manufacturer = {0, 1}指示厂商 ID 位于字节 0~2 - 子命令区分:
filter = "*,*,*,3f"表示第 4 字节必须为 0x3F,用于区分共用 NetFn 0x30/Cmd 0x93 的不同 OEM 子命令 - 固定值字节:
decode中的0x3f:1/unit:8定义固定值字段,编解码时自动校验和填充 - 厂商 ID 字节序:厂商 ID 在 IPMI 报文中为 3 字节小端序(如 0x7db 对应字节序列
db 07 00)
代码实现
命令处理器开发
命令处理器(handler)是 IPMI 命令的业务处理入口,遵循统一的函数签名。
处理器函数签名
mc::dict handler_name(mc::dict req, mc::dict ctx);req:BitString 解码后的请求字典,键名与decode中定义的字段名一致ctx:IPMI 上下文字典,包含通道信息(chan_num、ChanType、HostId、interface_id等)- 返回值:响应字典,键名与
encode中定义的字段名一致
处理器实现步骤
- 创建响应对象:调用
cmd_def::CommandName::rsp()构造带默认值的响应字典 - 设置 CompletionCode:成功为
0x00,异常使用对应完成码 - 处理业务逻辑:从请求字典中读取字段,执行业务操作
- 设置响应字段:填充响应数据
- 返回响应字典
标准命令处理器示例
以下为 reset_watchdog_timer 处理器的实现:
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 校验的典型模式:
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 命令处理器要点:
- 厂商 ID 校验:使用
req["ManufactureId"].as_uint32()读取解码后的厂商 ID,与MANUFACTURE_ID(0x0007db)比对,不匹配返回 0xCC - 请求字段访问:通过
req["FieldName"].as_uint8()/as_uint32()/as_string()按类型访问 - 响应字段设置:通过
rsp["FieldName"] = value设置响应字段 - 操作日志:使用
operation_log()记录关键操作,使用elog()记录错误
声明处理器
在 src/ipmi_cmd/cmd_handler.h 中声明处理器函数:
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,不经 RPC | SDR/SEL 等需本组件直接处理的命令 |
注册到标准路径:
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},
};注册到快捷路径:
static cmd_entry quick_cmd_registry[] = {
{cmd_def::ReserveDeviceSDR::cmd, cmd_handler::ipmi_reserve_device_sdr},
// 需本进程直接处理的命令
};两条路径的注册均在 register_cmds() 函数中完成,于 on_start() 阶段调用:
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 中:
| 选项 | 默认值 | 说明 |
|---|---|---|
tests | true | 构建测试用例 |
enable_coverage | false | 启用代码覆盖率 |
enable_dft | true | 启用 DFT 产测能力 |
chipv2_enable | false | 启用 chip v2 |
enable_bt | true | 启用 BT 传输装配 |
enable_ipmb | true | 启用 IPMB 传输装配 |
enable_ipmb_eth | true | 启用 IPMB-Eth 传输装配 |
enable_edma | true | 启用 EDMA 传输装配 |
部署注意事项
新增或修改 IPMI 命令后,需重启 ipmi_mgmt 服务以刷新 SHM 路由表使命令注册生效:
ssh root@<BMC_IP> "systemctl restart ipmi_mgmt"验证部署
部署完成后,通过以下方式验证命令是否注册成功:
- 检查服务状态:
ssh root@<BMC_IP> "systemctl status ipmi_mgmt"预期输出中服务状态为 active (running)。
- 查看服务日志确认命令注册:
ssh root@<BMC_IP> "journalctl -u ipmi_mgmt | grep 'register ipmi commands'"预期输出中包含 register ipmi commands 日志条目,表示命令注册完成。
调试方法
IPMI 命令测试工具
ipmitool
远程测试命令格式:
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw <netfn> <cmd> [data...]本地测试命令格式:
ipmitool raw <netfn> <cmd> [data...]参数说明:
| 参数 | 说明 |
|---|---|
-I lanplus | 使用 IPMI 2.0 LAN 接口(推荐) |
-I open | 本地 KCS 或 BT 接口 |
-H | BMC 的 IP 地址 |
-U | 用户名 |
-P | 密码 |
-C | 加密算法套件(推荐 17) |
raw | 发送原始 IPMI 命令 |
<netfn> | 网络功能码,需使用请求 NetFn(偶数),十六进制加 0x 前缀 |
<cmd> | 命令码,十六进制加 0x 前缀 |
[data...] | 命令数据字节,十六进制加 0x 前缀,空格分隔 |
详细语法参见: 命令使用方式
常见调试命令和日志查看方法
检查命令是否已注册
通过 GetNetFnSupport(0x06/0x09)命令查询已注册的 NetFn:
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> raw 0x06 0x09 0x0e 0x00通过 GetCommandSupport(0x06/0x0A)查询指定 NetFn 下已注册的命令:
# 查询 NetFn 0x30 下已注册的命令
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> raw 0x06 0x0a 0x0e 0x00 0x30 0x00 0x00查看 ipmi_mgmt 服务日志
# 查看最近日志
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 方法开启追踪:
# 追踪 BT 通道所有命令(NetFn=0xFF, Cmd=0xFF 表示不过滤)
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace true bt 255 255 "" file追踪指定命令
# 仅追踪 NetFn=0x30, Cmd=0x93 的命令
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace true bt 48 147 "" fileTrace 方法参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| EnableTrace | bool | 是否开启追踪 |
| Channel | string | 通道名:bt、ipmb、edma、ipmbeth |
| Netfn | uint8 | 目标 NetFn,0xFF 表示匹配任意 NetFn |
| Cmd | uint8 | 目标 Cmd,0xFF 表示匹配任意 Cmd |
| Filter | string | 原始过滤字节流(预留) |
| LogType | string | 日志输出方式:file 写文件,local 本地日志 |
查看追踪输出
追踪日志输出到 /tmp/ipmi.txt:
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 时自动截断重写
关闭追踪
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace false bt 0 0 "" ""注意
追踪功能默认关闭,仅在调试时开启。开启后会产生日志开销,调试完成后请及时关闭。
命令超时等常见问题的排查步骤
命令返回 0xC1(非法命令)
现象: 发送 IPMI 命令后返回完成码 0xC1
可能原因及排查步骤:
命令未注册:检查
cmd_definition.h是否正确定义,cmd_registry.cpp是否已注册路由表未刷新:重启 ipmi_mgmt 服务以刷新 SHM 路由表:
bashsystemctl restart ipmi_mgmt业务组件未运行:检查处理该命令的业务组件服务状态:
bashsystemctl status <component_name>NetFn/Cmd 不匹配:确认发送命令的 NetFn 和 Cmd 与定义一致
命令返回 0xC3(执行超时)
现象: 发送 IPMI 命令后返回完成码 0xC3
可能原因及排查步骤:
业务处理器执行超时:RPC 分发超时为 5 秒,检查处理器是否存在阻塞操作
业务组件无响应:检查业务组件是否正常处理 D-Bus 请求:
bashjournalctl -u <component_name> -fD-Bus 连接异常:检查 D-Bus 通信是否正常:
bashdbus-send --system --dest=<component_service> --print-reply <object_path> <interface>.<method>
命令返回 0xD4(权限不足)
现象: 发送 IPMI 命令后返回完成码 0xD4
可能原因及排查步骤:
防火墙拦截:命令被黑白名单拦截,检查防火墙策略:
bash# 查询防火墙状态 ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> raw 0x30 0x93 0xdb 0x07 0x00 0x4c用户权限不足:当前登录用户的角色或权限不满足命令定义中的
role/privilege要求命令被加入黑名单:检查命令是否被添加到自定义黑名单
命令返回 0xD5(命令不可用)
现象: 发送 IPMI 命令后返回完成码 0xD5
可能原因及排查步骤:
- 系统锁定:BMC 处于系统锁定状态,部分命令被限制执行
- 命令的 sys_locked_policy 设置为 Forbidden:确认该命令在系统锁定时是否允许执行
命令返回 0xCC(请求数据字段无效)
现象: 发送 IPMI 命令后返回完成码 0xCC
可能原因及排查步骤:
- 厂商 ID 不匹配:OEM 命令中厂商 ID 与 BMC 实际厂商 ID 不一致
- 请求数据格式错误:检查发送的数据字节序列是否与
decode定义的结构一致 - 子命令标识错误:OEM 命令中子命令字节与
filter定义不匹配
命令返回 0xC7(请求数据长度非法)
现象: 发送 IPMI 命令后返回完成码 0xC7
可能原因及排查步骤:
- 请求字节长度不正确:确认发送的数据字节数与
decode定义的结构总长度一致 - 缺少必要字段:检查是否遗漏了厂商 ID 或子命令等必要字段
命令无响应
现象: 发送 IPMI 命令后无任何响应
可能原因及排查步骤:
通道不通:检查网络连通性(远程)或 KCS/BT 驱动状态(本地)
ipmi_mgmt 服务异常:检查服务是否运行:
bashsystemctl status ipmi_mgmt消息长度超限:IPMB 最长 254 字节,BT 最长 252 字节,确认请求消息未超限
IPMI命令示例
以下以一个典型的 IPMI OEM 命令为例,展示从定义到调试的完整流程。
需求描述: 在 ipmi_mgmt 中新增一个 OEM 命令 GetDftMode,用于查询 DFT(产测)模式的启用状态和模式值。
步骤一:定义 IPMI 命令
在 src/ipmi_cmd/cmd_definition.h 中新增命令命名空间:
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为固定值字段,框架自动校验请求首字节为 0x1Fencode中定义响应包含 CompletionCode、Enabled 和 Mode 三个字段- 此命令不含厂商 ID,因此
manufacturer = {-1, -1}
步骤二:声明处理器
在 src/ipmi_cmd/cmd_handler.h 中声明:
mc::dict get_dft_mode(mc::dict req, mc::dict ctx);步骤三:实现处理器
在 src/ipmi_cmd/cmd_handler.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[] 数组中添加:
static cmd_entry cmd_registry[] = {
// 已有命令
{cmd_def::GetDftMode::cmd, cmd_handler::get_dft_mode},
};步骤五:编译部署
ssh root@<BMC_IP> "systemctl restart ipmi_mgmt"- 验证服务状态:
ssh root@<BMC_IP> "systemctl status ipmi_mgmt"预期服务状态为 active (running)。
步骤六:调试验证
发送测试命令
# 远程测试(首字节 0x1F 为子命令标识)
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw 0x30 0x90 0x1f预期响应:
00 01 00响应解读:
| 偏移 | 字节 | 含义 |
|---|---|---|
| 0 | 00 | CompletionCode(0x00 = 成功) |
| 1 | 01 | Enabled(0x01 = 已启用) |
| 2 | 00 | Mode(0x00 = 默认模式) |
使用追踪功能调试
如果命令未按预期响应,开启 TraceIpmi 追踪:
- 开启追踪:
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace true bt 48 255 "" file- 发送测试命令:
ipmitool -I lanplus -H <BMC_IP> -U <username> -P <password> -C 17 raw 0x30 0x90 0x1f- 查看追踪日志:
cat /tmp/ipmi.txt- 关闭追踪:
mdbctl call /bmc/kepler/Debug/IpmiCore/TraceIpmi bmc.kepler.Debug.IpmiCore.TraceIpmi.Trace false bt 0 0 "" ""常见问题排查
- 返回 0xC1:检查
cmd_definition.h和cmd_registry.cpp是否正确,重启 ipmi_mgmt 服务 - 返回 0xCC:检查子命令字节是否正确(GetDftMode 首字节应为 0x1F)
- 返回 0xC3:检查业务组件是否正常运行,处理器是否有阻塞操作
- 返回 0xD4:检查当前用户权限是否满足
DiagnoseMgmt要求
附录
完成码速查表
| 完成码 | 描述 |
|---|---|
| 0x00 | 命令执行成功 |
| 0xC1 | 非法命令(未注册或不支持) |
| 0xC3 | 执行超时 |
| 0xC7 | 请求数据长度非法 |
| 0xCC | 请求数据字段无效 |
| 0xD4 | 权限不足 |
| 0xD5 | 命令不可用(系统锁定等) |
| 0xFF | 未指定的错误 |
更多完成码参见 完成码定义。
消息长度约束
| 通道 | 最大消息长度 |
|---|---|
| IPMB | 254 字节(消息头 6 + 消息体 247 + 校验 1) |
| BT | 252 字节(消息头 4 + 消息体 247 + 校验 1) |
相关文档
- 接口定制:Redfish/CLI/SNMP/IPMI 接口定制总览
- IPMI 的 OEM 命令格式:OEM 命令分类和厂商 ID 规则
- 命令使用方式:ipmitool 命令语法
- 命令约束:消息长度和性能限制
- 完成码定义:IPMI 完成码列表
- 产品分类说明及其注意事项:接口修改流程和注意事项