tools: blob: implement layer above ipmi

Implement the layer above actual IPMI calls, such that we can verify the
behavior.  There is a layer beneath this that'll compute CRCs before
passing the commands down, that will go in next.

Change-Id: I0b8e3aa93c171d829e32727c7bb1b411659d80bd
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/tools/blob_handler.cpp b/tools/blob_handler.cpp
index a2fe9d3..e05da90 100644
--- a/tools/blob_handler.cpp
+++ b/tools/blob_handler.cpp
@@ -1,6 +1,151 @@
+/*
+ * 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 "blob_handler.hpp"
 
+#include "crc.hpp"
+#include "ipmi_errors.hpp"
+
+#include <array>
+#include <cstring>
+
+const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
+
+std::vector<std::uint8_t>
+    BlobHandler::sendIpmiPayload(BlobOEMCommands command,
+                                 const std::vector<std::uint8_t>& payload)
+{
+    std::vector<std::uint8_t> request, reply;
+
+    std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
+              std::back_inserter(request));
+    request.push_back(command);
+
+    if (payload.size() > 0)
+    {
+        /* Grow the vector to hold the bytes. */
+        request.reserve(request.size() + sizeof(std::uint16_t));
+
+        /* CRC required. */
+        std::uint16_t crc = generateCrc(payload);
+        std::uint8_t* src = reinterpret_cast<std::uint8_t*>(&crc);
+
+        std::copy(src, src + sizeof(crc), std::back_inserter(request));
+
+        /* Copy the payload. */
+        std::copy(payload.begin(), payload.end(), std::back_inserter(request));
+    }
+
+    try
+    {
+        reply = ipmi->sendPacket(request);
+    }
+    catch (const IpmiException& e)
+    {
+        std::fprintf(stderr, "Received exception: %s\n", e.what());
+        return {};
+    }
+
+    /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
+     * now.
+     */
+    if (reply.size() == 0)
+    {
+        return reply;
+    }
+
+    size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
+
+    /* This cannot be a response because it's smaller than the smallest
+     * response.
+     */
+    if (reply.size() < headerSize)
+    {
+        std::fprintf(stderr, "Invalid response length\n");
+        return {};
+    }
+
+    /* Verify the OEN. */
+    if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
+                    ipmiPhosphorOen.size()) != 0)
+    {
+        std::fprintf(stderr, "Invalid OEN received\n");
+        return {};
+    }
+
+    /* Validate CRC. */
+    std::uint16_t crc;
+    auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
+    std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
+
+    std::vector<std::uint8_t> bytes;
+    std::copy(&reply[headerSize], &reply[reply.size()],
+              std::back_inserter(bytes));
+
+    auto computed = generateCrc(bytes);
+    if (crc != computed)
+    {
+        std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
+                     crc, computed);
+        return {};
+    }
+
+    return bytes;
+}
+
+int BlobHandler::getBlobCount()
+{
+    std::uint32_t count;
+    auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
+    if (resp.size() != sizeof(count))
+    {
+        return 0;
+    }
+
+    /* LE to LE (need to make this portable as some point. */
+    std::memcpy(&count, resp.data(), sizeof(count));
+    return count;
+}
+
+std::string BlobHandler::enumerateBlob(std::uint32_t index)
+{
+    std::vector<std::uint8_t> payload;
+    std::uint8_t* data = reinterpret_cast<std::uint8_t*>(&index);
+    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
+
+    auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
+    std::string output;
+    std::copy(resp.begin(), resp.end(), std::back_inserter(output));
+    return output;
+}
+
 std::vector<std::string> BlobHandler::getBlobList()
 {
-    return {};
+    std::vector<std::string> list;
+    int blobCount = getBlobCount();
+
+    for (int i = 0; i < blobCount; i++)
+    {
+        auto name = enumerateBlob(i);
+        /* Currently ignore failures. */
+        if (!name.empty())
+        {
+            list.push_back(name);
+        }
+    }
+
+    return list;
 }
diff --git a/tools/blob_handler.hpp b/tools/blob_handler.hpp
index 285bbf1..fb2dcb5 100644
--- a/tools/blob_handler.hpp
+++ b/tools/blob_handler.hpp
@@ -6,8 +6,50 @@
 class BlobHandler : public BlobInterface
 {
   public:
+    enum BlobOEMCommands
+    {
+        bmcBlobGetCount = 0,
+        bmcBlobEnumerate = 1,
+        bmcBlobOpen = 2,
+        bmcBlobRead = 3,
+        bmcBlobWrite = 4,
+        bmcBlobCommit = 5,
+        bmcBlobClose = 6,
+        bmcBlobDelete = 7,
+        bmcBlobStat = 8,
+        bmcBlobSessionStat = 9,
+        bmcBlobWriteMeta = 10,
+    };
+
     explicit BlobHandler(IpmiInterface* ipmi) : ipmi(ipmi){};
 
+    /**
+     * Send the contents of the payload to IPMI, this method handles wrapping
+     * with the OEN, subcommand and CRC.
+     *
+     * @param[in] command - the blob command.
+     * @param[in] payload - the payload bytes.
+     * @return the bytes returned from the ipmi interface.
+     */
+    std::vector<std::uint8_t>
+        sendIpmiPayload(BlobOEMCommands command,
+                        const std::vector<std::uint8_t>& payload);
+
+    /**
+     * Retrieve the blob count.
+     *
+     * @return the number of blob_ids found (0 on failure).
+     */
+    int getBlobCount();
+
+    /**
+     * Given an index into the list of blobs, return the name.
+     *
+     * @param[in] index - the index into the list of blob ids.
+     * @return the name as a string or empty on failure.
+     */
+    std::string enumerateBlob(std::uint32_t index);
+
     std::vector<std::string> getBlobList() override;
 
   private: