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;
+}