tools: implement blob write

Implement blob write bytes.

Change-Id: Ie7c79db67ea58a82d39ac679a8e380934e93ca5a
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/blob_interface_mock.hpp b/test/blob_interface_mock.hpp
index eff241f..bf9fdbc 100644
--- a/test/blob_interface_mock.hpp
+++ b/test/blob_interface_mock.hpp
@@ -9,6 +9,8 @@
 {
   public:
     virtual ~BlobInterfaceMock() = default;
+    MOCK_METHOD3(writeBytes, void(std::uint16_t, std::uint32_t,
+                                  const std::vector<std::uint8_t>&));
     MOCK_METHOD0(getBlobList, std::vector<std::string>());
     MOCK_METHOD1(getStat, StatResponse(const std::string&));
     MOCK_METHOD2(openBlob,
diff --git a/test/tools_blob_unittest.cpp b/test/tools_blob_unittest.cpp
index 8205d3a..7893e3c 100644
--- a/test/tools_blob_unittest.cpp
+++ b/test/tools_blob_unittest.cpp
@@ -214,4 +214,27 @@
     EXPECT_EQ(0xedfe, session);
 }
 
+TEST_F(BlobHandlerTest, writeBytesSucceeds)
+{
+    /* The write bytes succeeds. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobWrite,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        'a',  'b',  'c',  'd'};
+
+    std::vector<std::uint8_t> bytes = {'a', 'b', 'c', 'd'};
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00, 0x00, 0x00,
+                                        0x00, 'a',  'b',  'c',  'd'};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    blob.writeBytes(0x0001, 0, bytes);
+}
+
 } // namespace host_tool
diff --git a/tools/blob_handler.cpp b/tools/blob_handler.cpp
index 572d5c8..781ee29 100644
--- a/tools/blob_handler.cpp
+++ b/tools/blob_handler.cpp
@@ -161,6 +161,26 @@
     }
 }
 
+void BlobHandler::writeBytes(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());
+
+    std::uint8_t* data = reinterpret_cast<std::uint8_t*>(&session);
+    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));
+
+    data = reinterpret_cast<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(BlobOEMCommands::bmcBlobWrite, payload);
+    return;
+}
+
 std::vector<std::string> BlobHandler::getBlobList()
 {
     std::vector<std::string> list;
diff --git a/tools/blob_handler.hpp b/tools/blob_handler.hpp
index 6c176e4..0114b89 100644
--- a/tools/blob_handler.hpp
+++ b/tools/blob_handler.hpp
@@ -54,6 +54,12 @@
      */
     std::string enumerateBlob(std::uint32_t index);
 
+    /**
+     * @throw BlobException.
+     */
+    void writeBytes(std::uint16_t session, std::uint32_t offset,
+                    const std::vector<std::uint8_t>& bytes) override;
+
     std::vector<std::string> getBlobList() override;
 
     /**
diff --git a/tools/blob_interface.hpp b/tools/blob_interface.hpp
index b79121b..00031b8 100644
--- a/tools/blob_interface.hpp
+++ b/tools/blob_interface.hpp
@@ -22,6 +22,17 @@
     virtual ~BlobInterface() = default;
 
     /**
+     * Write bytes to a blob.
+     *
+     * @param[in] session - the session id.
+     * @param[in] offset - the offset to which to write the bytes.
+     * @param[in] bytes - the bytes to send.
+     * @throws BlobException on failure.
+     */
+    virtual void writeBytes(std::uint16_t session, std::uint32_t offset,
+                            const std::vector<std::uint8_t>& bytes) = 0;
+
+    /**
      * Get a list of the blob_ids provided by the BMC.
      *
      * @return list of strings, each representing a blob_id returned.