initial drop of phosphor-ipmi-blobs

This implements a majority of the OEM IPMI BLOBS protocol.  The only
piece missing from this is the timed expiration of sessions.

Change-Id: I82c9d17b625c94fc3340edcfabbbf1ffeb5ad7ac
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/process.cpp b/process.cpp
new file mode 100644
index 0000000..595b5c2
--- /dev/null
+++ b/process.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * 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 "process.hpp"
+
+#include "ipmi.hpp"
+
+#include <cstring>
+#include <vector>
+
+namespace blobs
+{
+
+/* Used by all commands with data. */
+struct BmcRx
+{
+    uint8_t cmd;
+    uint16_t crc;
+    uint8_t data; /* one byte minimum of data. */
+} __attribute__((packed));
+
+IpmiBlobHandler validateBlobCommand(CrcInterface* crc, const uint8_t* reqBuf,
+                                    uint8_t* replyCmdBuf, size_t* dataLen)
+{
+    IpmiBlobHandler cmd;
+    size_t requestLength = (*dataLen);
+    /* We know dataLen is at least 1 already */
+    auto command = static_cast<BlobOEMCommands>(reqBuf[0]);
+
+    /* Validate it's at least well-formed. */
+    if (!validateRequestLength(command, requestLength))
+    {
+        return nullptr;
+    }
+
+    /* If there is a payload. */
+    if (requestLength > sizeof(uint8_t))
+    {
+        /* Verify the request includes: command, crc16, data */
+        if (requestLength < sizeof(struct BmcRx))
+        {
+            return nullptr;
+        }
+
+        /* We don't include the command byte at offset 0 as part of the crc
+         * payload area or the crc bytes at the beginning.
+         */
+        size_t requestBodyLen = requestLength - 3;
+
+        /* We start after the command byte. */
+        std::vector<uint8_t> bytes(requestBodyLen);
+
+        /* It likely has a well-formed payload. */
+        struct BmcRx request;
+        std::memcpy(&request, reqBuf, sizeof(request));
+        uint16_t crcValue = request.crc;
+
+        /* Set the in-place CRC to zero. */
+        std::memcpy(bytes.data(), &reqBuf[3], requestBodyLen);
+
+        crc->clear();
+        crc->compute(bytes.data(), bytes.size());
+
+        /* Crc expected but didn't match. */
+        if (crcValue != crc->get())
+        {
+            return nullptr;
+        }
+    }
+
+    /* Grab the corresponding handler for the command (could do map or array
+     * of function pointer lookup).
+     */
+    switch (command)
+    {
+        case BlobOEMCommands::bmcBlobGetCount:
+            cmd = getBlobCount;
+            break;
+        case BlobOEMCommands::bmcBlobEnumerate:
+            cmd = enumerateBlob;
+            break;
+        case BlobOEMCommands::bmcBlobOpen:
+            cmd = openBlob;
+            break;
+        case BlobOEMCommands::bmcBlobRead:
+            cmd = readBlob;
+            break;
+        case BlobOEMCommands::bmcBlobWrite:
+            cmd = writeBlob;
+            break;
+        case BlobOEMCommands::bmcBlobCommit:
+            cmd = commitBlob;
+            break;
+        case BlobOEMCommands::bmcBlobClose:
+            cmd = closeBlob;
+            break;
+        case BlobOEMCommands::bmcBlobDelete:
+            cmd = deleteBlob;
+            break;
+        case BlobOEMCommands::bmcBlobStat:
+            cmd = statBlob;
+            break;
+        case BlobOEMCommands::bmcBlobSessionStat:
+            cmd = sessionStatBlob;
+            break;
+        default:
+            return nullptr;
+    }
+
+    return cmd;
+}
+
+ipmi_ret_t processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
+                              CrcInterface* crc, const uint8_t* reqBuf,
+                              uint8_t* replyCmdBuf, size_t* dataLen)
+{
+    ipmi_ret_t result = cmd(mgr, reqBuf, replyCmdBuf, dataLen);
+    if (result != IPMI_CC_OK)
+    {
+        return result;
+    }
+
+    size_t replyLength = (*dataLen);
+
+    /* The command, whatever it was, returned success. */
+    if (replyLength == 0)
+    {
+        return result;
+    }
+
+    /* The response, if it has one byte, has three, to include the crc16. */
+    if (replyLength < (sizeof(uint16_t) + 1))
+    {
+        return IPMI_CC_INVALID;
+    }
+
+    /* The command, whatever it was, replied, so let's set the CRC. */
+    crc->clear();
+    replyCmdBuf[0] = 0x00;
+    replyCmdBuf[1] = 0x00;
+    crc->compute(replyCmdBuf, replyLength);
+
+    /* Copy the CRC into place. */
+    uint16_t crcValue = crc->get();
+    std::memcpy(replyCmdBuf, &crcValue, sizeof(crcValue));
+
+    return result;
+}
+} // namespace blobs