关于OpenBmc如何添加未支持的IPMI命令
- ipmi标准命令的实现代码是在phosphor-ipmi-host中进行实现的。可以使用devtool modify phosphor-ipmi-host进行下载对应的模块。
phosphor-ipmi-host模块生成相应的服务
-
在ipmid-new.cpp文件中,main函数下,我们可以看到以下代码:
sdbusp->request_name("xyz.openbmc_project.Ipmi.Host"); auto server = sdbusplus::asio::object_server(sdbusp); auto iface = server.add_interface("/xyz/openbmc_project/Ipmi", "xyz.openbmc_project.Ipmi.Server"); iface->register_method("execute", ipmi::executionEntry); iface->initialize();
-
通过此处代码,生成了一个服务,其他模块可以通过服务名称:xyz.openbmc_project.Ipmi.Host;路径:/xyz/openbmc_project/Ipmi, xyz.openbmc_project.Ipmi.Server;方法:execute 来调用该服务。该服务调用的函数为ipmi::executionEntry。举例:在phosphor-ipmi-net 工程下,command_table.cpp文件下,Table::executeCommand函数中,通过以下代码进行了服务的调用:
bus->async_method_call(
[handler, this](const boost::system::error_code& ec,
const IpmiDbusRspType& response) {
if (!ec)
{
const uint8_t& cc = std::get<3>(response);
const std::vector<uint8_t>& responseData =
std::get<4>(response);
std::vector<uint8_t> payload;
payload.reserve(1 + responseData.size());
payload.push_back(cc);
payload.insert(payload.end(), responseData.begin(),
responseData.end());
handler->outPayload = std::move(payload);
}
else
{
std::vector<uint8_t> payload;
payload.push_back(IPMI_CC_UNSPECIFIED_ERROR);
handler->outPayload = std::move(payload);
}
},
"xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
"xyz.openbmc_project.Ipmi.Server", "execute", netFn, lun, cmd,
commandData, options);
ipmi::executionEntry函数的调用细节
- executionEntry [ipmid-new.cpp]
->executeIpmiCommand(request) [ipmid-new.cpp]
->executeIpmiCommandCommon [ipmid-new.cpp] [此函数通过netfn进行选择], 在该函数中,通过netfn 与cmd 组成的
关键字,在handlerMap中进行查找,找到注册上去的回调函数,并且执行回调函数。因此,在函数的开始,我们需要将实现ipmi
协议的函数注册在handlerMap中即可。
如何将实现报文的函数进行注册
-
举例:在apphandler.cpp文件中,void register_netfn_app_function()函数下,我们注册了netfn 为 APP的回调函数。注:register_netfn_app_function函数仅仅只有声明和定义,但是并未进行调用。该函数的声明为:
`void register_netfn_app_functions() __attribute__((constructor));`
后缀标识为此函数在main函数执行之前要进行调用。
-
我们以函数ipmi::registerHandler为例,讲解如何注册回调函数。
registerHandler函数的实现在include/ipmid/handler.hpp文件中进行实现,该函数在此调用了两个函数:1、ipmi::makeHandler(std::forward<Handler>(handler)),该函数的作用是生成一个handler的函数指针。此函数非常重要,涉及到 回调函数参数的设定。进入该函数,发现生成HandlerBase::ptr的函数指针。该函数指针调用的是 template <typename Handler> class IpmiHandler final : public HandlerBase 的重载函数 在 HandlerBase类型下,会调用call方法来执行回调函数,所以我们需要重点查看executeCallback(request)函数。 该函数下,我们可以看到如下的注释: /* callbacks can contain an optional first argument of one of: * 1) boost::asio::yield_context * 2) ipmi::Context::ptr * 3) ipmi::message::Request::ptr * * If any of those is part of the callback signature as the first * argument, it will automatically get packed into the parameter pack * here. * * One more special optional argument is an ipmi::message::Payload. * This argument can be in any position, though logically it makes the * most sense if it is the last. If this class is included in the * handler signature, it will allow for the handler to unpack optional * parameters. For example, the Set LAN Configuration Parameters * command takes variable length (and type) values for each of the LAN * parameters. This means that the only fixed data is the channel and * parameter selector. All the remaining data can be extracted using * the Payload class and the unpack API available to the Payload class. */ 回调函数的形参,第一个参数可以是boost::asio::yield_context, ipmi::Context::ptr, ipmi::message::Request::ptr 后面跟着ipmi报文中私有数据区的变量,或者形参直接为ipmi报文中私有数据区的变量。 2、impl::registerHandler函数的作用为根据 netfn以及cmd生成关键字,将handler函数注册到handlerMap中。
以netfn=chassis ,cmd = 0xa为例,进行回调函数参数说明:
- 该回调函数的实现是在chassishandler.cpp中
ipmi::RspType<> ipmiSetFrontPanelButtonEnables(ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton,
bool disableDiagButton, bool disableSleepButton, uint4_t reserved)
-
可以看到,形参列表中的第一个参数为ipmi::Context::ptr,后面的参数分别与该命令的私有变量相对应。
附:当我们要实现一个phosphor-ipmi-host中未实现的命令时,建议可以在openbmc中的oem中进行查找该命令是否实现