initial commit

Add initial code from phosphor-ipmi-flash/tools that was not specific to
firmware update over ipmi-blobs.

Change-Id: I360537a7392347fe989397a699f6a712bc36e62c
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/src/ipmiblob/blob_handler.cpp b/src/ipmiblob/blob_handler.cpp
new file mode 100644
index 0000000..5be0b2d
--- /dev/null
+++ b/src/ipmiblob/blob_handler.cpp
@@ -0,0 +1,315 @@
+/*
+ * 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 "blob_errors.hpp"
+#include "crc.hpp"
+#include "ipmi_errors.hpp"
+
+#include <array>
+#include <cstring>
+
+namespace host_tool
+{
+
+namespace
+{
+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, bytes;
+
+    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);
+        auto src = reinterpret_cast<const 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)
+    {
+        throw BlobException(e.what());
+    }
+
+    /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
+     * now.
+     */
+    if (reply.size() == 0)
+    {
+        return reply;
+    }
+
+    /* This cannot be a response because it's smaller than the smallest
+     * response.
+     */
+    if (reply.size() < ipmiPhosphorOen.size())
+    {
+        throw BlobException("Invalid response length");
+    }
+
+    /* Verify the OEN. */
+    if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
+                    ipmiPhosphorOen.size()) != 0)
+    {
+        throw BlobException("Invalid OEN received");
+    }
+
+    /* In this case there was no data, as there was no CRC. */
+    std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
+    if (reply.size() < headerSize)
+    {
+        return {};
+    }
+
+    /* Validate CRC. */
+    std::uint16_t crc;
+    auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
+    std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));
+
+    for (const auto& byte : reply)
+    {
+        std::fprintf(stderr, "0x%02x ", byte);
+    }
+    std::fprintf(stderr, "\n");
+
+    bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());
+
+    auto computed = generateCrc(bytes);
+    if (crc != computed)
+    {
+        std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
+                     crc, computed);
+        throw BlobException("Invalid CRC on received data.");
+    }
+
+    return bytes;
+}
+
+int BlobHandler::getBlobCount()
+{
+    std::uint32_t count;
+    try
+    {
+        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));
+    }
+    catch (const BlobException& b)
+    {
+        return 0;
+    }
+
+    std::fprintf(stderr, "BLOB Count: %d\n", count);
+    return count;
+}
+
+std::string BlobHandler::enumerateBlob(std::uint32_t index)
+{
+    std::vector<std::uint8_t> payload;
+    auto data = reinterpret_cast<const std::uint8_t*>(&index);
+
+    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
+
+    try
+    {
+        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
+        return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
+                                 : "";
+    }
+    catch (const BlobException& b)
+    {
+        return "";
+    }
+}
+
+void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
+                               std::uint32_t offset,
+                               const std::vector<std::uint8_t>& bytes)
+{
+    std::vector<std::uint8_t> payload;
+
+    payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
+                    bytes.size());
+
+    auto data = reinterpret_cast<const std::uint8_t*>(&session);
+    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
+
+    data = reinterpret_cast<const std::uint8_t*>(&offset);
+    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
+
+    std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
+
+    auto resp = sendIpmiPayload(command, payload);
+}
+
+void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
+                            const std::vector<std::uint8_t>& bytes)
+{
+    return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset,
+                        bytes);
+}
+
+void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
+                             const std::vector<std::uint8_t>& bytes)
+{
+    return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
+}
+
+std::vector<std::string> BlobHandler::getBlobList()
+{
+    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;
+}
+
+StatResponse BlobHandler::getStat(const std::string& id)
+{
+    StatResponse meta;
+    std::vector<std::uint8_t> name, resp;
+
+    std::copy(id.begin(), id.end(), std::back_inserter(name));
+    name.push_back(0x00); /* need to add nul-terminator. */
+
+    try
+    {
+        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
+    }
+    catch (const BlobException& b)
+    {
+        throw;
+    }
+
+    std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
+    std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
+    int offset = sizeof(meta.blob_state) + sizeof(meta.size);
+    std::uint8_t len = resp[offset];
+    if (len > 0)
+    {
+        std::copy(&resp[offset + 1], &resp[resp.size()],
+                  std::back_inserter(meta.metadata));
+    }
+
+    return meta;
+}
+
+std::uint16_t BlobHandler::openBlob(const std::string& id,
+                                    std::uint16_t handlerFlags)
+{
+    std::uint16_t session;
+    std::vector<std::uint8_t> request, resp;
+    auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);
+
+    std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
+              std::back_inserter(request));
+    std::copy(id.begin(), id.end(), std::back_inserter(request));
+    request.push_back(0x00); /* need to add nul-terminator. */
+
+    try
+    {
+        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
+    }
+    catch (const BlobException& b)
+    {
+        throw;
+    }
+
+    if (resp.size() != sizeof(session))
+    {
+        throw BlobException("Did not receive session.");
+    }
+
+    std::memcpy(&session, resp.data(), sizeof(session));
+    return session;
+}
+
+void BlobHandler::closeBlob(std::uint16_t session)
+{
+    std::vector<std::uint8_t> request;
+    auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
+    std::copy(addrSession, addrSession + sizeof(session),
+              std::back_inserter(request));
+
+    try
+    {
+        sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
+    }
+    catch (const BlobException& b)
+    {
+        std::fprintf(stderr, "Received failure on close: %s\n", b.what());
+    }
+
+    return;
+}
+
+std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session,
+                                                 std::uint32_t offset,
+                                                 std::uint32_t length)
+{
+    std::vector<std::uint8_t> payload;
+
+    payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
+                    sizeof(std::uint32_t));
+
+    auto data = reinterpret_cast<const std::uint8_t*>(&session);
+    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
+
+    data = reinterpret_cast<const std::uint8_t*>(&offset);
+    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
+
+    data = reinterpret_cast<const std::uint8_t*>(&length);
+    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
+
+    return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
+}
+
+} // namespace host_tool