/* Copyright 2018 Intel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ipmbbridged.hpp" #include "ipmbdefines.hpp" #include "ipmbutils.hpp" #include #include #include #include #include #include #include #include #include #include /** * @brief Dbus */ static constexpr const char* ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb"; static constexpr const char* ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb"; static constexpr const char* ipmbDbusIntf = "org.openbmc.Ipmb"; boost::asio::io_context io; auto conn = std::make_shared(io); static std::list ipmbChannels; static const std::unordered_map ipmbChannelTypeMap = {{"me", ipmbChannelType::me}, {"ipmb", ipmbChannelType::ipmb}}; /** * @brief Ipmb request class methods */ IpmbRequest::IpmbRequest() { data.reserve(ipmbMaxDataSize); } IpmbRequest::IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun, uint8_t rqSA, uint8_t seq, uint8_t rqLun, uint8_t cmd, const std::vector& inputData) : address(address), netFn(netFn), rsLun(rsLun), rqSA(rqSA), seq(seq), rqLun(rqLun), cmd(cmd), timer(io) { data.reserve(ipmbMaxDataSize); state = ipmbRequestState::invalid; if (inputData.size() > 0) { data = std::move(inputData); } } void IpmbRequest::i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer, size_t bufferLength) { // constructing ipmb request from i2c buffer netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN); rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN); rqSA = ipmbBuffer->Header.Req.rqSA; seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN); rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN); cmd = ipmbBuffer->Header.Req.cmd; size_t dataLength = bufferLength - (ipmbConnectionHeaderLength + ipmbRequestDataHeaderLength + ipmbChecksumSize); if (dataLength > 0) { data.insert(data.end(), ipmbBuffer->Header.Req.data, &ipmbBuffer->Header.Req.data[dataLength]); } } int IpmbRequest::ipmbToi2cConstruct(std::vector& buffer) { /* Add one byte for length byte as per required by driver */ size_t bufferLength = 1 + data.size() + ipmbRequestDataHeaderLength + ipmbConnectionHeaderLength + ipmbChecksumSize; if (bufferLength > ipmbMaxFrameLength) { return -1; } buffer.resize(bufferLength); static_assert(ipmbMaxFrameLength >= sizeof(IPMB_HEADER)); IPMB_PKT* ipmbPkt = reinterpret_cast(buffer.data()); ipmbPkt->len = bufferLength - 1; IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr); // constructing buffer from ipmb request ipmbBuffer->Header.Req.address = address; ipmbBuffer->Header.Req.rsNetFnLUN = ipmbNetFnLunSet(netFn, rsLun); ipmbBuffer->Header.Req.rqSA = rqSA; ipmbBuffer->Header.Req.rqSeqLUN = ipmbSeqLunSet(seq, rqLun); ipmbBuffer->Header.Req.cmd = cmd; ipmbBuffer->Header.Req.checksum1 = ipmbChecksumCompute( (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize); if (data.size() > 0) { std::copy(data.begin(), data.end(), ipmbBuffer->Header.Req.data); } buffer[bufferLength - ipmbChecksumSize] = ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset, (ipmbRequestDataHeaderLength + data.size())); return 0; } std::tuple> IpmbRequest::returnMatchedResponse() { return std::make_tuple( static_cast(ipmbResponseStatus::success), matchedResponse->netFn, matchedResponse->rsLun, matchedResponse->cmd, matchedResponse->completionCode, matchedResponse->data); } static std::tuple> returnStatus(ipmbResponseStatus status) { // we only want to send status here, other fields are not relevant return std::make_tuple(static_cast(status), 0, 0, 0, 0, std::vector(0)); } /** * @brief Ipmb response class methods */ IpmbResponse::IpmbResponse() { data.reserve(ipmbMaxDataSize); } IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun, uint8_t rsSA, uint8_t seq, uint8_t rsLun, uint8_t cmd, uint8_t completionCode, const std::vector& inputData) : address(address), netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd), completionCode(completionCode) { data.reserve(ipmbMaxDataSize); if (inputData.size() > 0) { data = std::move(inputData); } } void IpmbResponse::i2cToIpmbConstruct(IPMB_HEADER* ipmbBuffer, size_t bufferLength) { netFn = ipmbNetFnGet(ipmbBuffer->Header.Resp.rqNetFnLUN); rqLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Resp.rqNetFnLUN); rsSA = ipmbBuffer->Header.Resp.rsSA; seq = ipmbSeqGet(ipmbBuffer->Header.Resp.rsSeqLUN); rsLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Resp.rsSeqLUN); cmd = ipmbBuffer->Header.Resp.cmd; completionCode = ipmbBuffer->Header.Resp.completionCode; size_t dataLength = bufferLength - (ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength + ipmbChecksumSize); if (dataLength > 0) { data.insert(data.end(), ipmbBuffer->Header.Resp.data, &ipmbBuffer->Header.Resp.data[dataLength]); } } std::shared_ptr> IpmbResponse::ipmbToi2cConstruct() { /* Add one byte for length byte as per required by driver */ size_t bufferLength = 1 + data.size() + ipmbResponseDataHeaderLength + ipmbConnectionHeaderLength + ipmbChecksumSize; if (bufferLength > ipmbMaxFrameLength) { return nullptr; } std::shared_ptr> buffer = std::make_shared>(bufferLength); IPMB_PKT* ipmbPkt = reinterpret_cast(buffer->data()); ipmbPkt->len = bufferLength - 1; IPMB_HEADER* ipmbBuffer = &(ipmbPkt->hdr); ipmbBuffer->Header.Resp.address = address; ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun); ipmbBuffer->Header.Resp.rsSA = rsSA; ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun); ipmbBuffer->Header.Resp.cmd = cmd; ipmbBuffer->Header.Resp.completionCode = completionCode; ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute( (uint8_t*)ipmbBuffer, ipmbConnectionHeaderLength - ipmbChecksumSize); if (data.size() > 0) { std::copy(data.begin(), data.end(), ipmbBuffer->Header.Resp.data); } (*buffer)[bufferLength - ipmbChecksumSize] = ipmbChecksumCompute((uint8_t*)ipmbBuffer + ipmbChecksum2StartOffset, (ipmbResponseDataHeaderLength + data.size())); return buffer; } bool IpmbCommandFilter::isBlocked(const uint8_t reqNetFn, const uint8_t cmd) { auto blockedCmd = unhandledCommands.find({reqNetFn, cmd}); if (blockedCmd != unhandledCommands.end()) { return true; } return false; } void IpmbCommandFilter::addFilter(const uint8_t reqNetFn, const uint8_t cmd) { if (unhandledCommands.insert({reqNetFn, cmd}).second) { phosphor::logging::log( "addFilter: added command to filter", phosphor::logging::entry("netFn = %d", reqNetFn), phosphor::logging::entry("cmd = %d", cmd)); } } /** * @brief Ipmb channel */ void IpmbChannel::ipmbSendI2cFrame(std::shared_ptr> buffer, size_t retriesAttempted = 0) { IPMB_PKT* ipmbPkt = reinterpret_cast(buffer->data()); uint8_t targetAddr = ipmbIsResponse(&(ipmbPkt->hdr)) ? ipmbPkt->hdr.Header.Resp.address : ipmbPkt->hdr.Header.Req.address; boost::asio::async_write(i2cTargetDescriptor, boost::asio::buffer(*buffer), [this, buffer, retriesAttempted, targetAddr](const boost::system::error_code& ec, size_t /* bytesSent */) { if (ec) { size_t currentRetryCnt = retriesAttempted; if (currentRetryCnt > ipmbI2cNumberOfRetries) { std::string msgToLog = "ipmbSendI2cFrame: send to I2C failed after retries." " busId=" + std::to_string(ipmbBusId) + ", targetAddr=" + std::to_string(targetAddr) + ", error=" + ec.message(); phosphor::logging::log( msgToLog.c_str()); return; } currentRetryCnt++; ipmbSendI2cFrame(buffer, currentRetryCnt); } }); } /** * @brief Ipmb Outstanding Requests */ void IpmbChannel::makeRequestInvalid(IpmbRequest& request) { // change request state to invalid and remove it from outstanding requests // list request.state = ipmbRequestState::invalid; outstandingRequests[request.seq] = nullptr; } void IpmbChannel::makeRequestValid(std::shared_ptr request) { // change request state to valid and add it to outstanding requests list request->state = ipmbRequestState::valid; outstandingRequests[request->seq] = request; } bool IpmbChannel::seqNumGet(uint8_t& seq) { static uint8_t seqNum = 0; for (int i = 0; i < ipmbMaxOutstandingRequestsCount; i++) { seqNum = (seqNum + 1) % ipmbMaxOutstandingRequestsCount; if (outstandingRequests[seqNum] == nullptr) { seq = seqNum; return true; } } return false; } void IpmbChannel::responseMatch(std::unique_ptr& response) { std::shared_ptr request = outstandingRequests[response->seq]; if (request != nullptr) { if (((ipmbRespNetFn(request->netFn)) == (response->netFn)) && ((request->rqLun) == (response->rqLun)) && ((request->rsLun) == (response->rsLun)) && ((request->cmd) == (response->cmd))) { // match, response is corresponding to previously sent request request->state = ipmbRequestState::matched; request->timer->cancel(); request->matchedResponse = std::move(response); } } } void IpmbChannel::processI2cEvent() { std::array buffer{}; IPMB_PKT* ipmbPkt = reinterpret_cast(buffer.data()); IPMB_HEADER* ipmbFrame = &(ipmbPkt->hdr); lseek(ipmbi2cTargetFd, 0, SEEK_SET); ssize_t r = read(ipmbi2cTargetFd, buffer.data(), ipmbMaxFrameLength); // Handle error cases. if (r < 0) { goto end; } /* Subtract first byte len size from total frame length */ r--; if ((r < ipmbMinFrameLength) || (r > ipmbMaxFrameLength)) { goto end; } // validate the frame if (!isFrameValid(ipmbFrame, r)) { goto end; } // if it is message received from ipmb channel, send out dbus signal if (getChannelType() == ipmbChannelType::ipmb) { auto ipmbMessageReceived = IpmbRequest(); ipmbMessageReceived.i2cToIpmbConstruct(ipmbFrame, r); sdbusplus::message_t msg = conn->new_signal(ipmbObj, ipmbDbusIntf, "receiveBroadcast"); msg.append(ipmbMessageReceived.netFn, ipmbMessageReceived.cmd, ipmbMessageReceived.data); msg.signal_send(); } // 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); } else { // 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, ipmbBmcTargetAddress, 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 bmcTargetAddress = getBmcTargetAddress(); if (payload.size() > ipmbMaxDataSize) { phosphor::logging::log( "processI2cEvent: response exceeding maximum size"); // prepare generic response auto ipmbResponse = IpmbResponse( address, netfn, rqLun, bmcTargetAddress, 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, bmcTargetAddress, 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); } end: i2cTargetDescriptor.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(); }); } IpmbChannel::IpmbChannel(boost::asio::io_context& io, uint8_t ipmbBmcTargetAddress, uint8_t ipmbRqTargetAddress, uint8_t channelIdx, std::shared_ptr commandFilter) : i2cTargetDescriptor(io), ipmbBmcTargetAddress(ipmbBmcTargetAddress), ipmbRqTargetAddress(ipmbRqTargetAddress), channelIdx(channelIdx), commandFilter(commandFilter) {} int IpmbChannel::ipmbChannelInit(const char* ipmbI2cTarget) { // extract bus id from target path and save std::string ipmbI2cTargetStr(ipmbI2cTarget); auto findHyphen = ipmbI2cTargetStr.find("-"); std::string busStr = ipmbI2cTargetStr.substr(findHyphen + 1); try { ipmbBusId = std::stoi(busStr); } catch (const std::invalid_argument&) { phosphor::logging::log( "ipmbChannelInit: invalid bus id in target-path config"); return -1; } // Check if sysfs has device. If not, enable I2C target driver by command // echo "ipmb-dev 0x1010" > /sys/bus/i2c/devices/i2c-0/new_device bool hasSysfs = std::filesystem::exists(ipmbI2cTarget); if (!hasSysfs) { std::string deviceFileName = "/sys/bus/i2c/devices/i2c-" + busStr + "/new_device"; std::string para = "ipmb-dev 0x1010"; // init with BMC addr 0x20 std::fstream deviceFile; deviceFile.open(deviceFileName, std::ios::out); if (!deviceFile.good()) { phosphor::logging::log( "ipmbChannelInit: error opening deviceFile"); return -1; } deviceFile << para; deviceFile.close(); } // open fd to i2c target device for read write ipmbi2cTargetFd = open(ipmbI2cTarget, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (ipmbi2cTargetFd < 0) { phosphor::logging::log( "ipmbChannelInit: error opening ipmbI2cTarget"); return -1; } i2cTargetDescriptor.assign(ipmbi2cTargetFd); i2cTargetDescriptor.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(); }); return 0; } int IpmbChannel::ipmbChannelUpdateTargetAddress(const uint8_t newBmcTargetAddr) { if (ipmbi2cTargetFd > 0) { i2cTargetDescriptor.close(); close(ipmbi2cTargetFd); ipmbi2cTargetFd = 0; } // disable old I2C target driver by command: // echo "0x1010" > /sys/bus/i2c/devices/i2c-0/delete_device std::string deviceFileName; std::string para; std::fstream deviceFile; deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/delete_device"; para = "0x1010"; // align with removed ipmb0 definition in dts file deviceFile.open(deviceFileName, std::ios::out); if (!deviceFile.good()) { phosphor::logging::log( "ipmbChannelUpdateTargetAddress: error opening deviceFile to delete " "sysfs"); return -1; } deviceFile << para; deviceFile.close(); // enable new I2C target driver by command: // echo "ipmb-dev 0x1012" > /sys/bus/i2c/devices/i2c-0/new_device deviceFileName = "/sys/bus/i2c/devices/i2c-" + std::to_string(ipmbBusId) + "/new_device"; std::ostringstream hex; uint16_t addr = 0x1000 + (newBmcTargetAddr >> 1); hex << std::hex << static_cast(addr); const std::string& addressHexStr = hex.str(); para = "ipmb-dev 0x" + addressHexStr; deviceFile.open(deviceFileName, std::ios::out); if (!deviceFile.good()) { phosphor::logging::log( "ipmbChannelUpdateTargetAddress: error opening deviceFile to create " "sysfs"); return -1; } deviceFile << para; deviceFile.close(); // open fd to i2c target device std::string ipmbI2cTargetStr = "/dev/ipmb-" + std::to_string(ipmbBusId); ipmbi2cTargetFd = open(ipmbI2cTargetStr.c_str(), O_RDWR | O_NONBLOCK); if (ipmbi2cTargetFd < 0) { phosphor::logging::log( "ipmbChannelInit: error opening ipmbI2cTarget"); return -1; } // start to receive i2c data as target i2cTargetDescriptor.assign(ipmbi2cTargetFd); i2cTargetDescriptor.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(); }); ipmbBmcTargetAddress = newBmcTargetAddr; return 0; } uint8_t IpmbChannel::getBusId() { return ipmbBusId; } uint8_t IpmbChannel::getBmcTargetAddress() { return ipmbBmcTargetAddress; } uint8_t IpmbChannel::getRqTargetAddress() { return ipmbRqTargetAddress; } uint8_t IpmbChannel::getDevIndex() { return channelIdx >> 2; } uint8_t IpmbChannel::getChannelIdx() { return channelIdx; } ipmbChannelType IpmbChannel::getChannelType() { return static_cast((channelIdx & 3)); } void IpmbChannel::addFilter(const uint8_t respNetFn, const uint8_t cmd) { if (commandFilter) { commandFilter->addFilter(respNetFn, cmd); } } std::tuple> IpmbChannel::requestAdd(boost::asio::yield_context& yield, std::shared_ptr request) { makeRequestValid(request); std::vector buffer(0); if (request->ipmbToi2cConstruct(buffer) != 0) { return returnStatus(ipmbResponseStatus::error); } for (int i = 0; i < ipmbNumberOfTries; i++) { boost::system::error_code ec; int i2cRetryCnt = 0; for (; i2cRetryCnt < ipmbI2cNumberOfRetries; i2cRetryCnt++) { boost::asio::async_write(i2cTargetDescriptor, boost::asio::buffer(buffer), yield[ec]); if (ec) { continue; // retry } break; } if (i2cRetryCnt == ipmbI2cNumberOfRetries) { std::string msgToLog = "requestAdd: Sent to I2C failed after retries." " busId=" + std::to_string(ipmbBusId) + ", error=" + ec.message(); phosphor::logging::log( msgToLog.c_str()); } request->timer->expires_after( std::chrono::milliseconds(ipmbRequestRetryTimeout)); request->timer->async_wait(yield[ec]); if (ec && ec != boost::asio::error::operation_aborted) { // unexpected error - invalidate request and return generic error phosphor::logging::log( "requestAdd: async_wait error"); makeRequestInvalid(*request); return returnStatus(ipmbResponseStatus::error); } if (request->state == ipmbRequestState::matched) { // matched response, send it to client application makeRequestInvalid(*request); return request->returnMatchedResponse(); } } makeRequestInvalid(*request); return returnStatus(ipmbResponseStatus::timeout); } static IpmbChannel* getChannel(uint8_t reqChannel) { auto channel = std::find_if(ipmbChannels.begin(), ipmbChannels.end(), [reqChannel](IpmbChannel& channel) { return channel.getChannelIdx() == reqChannel; }); if (channel != ipmbChannels.end()) { return &(*channel); } return nullptr; } static int initializeChannels() { std::shared_ptr commandFilter = std::make_shared(); constexpr const char* configFilePath = "/usr/share/ipmbbridge/ipmb-channels.json"; std::ifstream configFile(configFilePath); if (!configFile.is_open()) { phosphor::logging::log( "initializeChannels: Cannot open config path"); return -1; } try { uint8_t devIndex = 0; auto data = nlohmann::json::parse(configFile, nullptr); for (const auto& channelConfig : data["channels"]) { const std::string& typeConfig = channelConfig["type"]; const std::string& targetPath = channelConfig["slave-path"]; uint8_t bmcAddr = channelConfig["bmc-addr"]; uint8_t reqAddr = channelConfig["remote-addr"]; ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig); if (channelConfig.contains("devIndex")) { devIndex = channelConfig["devIndex"]; } auto channel = ipmbChannels.emplace( ipmbChannels.end(), io, bmcAddr, reqAddr, ((devIndex << 2) | static_cast(type)), commandFilter); if (channel->ipmbChannelInit(targetPath.c_str()) < 0) { phosphor::logging::log( "initializeChannels: channel initialization failed"); return -1; } } } catch (const nlohmann::json::exception& e) { phosphor::logging::log( "initializeChannels: Error parsing config file"); return -1; } catch (const std::out_of_range& e) { phosphor::logging::log( "initializeChannels: Error invalid type"); return -1; } return 0; } auto ipmbHandleRequest = [](boost::asio::yield_context yield, uint8_t reqChannel, uint8_t netfn, uint8_t lun, uint8_t cmd, std::vector dataReceived) { IpmbChannel* channel = getChannel(reqChannel); if (channel == nullptr) { phosphor::logging::log( "ipmbHandleRequest: requested channel does not exist"); return returnStatus(ipmbResponseStatus::invalid_param); } // check outstanding request list for valid sequence number uint8_t seqNum = 0; bool seqValid = channel->seqNumGet(seqNum); if (!seqValid) { phosphor::logging::log( "ipmbHandleRequest: cannot add more requests to the list"); return returnStatus(ipmbResponseStatus::busy); } uint8_t bmcTargetAddress = channel->getBmcTargetAddress(); uint8_t rqTargetAddress = channel->getRqTargetAddress(); // construct the request to add it to outstanding request list std::shared_ptr request = std::make_shared( rqTargetAddress, netfn, ipmbRsLun, bmcTargetAddress, seqNum, lun, cmd, dataReceived); if (!request->timer) { phosphor::logging::log( "ipmbHandleRequest: timer object does not exist"); return returnStatus(ipmbResponseStatus::error); } return channel->requestAdd(yield, request); }; void addUpdateTargetAddrHandler() { // callback to handle dbus signal of updating target addr std::function updateTargetAddrHandler = [](sdbusplus::message_t& message) { uint8_t reqChannel, busId, targetAddr; // valid source of signal, check whether from multi-node manager std::string pathName = message.get_path(); if (pathName != "/xyz/openbmc_project/MultiNode/Status") { phosphor::logging::log( "addUpdateTargetAddrHandler: invalid obj path"); return; } message.read(reqChannel, busId, targetAddr); IpmbChannel* channel = getChannel(reqChannel); if (channel == nullptr || channel->getChannelType() != ipmbChannelType::ipmb) { phosphor::logging::log( "addUpdateTargetAddrHandler: invalid channel"); return; } if (busId != channel->getBusId()) { phosphor::logging::log( "addUpdateTargetAddrHandler: invalid busId"); return; } if (channel->getBmcTargetAddress() == targetAddr) { phosphor::logging::log( "addUpdateTargetAddrHandler: channel bmc target addr is " "unchanged, do nothing"); return; } channel->ipmbChannelUpdateTargetAddress(targetAddr); }; static auto match = std::make_unique( static_cast(*conn), "type='signal',member='updateBmcSlaveAddr',", updateTargetAddrHandler); } void addSendBroadcastHandler() { // callback to handle dbus signal of sending broadcast message std::function sendBroadcastHandler = [](sdbusplus::message_t& message) { uint8_t reqChannel, netFn, lun, cmd; std::vector dataReceived; message.read(reqChannel, netFn, lun, cmd, dataReceived); IpmbChannel* channel = getChannel(reqChannel); if (channel == nullptr) { phosphor::logging::log( "addSendBroadcastMsgHandler: requested channel does not " "exist"); return; } uint8_t bmcTargetAddress = channel->getBmcTargetAddress(); uint8_t seqNum = 0; // seqNum is not used in broadcast msg uint8_t targetAddr = broadcastAddress; std::shared_ptr request = std::make_shared( targetAddr, netFn, ipmbRsLun, bmcTargetAddress, seqNum, lun, cmd, dataReceived); std::shared_ptr> buffer = std::make_shared>(); if (request->ipmbToi2cConstruct(*buffer) != 0) { return; } channel->ipmbSendI2cFrame(buffer); }; static auto match = std::make_unique( static_cast(*conn), "type='signal',member='sendBroadcast',", sendBroadcastHandler); } /** * @brief Main */ int main() { 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(); if (initializeChannels() < 0) { phosphor::logging::log( "Error initializeChannels"); return -1; } addUpdateTargetAddrHandler(); addSendBroadcastHandler(); io.run(); return 0; }