|  | /* | 
|  | // Copyright (c) 2018 Intel Corporation | 
|  | // | 
|  | // 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 <bridgingcommands.hpp> | 
|  | #include <ipmid/api.hpp> | 
|  | #include <ipmid/utils.hpp> | 
|  | #include <manufacturingcommands.hpp> | 
|  | #include <phosphor-logging/log.hpp> | 
|  | #include <sdbusplus/bus.hpp> | 
|  | #include <sdbusplus/bus/match.hpp> | 
|  | #include <sdbusplus/message.hpp> | 
|  | #include <storagecommands.hpp> | 
|  | #include <user_channel/channel_layer.hpp> | 
|  |  | 
|  | #include <bitset> | 
|  | #include <cstring> | 
|  | #include <vector> | 
|  |  | 
|  | static constexpr const char* wdtService = "xyz.openbmc_project.Watchdog"; | 
|  | static constexpr const char* wdtInterface = | 
|  | "xyz.openbmc_project.State.Watchdog"; | 
|  | static constexpr const char* wdtObjPath = "/xyz/openbmc_project/watchdog/host0"; | 
|  | static constexpr const char* wdtInterruptFlagProp = | 
|  | "PreTimeoutInterruptOccurFlag"; | 
|  |  | 
|  | 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* ipmbIntf = "org.openbmc.Ipmb"; | 
|  |  | 
|  | static Bridging bridging; | 
|  | static bool eventMessageBufferFlag = false; | 
|  |  | 
|  | void Bridging::clearResponseQueue() | 
|  | { | 
|  | responseQueue.clear(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief utils for checksum | 
|  | */ | 
|  | static bool ipmbChecksumValidate(const uint8_t* data, uint8_t length) | 
|  | { | 
|  | if (data == nullptr) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | uint8_t checksum = 0; | 
|  |  | 
|  | for (uint8_t idx = 0; idx < length; idx++) | 
|  | { | 
|  | checksum += data[idx]; | 
|  | } | 
|  |  | 
|  | if (0 == checksum) | 
|  | { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static uint8_t ipmbChecksumCompute(uint8_t* data, uint8_t length) | 
|  | { | 
|  | if (data == nullptr) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | uint8_t checksum = 0; | 
|  |  | 
|  | for (uint8_t idx = 0; idx < length; idx++) | 
|  | { | 
|  | checksum += data[idx]; | 
|  | } | 
|  |  | 
|  | checksum = (~checksum) + 1; | 
|  | return checksum; | 
|  | } | 
|  |  | 
|  | static inline bool | 
|  | ipmbConnectionHeaderChecksumValidate(const ipmbHeader* ipmbHeader) | 
|  | { | 
|  | return ipmbChecksumValidate(reinterpret_cast<const uint8_t*>(ipmbHeader), | 
|  | ipmbConnectionHeaderLength); | 
|  | } | 
|  |  | 
|  | static inline bool ipmbDataChecksumValidate(const ipmbHeader* ipmbHeader, | 
|  | uint8_t length) | 
|  | { | 
|  | return ipmbChecksumValidate((reinterpret_cast<const uint8_t*>(ipmbHeader) + | 
|  | ipmbConnectionHeaderLength), | 
|  | (length - ipmbConnectionHeaderLength)); | 
|  | } | 
|  |  | 
|  | static bool isFrameValid(const ipmbHeader* frame, uint8_t length) | 
|  | { | 
|  | if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (false == ipmbConnectionHeaderChecksumValidate(frame)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (false == ipmbDataChecksumValidate(frame, length)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | IpmbRequest::IpmbRequest(const ipmbHeader* ipmbBuffer, size_t bufferLength) | 
|  | { | 
|  | address = ipmbBuffer->Header.Req.address; | 
|  | 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]); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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, | 
|  | std::vector<uint8_t>& 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::ipmbToi2cConstruct(uint8_t* buffer, size_t* bufferLength) | 
|  | { | 
|  | ipmbHeader* ipmbBuffer = (ipmbHeader*)buffer; | 
|  |  | 
|  | 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( | 
|  | buffer, ipmbConnectionHeaderLength - ipmbChecksumSize); | 
|  |  | 
|  | if (data.size() > 0) | 
|  | { | 
|  | std::copy( | 
|  | data.begin(), data.end(), | 
|  | &buffer[ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength]); | 
|  | } | 
|  |  | 
|  | *bufferLength = data.size() + ipmbResponseDataHeaderLength + | 
|  | ipmbConnectionHeaderLength + ipmbChecksumSize; | 
|  |  | 
|  | buffer[*bufferLength - ipmbChecksumSize] = | 
|  | ipmbChecksumCompute(&buffer[ipmbChecksum2StartOffset], | 
|  | (ipmbResponseDataHeaderLength + data.size())); | 
|  | } | 
|  |  | 
|  | void IpmbRequest::prepareRequest(sdbusplus::message_t& mesg) | 
|  | { | 
|  | mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data); | 
|  | } | 
|  |  | 
|  | static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd) | 
|  | { | 
|  | return (netFn << 8) | cmd; | 
|  | } | 
|  |  | 
|  | static constexpr bool isMeCmdAllowed(uint8_t netFn, uint8_t cmd) | 
|  | { | 
|  | constexpr uint8_t netFnMeOEM = 0x2E; | 
|  | constexpr uint8_t netFnMeOEMGeneral = 0x3E; | 
|  | constexpr uint8_t cmdMeOemSendRawPeci = 0x40; | 
|  | constexpr uint8_t cmdMeOemAggSendRawPeci = 0x41; | 
|  | constexpr uint8_t cmdMeOemCpuPkgConfWrite = 0x43; | 
|  | constexpr uint8_t cmdMeOemCpuPciConfWrite = 0x45; | 
|  | constexpr uint8_t cmdMeOemReadMemSmbus = 0x47; | 
|  | constexpr uint8_t cmdMeOemWriteMemSmbus = 0x48; | 
|  | constexpr uint8_t cmdMeOemSlotIpmb = 0x51; | 
|  | constexpr uint8_t cmdMeOemSlotI2cControllerWriteRead = 0x52; | 
|  | constexpr uint8_t cmdMeOemSendRawPmbus = 0xD9; | 
|  | constexpr uint8_t cmdMeOemUnlockMeRegion = 0xE7; | 
|  | constexpr uint8_t cmdMeOemAggSendRawPmbus = 0xEC; | 
|  |  | 
|  | switch (makeCmdKey(netFn, cmd)) | 
|  | { | 
|  | // Restrict ME Controller write command | 
|  | case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead): | 
|  | // Restrict ME OEM commands | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPeci): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPeci): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemCpuPkgConfWrite): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemCpuPciConfWrite): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemReadMemSmbus): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemWriteMemSmbus): | 
|  | case makeCmdKey(netFnMeOEMGeneral, cmdMeOemSlotIpmb): | 
|  | case makeCmdKey(netFnMeOEMGeneral, cmdMeOemSlotI2cControllerWriteRead): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPmbus): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemUnlockMeRegion): | 
|  | case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPmbus): | 
|  | return false; | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | ipmi::Cc Bridging::handleIpmbChannel(ipmi::Context::ptr ctx, | 
|  | const uint8_t tracking, | 
|  | const std::vector<uint8_t>& msgData, | 
|  | std::vector<uint8_t>& rspData) | 
|  | { | 
|  | ipmi::Manufacturing mtm; | 
|  |  | 
|  | size_t msgLen = msgData.size(); | 
|  | if ((msgLen < ipmbMinFrameLength) || (msgLen > ipmbMaxFrameLength)) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "handleIpmbChannel, IPMB data length is invalid"); | 
|  | return ipmi::ccReqDataLenInvalid; | 
|  | } | 
|  |  | 
|  | // Bridging to ME requires Administrator lvl | 
|  | if ((ctx->priv) != ipmi::Privilege::Admin) | 
|  | { | 
|  | return ipmi::ccInsufficientPrivilege; | 
|  | } | 
|  |  | 
|  | auto sendMsgReqData = reinterpret_cast<const ipmbHeader*>(msgData.data()); | 
|  |  | 
|  | // allow bridging to ME only | 
|  | if (sendMsgReqData->Header.Req.address != ipmbMeTargetAddress) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "handleIpmbChannel, IPMB address invalid"); | 
|  | return ipmi::ccParmOutOfRange; | 
|  | } | 
|  |  | 
|  | constexpr uint8_t shiftLUN = 2; | 
|  | if (mtm.getMfgMode() == ipmi::SpecialMode::none) | 
|  | { | 
|  | if (!isMeCmdAllowed((sendMsgReqData->Header.Req.rsNetFnLUN >> shiftLUN), | 
|  | sendMsgReqData->Header.Req.cmd)) | 
|  | { | 
|  | constexpr ipmi::Cc ccCmdNotSupportedInPresentState = 0xD5; | 
|  | return ccCmdNotSupportedInPresentState; | 
|  | } | 
|  | } | 
|  |  | 
|  | // check allowed modes | 
|  | if (tracking != modeNoTracking && tracking != modeTrackRequest) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "handleIpmbChannel, mode not supported"); | 
|  | return ipmi::ccParmOutOfRange; | 
|  | } | 
|  |  | 
|  | // check if request contains valid IPMB frame | 
|  | if (!isFrameValid(sendMsgReqData, msgLen)) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "handleIpmbChannel, IPMB frame invalid"); | 
|  | return ipmi::ccParmOutOfRange; | 
|  | } | 
|  |  | 
|  | auto ipmbRequest = IpmbRequest(sendMsgReqData, msgLen); | 
|  |  | 
|  | typedef std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, | 
|  | std::vector<uint8_t>> | 
|  | IPMBResponse; | 
|  |  | 
|  | // send request to IPMB | 
|  | boost::system::error_code ec; | 
|  | auto ipmbResponse = ctx->bus->yield_method_call<IPMBResponse>( | 
|  | ctx->yield, ec, ipmbBus, ipmbObj, ipmbIntf, "sendRequest", | 
|  | ipmbMeChannelNum, ipmbRequest.netFn, ipmbRequest.rqLun, ipmbRequest.cmd, | 
|  | ipmbRequest.data); | 
|  | if (ec) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "handleIpmbChannel, dbus call exception"); | 
|  | return ipmi::ccUnspecifiedError; | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> dataReceived(0); | 
|  | int status = -1; | 
|  | uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0; | 
|  |  | 
|  | std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse; | 
|  |  | 
|  | auto respReceived = | 
|  | IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address, | 
|  | ipmbRequest.seq, lun, cmd, cc, dataReceived); | 
|  |  | 
|  | // check IPMB layer status | 
|  | if (status) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::WARNING>( | 
|  | "handleIpmbChannel, ipmb returned non zero status"); | 
|  | return ipmi::ccResponseError; | 
|  | } | 
|  |  | 
|  | switch (tracking) | 
|  | { | 
|  | case modeNoTracking: | 
|  | { | 
|  | if (getResponseQueueSize() == responseQueueMaxSize) | 
|  | { | 
|  | return ipmi::ccBusy; | 
|  | } | 
|  | insertMessageInQueue(respReceived); | 
|  | break; | 
|  | } | 
|  | case modeTrackRequest: | 
|  | { | 
|  | size_t dataLength = 0; | 
|  | respReceived.ipmbToi2cConstruct(rspData.data(), &dataLength); | 
|  | // resizing the rspData to its correct length | 
|  | rspData.resize(dataLength); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "handleIpmbChannel, mode not supported"); | 
|  | return ipmi::ccParmOutOfRange; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ipmi::ccSuccess; | 
|  | } | 
|  |  | 
|  | void Bridging::insertMessageInQueue(IpmbResponse msg) | 
|  | { | 
|  | responseQueue.insert(responseQueue.end(), std::move(msg)); | 
|  | } | 
|  |  | 
|  | void Bridging::eraseMessageFromQueue() | 
|  | { | 
|  | responseQueue.erase(responseQueue.begin()); | 
|  | } | 
|  |  | 
|  | IpmbResponse Bridging::getMessageFromQueue() | 
|  | { | 
|  | return responseQueue.front(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief This command is used for bridging ipmi message between channels. | 
|  | * @param channelNumber         - channel number to send message to | 
|  | * @param authenticationEnabled - authentication. | 
|  | * @param encryptionEnabled     - encryption | 
|  | * @param Tracking              - track request | 
|  | * @param msg                   - message data | 
|  | * | 
|  | * @return IPMI completion code plus response data on success. | 
|  | * - rspData - response data | 
|  | **/ | 
|  | ipmi::RspType<std::vector<uint8_t> // responseData | 
|  | > | 
|  | ipmiAppSendMessage(ipmi::Context::ptr ctx, const uint4_t channelNumber, | 
|  | const bool authenticationEnabled, | 
|  | const bool encryptionEnabled, const uint2_t tracking, | 
|  | ipmi::message::Payload& msg) | 
|  | { | 
|  | // check message fields: | 
|  | // encryption not supported | 
|  | if (encryptionEnabled) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "ipmiAppSendMessage, encryption not supported"); | 
|  | return ipmi::responseParmOutOfRange(); | 
|  | } | 
|  |  | 
|  | // authentication not supported | 
|  | if (authenticationEnabled) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "ipmiAppSendMessage, authentication not supported"); | 
|  | return ipmi::responseParmOutOfRange(); | 
|  | } | 
|  |  | 
|  | ipmi::Cc returnVal; | 
|  | std::vector<uint8_t> rspData(ipmbMaxFrameLength); | 
|  | size_t dataLength = 0; | 
|  | std::vector<uint8_t> unpackMsg; | 
|  |  | 
|  | auto channelNo = static_cast<const uint8_t>(channelNumber); | 
|  | // Get the channel number | 
|  | switch (channelNo) | 
|  | { | 
|  | // we only handle ipmb for now | 
|  | case targetChannelIpmb: | 
|  | case targetChannelOtherLan: | 
|  | if (msg.unpack(unpackMsg) || !msg.fullyUnpacked()) | 
|  | { | 
|  | return ipmi::responseReqDataLenInvalid(); | 
|  | } | 
|  |  | 
|  | returnVal = bridging.handleIpmbChannel( | 
|  | ctx, static_cast<const uint8_t>(tracking), unpackMsg, rspData); | 
|  | break; | 
|  | // fall through to default | 
|  | case targetChannelIcmb10: | 
|  | case targetChannelIcmb09: | 
|  | case targetChannelLan: | 
|  | case targetChannelSerialModem: | 
|  | case targetChannelPciSmbus: | 
|  | case targetChannelSmbus10: | 
|  | case targetChannelSmbus20: | 
|  | case targetChannelSystemInterface: | 
|  | default: | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "ipmiAppSendMessage, TargetChannel invalid"); | 
|  | return ipmi::responseParmOutOfRange(); | 
|  | } | 
|  | if (returnVal != ipmi::ccSuccess) | 
|  | { | 
|  | return ipmi::response(returnVal); | 
|  | } | 
|  |  | 
|  | return ipmi::responseSuccess(rspData); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief This command is used to Get data from the receive message queue. | 
|  | *  This command should be executed executed via system interface only. | 
|  | * | 
|  | * @return IPMI completion code plus response data on success. | 
|  | * - channelNumber | 
|  | * - messageData | 
|  | **/ | 
|  |  | 
|  | ipmi::RspType<uint8_t,             // channelNumber | 
|  | std::vector<uint8_t> // messageData | 
|  | > | 
|  | ipmiAppGetMessage(ipmi::Context::ptr ctx) | 
|  | { | 
|  | ipmi::ChannelInfo chInfo; | 
|  |  | 
|  | try | 
|  | { | 
|  | getChannelInfo(ctx->channel, chInfo); | 
|  | } | 
|  | catch (const sdbusplus::exception_t& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppGetMessage: Failed to get Channel Info", | 
|  | phosphor::logging::entry("MSG: %s", e.description())); | 
|  | return ipmi::responseUnspecifiedError(); | 
|  | } | 
|  | if (chInfo.mediumType != | 
|  | static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface)) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppGetMessage: Error - supported only in System(SMS) " | 
|  | "interface"); | 
|  | return ipmi::responseCommandNotAvailable(); | 
|  | } | 
|  |  | 
|  | uint8_t channelData = 0; | 
|  | std::vector<uint8_t> res(ipmbMaxFrameLength); | 
|  | size_t dataLength = 0; | 
|  |  | 
|  | if (!bridging.getResponseQueueSize()) | 
|  | { | 
|  | constexpr ipmi::Cc ipmiGetMessageCmdDataNotAvailable = 0x80; | 
|  | phosphor::logging::log<phosphor::logging::level::INFO>( | 
|  | "ipmiAppGetMessage, no data available"); | 
|  | return ipmi::response(ipmiGetMessageCmdDataNotAvailable); | 
|  | } | 
|  |  | 
|  | // channel number set. | 
|  | channelData |= static_cast<uint8_t>(targetChannelSystemInterface) & 0x0F; | 
|  |  | 
|  | // Priviledge level set. | 
|  | channelData |= SYSTEM_INTERFACE & 0xF0; | 
|  |  | 
|  | // Get the first message from queue | 
|  | auto respQueueItem = bridging.getMessageFromQueue(); | 
|  |  | 
|  | // construct response data. | 
|  | respQueueItem.ipmbToi2cConstruct(res.data(), &dataLength); | 
|  |  | 
|  | // Remove the message from queue | 
|  | bridging.eraseMessageFromQueue(); | 
|  |  | 
|  | // resizing the rspData to its correct length | 
|  | res.resize(dataLength); | 
|  |  | 
|  | return ipmi::responseSuccess(channelData, res); | 
|  | } | 
|  |  | 
|  | std::size_t Bridging::getResponseQueueSize() | 
|  | { | 
|  | return responseQueue.size(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | @brief This command is used to retrive present message available states. | 
|  |  | 
|  | @return IPMI completion code plus Flags as response data on success. | 
|  | **/ | 
|  | ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags(ipmi::Context::ptr ctx) | 
|  | { | 
|  | ipmi::ChannelInfo chInfo; | 
|  |  | 
|  | try | 
|  | { | 
|  | getChannelInfo(ctx->channel, chInfo); | 
|  | } | 
|  | catch (const sdbusplus::exception_t& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppGetMessageFlags: Failed to get Channel Info", | 
|  | phosphor::logging::entry("MSG: %s", e.description())); | 
|  | return ipmi::responseUnspecifiedError(); | 
|  | } | 
|  | if (chInfo.mediumType != | 
|  | static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface)) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppGetMessageFlags: Error - supported only in System(SMS) " | 
|  | "interface"); | 
|  | return ipmi::responseCommandNotAvailable(); | 
|  | } | 
|  |  | 
|  | std::bitset<8> getMsgFlagsRes; | 
|  |  | 
|  | // set event message buffer bit | 
|  | if (!eventMessageBufferFlag) | 
|  | { | 
|  | getMsgFlagsRes.set(getMsgFlagEventMessageBit); | 
|  | } | 
|  | else | 
|  | { | 
|  | getMsgFlagsRes.reset(getMsgFlagEventMessageBit); | 
|  | } | 
|  |  | 
|  | // set message fields | 
|  | if (bridging.getResponseQueueSize() > 0) | 
|  | { | 
|  | getMsgFlagsRes.set(getMsgFlagReceiveMessageBit); | 
|  | } | 
|  | else | 
|  | { | 
|  | getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit); | 
|  | } | 
|  |  | 
|  | try | 
|  | { | 
|  | std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
|  | ipmi::Value variant = ipmi::getDbusProperty( | 
|  | *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp); | 
|  | if (std::get<bool>(variant)) | 
|  | { | 
|  | getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit); | 
|  | } | 
|  | } | 
|  | catch (const sdbusplus::exception_t& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppGetMessageFlags, dbus call exception"); | 
|  | return ipmi::responseUnspecifiedError(); | 
|  | } | 
|  |  | 
|  | return ipmi::responseSuccess(getMsgFlagsRes); | 
|  | } | 
|  |  | 
|  | /** @brief This command is used to flush unread data from the receive | 
|  | *   message queue | 
|  | *  @param receiveMessage  - clear receive message queue | 
|  | *  @param eventMsgBufFull - clear event message buffer full | 
|  | *  @param reserved2       - reserved bit | 
|  | *  @param watchdogTimeout - clear watchdog pre-timeout interrupt flag | 
|  | *  @param reserved1       - reserved bit | 
|  | *  @param oem0            - clear OEM 0 data | 
|  | *  @param oem1            - clear OEM 1 data | 
|  | *  @param oem2            - clear OEM 2 data | 
|  |  | 
|  | *  @return IPMI completion code on success | 
|  | */ | 
|  | ipmi::RspType<> ipmiAppClearMessageFlags(ipmi::Context::ptr ctx, | 
|  | bool receiveMessage, | 
|  | bool eventMsgBufFull, bool reserved2, | 
|  | bool watchdogTimeout, bool reserved1, | 
|  | bool oem0, bool oem1, bool oem2) | 
|  | { | 
|  | ipmi::ChannelInfo chInfo; | 
|  |  | 
|  | try | 
|  | { | 
|  | getChannelInfo(ctx->channel, chInfo); | 
|  | } | 
|  | catch (const sdbusplus::exception_t& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppClearMessageFlags: Failed to get Channel Info", | 
|  | phosphor::logging::entry("MSG: %s", e.description())); | 
|  | return ipmi::responseUnspecifiedError(); | 
|  | } | 
|  | if (chInfo.mediumType != | 
|  | static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface)) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppClearMessageFlags: Error - supported only in System(SMS) " | 
|  | "interface"); | 
|  | return ipmi::responseCommandNotAvailable(); | 
|  | } | 
|  |  | 
|  | if (reserved1 || reserved2) | 
|  | { | 
|  | return ipmi::responseInvalidFieldRequest(); | 
|  | } | 
|  |  | 
|  | if (receiveMessage) | 
|  | { | 
|  | bridging.clearResponseQueue(); | 
|  | } | 
|  |  | 
|  | if (eventMessageBufferFlag != true && eventMsgBufFull == true) | 
|  | { | 
|  | eventMessageBufferFlag = true; | 
|  | } | 
|  |  | 
|  | try | 
|  | { | 
|  | if (watchdogTimeout) | 
|  | { | 
|  | std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
|  | ipmi::setDbusProperty(*dbus, wdtService, wdtObjPath, wdtInterface, | 
|  | wdtInterruptFlagProp, false); | 
|  | } | 
|  | } | 
|  | catch (const sdbusplus::exception_t& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppClearMessageFlags: can't Clear/Set " | 
|  | "PreTimeoutInterruptOccurFlag"); | 
|  | return ipmi::responseUnspecifiedError(); | 
|  | } | 
|  |  | 
|  | return ipmi::responseSuccess(); | 
|  | } | 
|  |  | 
|  | using systemEventType = std::tuple< | 
|  | uint16_t, // Generator ID | 
|  | uint32_t, // Timestamp | 
|  | uint8_t,  // Sensor Type | 
|  | uint8_t,  // EvM Rev | 
|  | uint8_t,  // Sensor Number | 
|  | uint7_t,  // Event Type | 
|  | bool,     // Event Direction | 
|  | std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data | 
|  | using oemTsEventType = std::tuple< | 
|  | uint32_t,                                                   // Timestamp | 
|  | std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data | 
|  | using oemEventType = | 
|  | std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data | 
|  |  | 
|  | /** @brief implements of Read event message buffer command | 
|  | * | 
|  | *  @returns IPMI completion code plus response data | 
|  | *   - recordID - SEL Record ID | 
|  | *   - recordType - Record Type | 
|  | *   - generatorID - Generator ID | 
|  | *   - timeStamp - Timestamp | 
|  | *   - sensorType - Sensor Type | 
|  | *   - eventMsgFormatRev - Event Message format version | 
|  | *   - sensorNumber - Sensor Number | 
|  | *   - eventType - Event Type | 
|  | *   - eventDir - Event Direction | 
|  | *   - eventData - Event Data field | 
|  | */ | 
|  | ipmi::RspType<uint16_t, // Record ID | 
|  | uint8_t,  // Record Type | 
|  | std::variant<systemEventType, oemTsEventType, | 
|  | oemEventType>> // Record Content | 
|  | ipmiAppReadEventMessageBuffer(ipmi::Context::ptr ctx) | 
|  | { | 
|  | ipmi::ChannelInfo chInfo; | 
|  |  | 
|  | try | 
|  | { | 
|  | getChannelInfo(ctx->channel, chInfo); | 
|  | } | 
|  | catch (const sdbusplus::exception_t& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppReadEventMessageBuffer: Failed to get Channel Info", | 
|  | phosphor::logging::entry("MSG: %s", e.description())); | 
|  | return ipmi::responseUnspecifiedError(); | 
|  | } | 
|  | if (chInfo.mediumType != | 
|  | static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface)) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "ipmiAppReadEventMessageBuffer: Error - supported only in " | 
|  | "System(SMS) interface"); | 
|  | return ipmi::responseCommandNotAvailable(); | 
|  | } | 
|  |  | 
|  | uint16_t recordId = | 
|  | static_cast<uint16_t>(0x5555); // recordId: 0x55 << 8 | 0x55 | 
|  | uint16_t generatorId = | 
|  | static_cast<uint16_t>(0xA741); // generatorId: 0xA7 << 8 | 0x41 | 
|  | constexpr uint8_t recordType = 0xC0; | 
|  | constexpr uint8_t eventMsgFormatRev = 0x3A; | 
|  | constexpr uint8_t sensorNumber = 0xFF; | 
|  |  | 
|  | // TODO need to be implemented. | 
|  | std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{}; | 
|  | // All '0xFF' since unused. | 
|  | eventData.fill(0xFF); | 
|  |  | 
|  | // Set the event message buffer flag | 
|  | eventMessageBufferFlag = true; | 
|  |  | 
|  | return ipmi::responseSuccess( | 
|  | recordId, recordType, | 
|  | systemEventType{generatorId, 0, 0, eventMsgFormatRev, sensorNumber, | 
|  | static_cast<uint7_t>(0), false, eventData}); | 
|  | } | 
|  |  | 
|  | static void register_bridging_functions() __attribute__((constructor)); | 
|  | static void register_bridging_functions() | 
|  | { | 
|  | ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, | 
|  | ipmi::app::cmdClearMessageFlags, | 
|  | ipmi::Privilege::User, ipmiAppClearMessageFlags); | 
|  |  | 
|  | ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, | 
|  | ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User, | 
|  | ipmiAppGetMessageFlags); | 
|  |  | 
|  | ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, | 
|  | ipmi::app::cmdGetMessage, ipmi::Privilege::User, | 
|  | ipmiAppGetMessage); | 
|  |  | 
|  | ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, | 
|  | ipmi::app::cmdSendMessage, ipmi::Privilege::User, | 
|  | ipmiAppSendMessage); | 
|  |  | 
|  | ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp, | 
|  | ipmi::app::cmdReadEventMessageBuffer, | 
|  | ipmi::Privilege::User, ipmiAppReadEventMessageBuffer); | 
|  |  | 
|  | return; | 
|  | } |