Move commands from ipmi-providers
Many commands were in ipmi-providers and need to be moved into the
active development library (intel-ipmi-oem). This copies wholesale
those commands, even though many need to be rewritten to use the new
ipmi providers API.
Tested-by: build and see that the commands are still present even when
the ipmi-providers library is removed
Change-Id: If326f5d7844adeed7da2d3b7a2f1d3eeeea43b29
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/src/bridgingcommands.cpp b/src/bridgingcommands.cpp
new file mode 100644
index 0000000..0c727f6
--- /dev/null
+++ b/src/bridgingcommands.cpp
@@ -0,0 +1,514 @@
+/*
+// 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 <ipmid/api.h>
+
+#include <bridgingcommands.hpp>
+#include <cstring>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+#include <vector>
+
+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;
+
+/**
+ * @brief utils for checksum
+ */
+static bool ipmbChecksumValidate(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(ipmbHeader *ipmbHeader)
+{
+ return ipmbChecksumValidate(reinterpret_cast<uint8_t *>(ipmbHeader),
+ ipmbConnectionHeaderLength);
+}
+
+static inline bool ipmbDataChecksumValidate(ipmbHeader *ipmbHeader,
+ uint8_t length)
+{
+ return ipmbChecksumValidate(
+ (reinterpret_cast<uint8_t *>(ipmbHeader) + ipmbConnectionHeaderLength),
+ (length - ipmbConnectionHeaderLength));
+}
+
+static bool isFrameValid(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::message &mesg)
+{
+ mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data);
+}
+
+Bridging::Bridging() : dbus(ipmid_get_sd_bus_connection())
+{
+}
+
+ipmi_return_codes Bridging::handleIpmbChannel(sSendMessageReq *sendMsgReq,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if ((*dataLen < (sizeof(sSendMessageReq) + ipmbMinFrameLength)) ||
+ (*dataLen > (sizeof(sSendMessageReq) + ipmbMaxFrameLength)))
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto sendMsgReqData = reinterpret_cast<ipmbHeader *>(sendMsgReq->data);
+
+ // TODO: check privilege lvl. Bridging to ME requires Administrator lvl
+
+ // allow bridging to ME only
+ if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, IPMB address invalid");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // check allowed modes
+ if (sendMsgReq->modeGet() != modeNoTracking &&
+ sendMsgReq->modeGet() != modeTrackRequest)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, mode not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // check if request contains valid IPMB frame
+ if (!isFrameValid(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq))))
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, IPMB frame invalid");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ auto ipmbRequest =
+ IpmbRequest(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq)));
+
+ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
+ ipmbResponse;
+
+ // send request to IPMB
+ try
+ {
+ auto mesg =
+ dbus.new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest");
+ ipmbRequest.prepareRequest(mesg);
+ auto ret = dbus.call(mesg);
+ ret.read(ipmbResponse);
+ }
+ catch (sdbusplus::exception::SdBusError &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "handleIpmbChannel, dbus call exception");
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ 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");
+ *dataLen = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto sendMsgRes = reinterpret_cast<uint8_t *>(response);
+
+ switch (sendMsgReq->modeGet())
+ {
+ case modeNoTracking:
+ if (responseQueue.size() == responseQueueMaxSize)
+ {
+ *dataLen = 0;
+ return IPMI_CC_BUSY;
+ }
+ responseQueue.insert(responseQueue.end(), std::move(respReceived));
+ *dataLen = 0;
+ return IPMI_CC_OK;
+
+ break;
+ case modeTrackRequest:
+ respReceived.ipmbToi2cConstruct(sendMsgRes, dataLen);
+ return IPMI_CC_OK;
+
+ break;
+ default:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, mode not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+}
+
+ipmi_return_codes Bridging::sendMessageHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ ipmi_return_codes retCode = IPMI_CC_OK;
+
+ if (*dataLen < sizeof(sSendMessageReq))
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto sendMsgReq = reinterpret_cast<sSendMessageReq *>(request);
+
+ // check message fields:
+ // encryption not supported
+ if (sendMsgReq->encryptionGet() != 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "sendMessageHandler, encryption not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // authentication not supported
+ if (sendMsgReq->authenticationGet() != 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "sendMessageHandler, authentication not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ switch (sendMsgReq->channelNumGet())
+ {
+ // we only handle ipmb for now
+ case targetChannelIpmb:
+ case targetChannelOtherLan:
+ retCode = handleIpmbChannel(sendMsgReq, response, dataLen);
+ 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>(
+ "sendMessageHandler, TargetChannel invalid");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ return retCode;
+}
+
+ipmi_return_codes Bridging::getMessageHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if (*dataLen != 0)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto getMsgRes = reinterpret_cast<sGetMessageRes *>(response);
+ auto getMsgResData = static_cast<uint8_t *>(getMsgRes->data);
+
+ std::memset(getMsgRes, 0, sizeof(sGetMessageRes));
+
+ auto respQueueItem = responseQueue.begin();
+
+ if (respQueueItem == responseQueue.end())
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "getMessageHandler, no data available");
+ *dataLen = 0;
+ return ipmiGetMessageCmdDataNotAvailable;
+ }
+
+ // set message fields
+ getMsgRes->privilegeLvlSet(SYSTEM_INTERFACE);
+ getMsgRes->channelNumSet(targetChannelSystemInterface);
+
+ // construct response
+ respQueueItem->ipmbToi2cConstruct(getMsgResData, dataLen);
+ responseQueue.erase(respQueueItem);
+
+ *dataLen = *dataLen + sizeof(sGetMessageRes);
+ return IPMI_CC_OK;
+}
+
+ipmi_return_codes Bridging::getMessageFlagsHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if (*dataLen != 0)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto getMsgFlagsRes = reinterpret_cast<sGetMessageFlagsResp *>(response);
+
+ std::memset(getMsgFlagsRes, 0, sizeof(sGetMessageFlagsResp));
+
+ // preserve current (legacy) behaviour
+ getMsgFlagsRes->eventMessageBitSet(1);
+
+ // set message fields
+ if (responseQueue.size() > 0)
+ {
+ getMsgFlagsRes->receiveMessageBitSet(1);
+ }
+ else
+ {
+ getMsgFlagsRes->receiveMessageBitSet(0);
+ }
+
+ *dataLen = sizeof(sGetMessageFlagsResp);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_return_codes Bridging::clearMessageFlagsHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if (*dataLen != sizeof(sClearMessageFlagsReq))
+ {
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto clearMsgFlagsReq = reinterpret_cast<sClearMessageFlagsReq *>(request);
+
+ if (clearMsgFlagsReq->receiveMessageBitGet() == 1)
+ {
+ responseQueue.clear();
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiAppSendMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.sendMessageHandler(request, response, dataLen);
+
+ return retCode;
+}
+
+ipmi_ret_t ipmiAppGetMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.getMessageHandler(request, response, dataLen);
+
+ return retCode;
+}
+
+ipmi_ret_t ipmiAppGetMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.getMessageFlagsHandler(request, response, dataLen);
+
+ return retCode;
+}
+
+ipmi_ret_t ipmiAppClearMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.clearMessageFlagsHandler(request, response, dataLen);
+
+ *dataLen = 0;
+
+ return retCode;
+}
+
+static void register_bridging_functions() __attribute__((constructor));
+static void register_bridging_functions()
+{
+ ipmi_register_callback(
+ NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdClearMessageFlags,
+ NULL, ipmiAppClearMessageFlags, PRIVILEGE_USER);
+
+ ipmi_register_callback(
+ NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessageFlags, NULL,
+ ipmiAppGetMessageFlags, PRIVILEGE_USER);
+
+ ipmi_register_callback(NETFUN_APP,
+ Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessage,
+ NULL, ipmiAppGetMessage, PRIVILEGE_USER);
+
+ ipmi_register_callback(NETFUN_APP,
+ Bridging::IpmiAppBridgingCmds::ipmiCmdSendMessage,
+ NULL, ipmiAppSendMessage, PRIVILEGE_USER);
+
+ return;
+}