ipmbbridge initial implementation

ipmbbridge gives the ability for openbmc to host ipmb transactions over
smbus

Change-Id: I3744dbef5a6db0b2ff4f2b691e68ca8dc3b1d24b
Signed-off-by: Dawid Frycki <dawid.frycki@intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/ipmbbridged.hpp b/ipmbbridged.hpp
new file mode 100644
index 0000000..c139391
--- /dev/null
+++ b/ipmbbridged.hpp
@@ -0,0 +1,331 @@
+/* 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 "ipmbdefines.hpp"
+
+#include <boost/container/flat_set.hpp>
+#include <optional>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/message.hpp>
+#include <vector>
+
+#ifndef IPMBBRIDGED_HPP
+#define IPMBBRIDGED_HPP
+
+/**
+ * @brief Ipmb return status codes (sendRequest API call)
+ */
+enum class ipmbResponseStatus
+{
+    success = 0,
+    error = 1,
+    invalid_param = 2,
+    busy = 3,
+    timeout = 4,
+};
+
+/**
+ * @brief Ipmb outstanding requests defines
+ */
+constexpr int ipmbMaxOutstandingRequestsCount = 64;
+constexpr int ipmbNumberOfTries = 6;
+constexpr uint64_t ipmbRequestRetryTimeout = 250; // ms
+
+/**
+ * @brief Ipmb I2C communication
+ */
+constexpr uint8_t ipmbI2cNumberOfRetries = 2;
+
+/**
+ * @brief Ipmb defines
+ */
+constexpr size_t ipmbMaxDataSize = 256;
+constexpr size_t ipmbConnectionHeaderLength = 3;
+constexpr size_t ipmbResponseDataHeaderLength = 4;
+constexpr size_t ipmbRequestDataHeaderLength = 3;
+constexpr size_t ipmbAddressSize = 1;
+constexpr size_t ipmbChecksumSize = 1;
+constexpr size_t ipmbChecksum2StartOffset = 3;
+constexpr size_t ipmbMinFrameLength = 7;
+constexpr size_t ipmbMaxFrameLength = ipmbConnectionHeaderLength +
+                                      ipmbResponseDataHeaderLength +
+                                      ipmbChecksumSize + ipmbMaxDataSize;
+
+/**
+ * @brief Ipmb misc
+ */
+constexpr uint8_t ipmbNetFnResponseMask = 0x01;
+constexpr uint8_t ipmbLunMask = 0x03;
+constexpr uint8_t ipmbSeqMask = 0x3F;
+constexpr uint8_t ipmbRsLun = 0x0;
+
+/**
+ * @brief Ipmb setters
+ */
+constexpr uint8_t ipmbNetFnLunSet(uint8_t netFn, uint8_t lun)
+{
+    return ((netFn << 2) | (lun & ipmbLunMask));
+}
+
+constexpr uint8_t ipmbSeqLunSet(uint8_t seq, uint8_t lun)
+{
+    return ((seq << 2) | (lun & ipmbLunMask));
+}
+
+constexpr uint8_t ipmbAddressTo7BitSet(uint8_t address)
+{
+    return address >> 1;
+}
+
+constexpr uint8_t ipmbRespNetFn(uint8_t netFn)
+{
+    return netFn |= 1;
+}
+
+/**
+ * @brief Ipmb getters
+ */
+constexpr uint8_t ipmbNetFnGet(uint8_t netFnLun)
+{
+    return netFnLun >> 2;
+}
+
+constexpr uint8_t ipmbLunFromNetFnLunGet(uint8_t netFnLun)
+{
+    return netFnLun & ipmbLunMask;
+}
+
+constexpr uint8_t ipmbSeqGet(uint8_t seqNumLun)
+{
+    return seqNumLun >> 2;
+}
+
+constexpr uint8_t ipmbLunFromSeqLunGet(uint8_t seqNumLun)
+{
+    return seqNumLun & ipmbLunMask;
+}
+
+/**
+ * @brief Ipmb checkers
+ */
+constexpr bool ipmbIsResponse(IPMB_HEADER *ipmbHeader)
+{
+    return ipmbNetFnGet(ipmbHeader->Header.Resp.rqNetFnLUN) &
+           ipmbNetFnResponseMask;
+}
+
+/**
+ * @brief Ipmb request state
+ */
+enum class ipmbRequestState
+{
+    invalid,
+    valid,
+    matched,
+};
+
+/**
+ * @brief Channel types
+ */
+enum class ipmbChannelType
+{
+    ipmb = 0,
+    me = 1
+};
+
+/**
+ * @brief Channel configuration structure
+ */
+struct IpmbChannelConfig
+{
+    ipmbChannelType type;
+    const char *ipmbI2cSlave;
+    const char *ipmbI2cMaster;
+    uint8_t ipmbBmcSlaveAddress;
+    uint8_t ipmbRqSlaveAddress;
+};
+
+// TODO w/a to differentiate channel origin of incoming IPMI response:
+// extracting channel number from 2 oldest bits of seq
+constexpr ipmbChannelType getChannelFromSeq(const uint8_t &seq)
+{
+    return static_cast<ipmbChannelType>((seq & 0xC0) >> 6);
+}
+
+/**
+ * @brief IpmbResponse declaration
+ */
+struct 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> data;
+
+    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);
+
+    void i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer, size_t bufferLength);
+
+    int ipmbToi2cConstruct(std::vector<uint8_t> &buffer);
+};
+
+/**
+ * @brief IpmbRequest declaration
+ */
+struct IpmbRequest
+{
+    uint8_t address;
+    uint8_t netFn;
+    uint8_t rsLun;
+    uint8_t rqSA;
+    uint8_t seq;
+    uint8_t rqLun;
+    uint8_t cmd;
+    std::vector<uint8_t> data;
+
+    size_t dataLength;
+    ipmbRequestState state;
+    std::optional<boost::asio::steady_timer> timer;
+    std::unique_ptr<IpmbResponse> matchedResponse;
+
+    // creates empty request with empty timer object
+    IpmbRequest();
+
+    IpmbRequest(uint8_t address, uint8_t netFn, uint8_t rsLun, uint8_t rqSA,
+                uint8_t seq, uint8_t rqLun, uint8_t cmd,
+                std::vector<uint8_t> &inputData);
+
+    IpmbRequest(const IpmbRequest &) = delete;
+    IpmbRequest &operator=(IpmbRequest const &) = delete;
+
+    void incomingMessageHandler();
+
+    std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
+        returnMatchedResponse();
+
+    std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
+        returnStatusResponse(int status);
+
+    void i2cToIpmbConstruct(IPMB_HEADER *ipmbBuffer, size_t bufferLength);
+
+    int ipmbToi2cConstruct(std::vector<uint8_t> &buffer);
+
+    // TODO w/a to differentiate channel origin of incoming IPMI response:
+    // saving channel number at two oldest unused bits of seq
+    void addChannelToSeq(const ipmbChannelType &channelType);
+};
+
+/**
+ * @brief Command filtering class declaration
+ *
+ * This feature provides simple mechanism for filtering out commands - which are
+ * not implemented in IPMI - on IPMB level, in order to reduce DBus traffic
+ */
+class IpmbCommandFilter
+{
+  public:
+    // function checking if netFn & cmd combination exist in blocked command
+    // list
+    bool isBlocked(const uint8_t reqNetFn, const uint8_t cmd);
+    // function adding netfFn & cmd combination to the blocked command list
+    void addFilter(const uint8_t reqNetFn, const uint8_t cmd);
+
+  private:
+    boost::container::flat_set<std::pair<uint8_t, uint8_t>> unhandledCommands;
+};
+
+/**
+ * @brief Command filtering defines
+ */
+
+constexpr uint8_t ipmbIpmiInvalidCommand = 0xC1;
+
+constexpr uint8_t ipmbReqNetFnFromRespNetFn(uint8_t reqNetFn)
+{
+    return reqNetFn & ~ipmbNetFnResponseMask;
+}
+
+/**
+ * @brief IpmbChannel class declaration
+ */
+class IpmbChannel
+{
+  public:
+    IpmbChannel(boost::asio::io_service &io, uint8_t ipmbBmcSlaveAddress,
+                uint8_t ipmbRqSlaveAddress, ipmbChannelType type,
+                std::shared_ptr<IpmbCommandFilter> commandFilter);
+
+    IpmbChannel(const IpmbChannel &) = delete;
+    IpmbChannel &operator=(IpmbChannel const &) = delete;
+
+    int ipmbChannelInit(const char *ipmbI2cSlave, const char *ipmbI2cMaster);
+
+    bool seqNumGet(uint8_t &seq);
+
+    ipmbChannelType getChannelType();
+
+    uint8_t getBmcSlaveAddress();
+
+    uint8_t getRqSlaveAddress();
+
+    void addFilter(const uint8_t respNetFn, const uint8_t cmd);
+
+    void processI2cEvent();
+
+    void ipmbResponseSend(std::shared_ptr<std::vector<uint8_t>> buffer,
+                          size_t retriesAttempted);
+
+    std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
+        requestAdd(boost::asio::yield_context &yield,
+                   std::shared_ptr<IpmbRequest> requestToSend);
+
+  private:
+    boost::asio::ip::tcp::socket i2cSlaveSocket;
+    boost::asio::posix::stream_descriptor i2cMasterSocket;
+
+    int ipmbi2cMasterFd;
+    int ipmbi2cSlaveFd;
+
+    uint8_t ipmbBmcSlaveAddress;
+    uint8_t ipmbRqSlaveAddress;
+
+    ipmbChannelType type;
+
+    std::shared_ptr<IpmbCommandFilter> commandFilter;
+
+    // array storing outstanding requests
+    std::array<std::shared_ptr<IpmbRequest>, ipmbMaxOutstandingRequestsCount>
+        outstandingRequests;
+
+    void requestTimerCallback(std::shared_ptr<IpmbRequest> request,
+                              std::shared_ptr<std::vector<uint8_t>> buffer);
+
+    void responseMatch(std::unique_ptr<IpmbResponse> &response);
+
+    void makeRequestInvalid(IpmbRequest &request);
+
+    void makeRequestValid(std::shared_ptr<IpmbRequest> request);
+};
+
+#endif