tools: implement blob read

Implement blob read.  This isn't currently used by the flash update
process however, it is useful.

Tested: Verified it read back bytes written to another blob handler.
Change-Id: Ib1590448d0bfaa3d4fde20ae8bae353c9107e5c4
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/blob_interface_mock.hpp b/test/blob_interface_mock.hpp
index 520d5b4..845d7c1 100644
--- a/test/blob_interface_mock.hpp
+++ b/test/blob_interface_mock.hpp
@@ -19,6 +19,9 @@
                  std::uint16_t(const std::string&,
                                blobs::FirmwareBlobHandler::UpdateFlags));
     MOCK_METHOD1(closeBlob, void(std::uint16_t));
+    MOCK_METHOD3(readBytes,
+                 std::vector<std::uint8_t>(std::uint16_t, std::uint32_t,
+                                           std::uint32_t));
 };
 
 } // namespace host_tool
diff --git a/test/tools_blob_unittest.cpp b/test/tools_blob_unittest.cpp
index 18cc6eb..a51eb25 100644
--- a/test/tools_blob_unittest.cpp
+++ b/test/tools_blob_unittest.cpp
@@ -255,4 +255,32 @@
     blob.writeBytes(0x0001, 0, bytes);
 }
 
+TEST_F(BlobHandlerTest, readBytesSucceeds)
+{
+    /* The reading of bytes succeeds. */
+
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobRead,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00};
+
+    std::vector<std::uint8_t> expectedBytes = {'a', 'b', 'c', 'd'};
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00,
+                                      'a',  'b',  'c',  'd'};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x04, 0x00, 0x00, 0x00};
+    std::vector<std::uint8_t> respCrc = {'a', 'b', 'c', 'd'};
+
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(respCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    EXPECT_EQ(blob.readBytes(0x0001, 0, 4), expectedBytes);
+}
+
 } // namespace host_tool
diff --git a/tools/blob_handler.cpp b/tools/blob_handler.cpp
index e1a995d..081c32f 100644
--- a/tools/blob_handler.cpp
+++ b/tools/blob_handler.cpp
@@ -294,4 +294,25 @@
     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));
+
+    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));
+
+    data = reinterpret_cast<std::uint8_t*>(&length);
+    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));
+
+    return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
+}
+
 } // namespace host_tool
diff --git a/tools/blob_handler.hpp b/tools/blob_handler.hpp
index e7da30f..1cbc963 100644
--- a/tools/blob_handler.hpp
+++ b/tools/blob_handler.hpp
@@ -69,6 +69,13 @@
 
     void closeBlob(std::uint16_t session) override;
 
+    /**
+     * @throws BlobException.
+     */
+    std::vector<std::uint8_t> readBytes(std::uint16_t session,
+                                        std::uint32_t offset,
+                                        std::uint32_t length) override;
+
   private:
     /**
      * Send the contents of the payload to IPMI, this method handles wrapping
diff --git a/tools/blob_interface.hpp b/tools/blob_interface.hpp
index 07525c9..61ee5eb 100644
--- a/tools/blob_interface.hpp
+++ b/tools/blob_interface.hpp
@@ -76,6 +76,19 @@
      * @param[in] session - the session to close.
      */
     virtual void closeBlob(std::uint16_t session) = 0;
+
+    /**
+     * Read bytes from a blob.
+     *
+     * @param[in] session - the session id.
+     * @param[in] offset - the offset to which to write the bytes.
+     * @param[in] length - the number of bytes to read.
+     * @return the bytes read
+     * @throws BlobException on failure.
+     */
+    virtual std::vector<std::uint8_t> readBytes(std::uint16_t session,
+                                                std::uint32_t offset,
+                                                std::uint32_t length) = 0;
 };
 
 } // namespace host_tool