目录
代码分析
配置IPMB功能
在设备树中配置:
配置ipmb-channels.json文件
在ipmbbridged.cpp文件main函数下,我们通过以下代码对函数实现的方法进行进行了注册,该方法的回调函数为 “ipmbHandleRequest”。
conn->request_name(ipmbBus);
auto server = sdbusplus::asio::object_server(conn);
std::shared_ptr ipmbIface =
server.add_interface(ipmbObj, ipmbDbusIntf);
ipmbIface->register_method("sendRequest", std::move(ipmbHandleRequest));
ipmbIface->initialize();
接着,通过initializeChannels函数对模块进行了初始化。该函数是读取/usr/share/ipmbbridge/ipmb-channels.json文件的内容来进行对class IpmbChannel 进行构造。
通过下面代码,可以看到,在/dev/ipmb-N得到新的数据时,将会调用processI2cEvent()函数来进行处理。
i2cSlaveDescriptor.assign(ipmbi2cSlaveFd);
i2cSlaveDescriptor.async_wait(
boost::asio::posix::descriptor_base::wait_read,
[this](const boost::system::error_code &ec) {
if (ec)
{
phosphor::logging::log(
"Error: processI2cEvent()");
return;
}
processI2cEvent();
});
通过对processI2cEvent函数的分析,可以看到该函数会对接收到的报文进行各种解析,包括接收到报文字节的长度,报文头是否正确等等。当从机是在向openbmc回复应答报文时,该报文是通过以下case进行返回的。
// copy frame to ipmib message buffer
if (ipmbIsResponse(ipmbFrame))
{
std::unique_ptr ipmbMessageReceived =
std::make_unique();
ipmbMessageReceived->i2cToIpmbConstruct(ipmbFrame, r);
// try to match response with outstanding request
responseMatch(ipmbMessageReceived);
}
如果是其他板卡向openbmc发送报文时,则是通过以下代码。分析该代码可知,在判定netfn与cmd合法之后,报文将送到phoshpor-ipmi-host中对应的函数中进行处理。
// if command is blocked - respond with 'invalid command'
// completion code
if (commandFilter)
{
uint8_t netFn = ipmbNetFnGet(ipmbFrame->Header.Req.rsNetFnLUN);
uint8_t cmd = ipmbFrame->Header.Req.cmd;
uint8_t rqSA = ipmbFrame->Header.Req.rqSA;
if (commandFilter->isBlocked(netFn, cmd))
{
uint8_t seq = ipmbSeqGet(ipmbFrame->Header.Req.rqSeqLUN);
uint8_t lun =
ipmbLunFromSeqLunGet(ipmbFrame->Header.Req.rqSeqLUN);
// prepare generic response
auto ipmbResponse = IpmbResponse(
rqSA, ipmbRespNetFn(netFn), lun, ipmbBmcSlaveAddress, seq,
ipmbRsLun, cmd, ipmbIpmiInvalidCmd, {});
auto buffer = ipmbResponse.ipmbToi2cConstruct();
if (buffer)
{
ipmbSendI2cFrame(buffer);
}
goto end;
}
}
auto ipmbMessageReceived = IpmbRequest();
ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r);
int devId = getDevIndex();
std::map> options{
{"rqSA", ipmbAddressTo7BitSet(ipmbMessageReceived.rqSA)},
{"hostId", devId}};
using IpmiDbusRspType = std::tuple>;
conn->async_method_call(
[this, rqLun{ipmbMessageReceived.rqLun},
seq{ipmbMessageReceived.seq}, address{ipmbMessageReceived.rqSA}](
const boost::system::error_code &ec,
const IpmiDbusRspType &response) {
const auto &[netfn, lun, cmd, cc, payload] = response;
if (ec)
{
phosphor::logging::log(
"processI2cEvent: error getting response from IPMI");
return;
}
uint8_t bmcSlaveAddress = getBmcSlaveAddress();
if (payload.size() > ipmbMaxDataSize)
{
phosphor::logging::log(
"processI2cEvent: response exceeding maximum size");
// prepare generic response
auto ipmbResponse = IpmbResponse(
address, netfn, rqLun, bmcSlaveAddress, seq, ipmbRsLun,
cmd, ipmbIpmiCmdRespNotProvided, {});
auto buffer = ipmbResponse.ipmbToi2cConstruct();
if (buffer)
{
ipmbSendI2cFrame(buffer);
}
return;
}
if (!(netfn & ipmbNetFnResponseMask))
{
// we are not expecting request here
phosphor::logging::log(
"processI2cEvent: got a request instead of response");
return;
}
// if command is not supported, add it to filter
if (cc == ipmbIpmiInvalidCmd)
{
addFilter(ipmbReqNetFnFromRespNetFn(netfn), cmd);
}
// payload is empty after constructor invocation
auto ipmbResponse =
IpmbResponse(address, netfn, rqLun, bmcSlaveAddress, seq,
lun, cmd, cc, payload);
auto buffer = ipmbResponse.ipmbToi2cConstruct();
if (!buffer)
{
phosphor::logging::log(
"processI2cEvent: error constructing a request");
return;
}
ipmbSendI2cFrame(buffer);
},
"xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
"xyz.openbmc_project.Ipmi.Server", "execute",
ipmbMessageReceived.netFn, ipmbMessageReceived.rsLun,
ipmbMessageReceived.cmd, ipmbMessageReceived.data, options);
}
在函数ipmbHandleRequest中,我们是进行将报文发送,并对processI2cEvent处理之后的结果进行分析,如果报文分析正常的话,我们将通过以下代码将子板中的数据返回至方法调用处。
if (request->state == ipmbRequestState::matched)
{
// matched response, send it to client application
makeRequestInvalid(*request);
return request->returnMatchedResponse();
}
我是在phosphor-ipmi-host中对该方法进行的调用,具体调用代码如下:
int sendIpmbCmd(uint8_t netFn, uint8_t cmd, uint8_t bicAddr,
std::vector& cmdData, std::vector& respData)
{
static constexpr uint8_t lun = 0;
auto bus = getSdBus();
auto method = bus->new_method_call("xyz.openbmc_project.Ipmi.Channel.Ipmb",
"/xyz/openbmc_project/Ipmi/Channel/Ipmb",
"org.openbmc.Ipmb", "sendRequest");
method.append(bicAddr, netFn, lun, cmd, cmdData);
auto reply = bus->call(method);
if (reply.is_method_error())
{
phosphor::logging::log(
"Error reading from BIC");
return -1;
}
IpmbMethodType resp;
reply.read(resp);
respData =
std::move(std::get>(resp));
return 0;
}
&i2c1 {
/* PCIe slot 2 (x16) */
status = "okay";
multi-master;
ipmb1@10 {
compatible = "ipmb-dev";
reg = <0x10 | I2C_OWM_SLAVE_ADDRESS>;
i2c-protocol;
};
};
其中openbmc中i2C的从机地址为0x10,该从机地址可以通过devmem 0x1e78a058进行查看
键入命令:bitbake linux-aspeed -c menuconfig
按下“/” 并键入ipmb进行查看。
在device driver 下进入character device 将IPMI top-level message handler进行勾选,将IPMB Interface handler 进行勾选
保存退出即可;
{
"channels": [
{
"type": "me",
"slave-path": "/dev/ipmb-1",
"bmc-addr": 32,
"remote-addr": 176
}
]
}
remote-addr为子板卡 (从机地址 << 1);