buffer: Implement "readBufferHeader" and enforce endianness

Add buffer header reader and a getter API for the cachedBufferHeader for
testing.

Also update CircularBufferHeader with boost endian library.

Tested: Unit Tested

Signed-off-by: Brandon Kim <brandonkim@google.com>
Change-Id: I3afaa96f1fab2dd160058e8127dcba494efe3ce7
diff --git a/include/buffer.hpp b/include/buffer.hpp
index 972884e..08d400d 100644
--- a/include/buffer.hpp
+++ b/include/buffer.hpp
@@ -2,44 +2,52 @@
 
 #include "pci_handler.hpp"
 
+#include <boost/endian/arithmetic.hpp>
+
 #include <array>
 #include <cstdint>
 #include <memory>
+#include <tuple>
 
 namespace bios_bmc_smm_error_logger
 {
 
+/* Adding endianness */
+using boost::endian::little_uint16_t;
+using boost::endian::little_uint32_t;
+using boost::endian::little_uint64_t;
+
 struct CircularBufferHeader
 {
-    uint32_t bmcInterfaceVersion;        // Offset 0x0
-    uint32_t biosInterfaceVersion;       // Offset 0x4
-    std::array<uint32_t, 4> magicNumber; // Offset 0x8
-    uint16_t queueSize;                  // Offset 0x18
-    uint16_t ueRegionSize;               // Offset 0x1a
-    uint32_t bmcFlags;                   // Offset 0x1c
-    uint16_t bmcReadPtr;                 // Offset 0x20
-    std::array<uint8_t, 6> padding1;     // Offset 0x22
-    uint32_t biosFlags;                  // Offset 0x28
-    uint16_t biosWritePtr;               // Offset 0x2c
-    std::array<uint8_t, 2> padding2;     // Offset 0x2e
-    // UE reserved region:                  Offset 0x30
-    // Error log queue:                     Offset 0x30 + UE reserved region
+    little_uint32_t bmcInterfaceVersion;        // Offset 0x0
+    little_uint32_t biosInterfaceVersion;       // Offset 0x4
+    std::array<little_uint32_t, 4> magicNumber; // Offset 0x8
+    little_uint16_t queueSize;                  // Offset 0x18
+    little_uint16_t ueRegionSize;               // Offset 0x1a
+    little_uint32_t bmcFlags;                   // Offset 0x1c
+    little_uint16_t bmcReadPtr;                 // Offset 0x20
+    std::array<uint8_t, 6> reserved1;           // Offset 0x22
+    little_uint32_t biosFlags;                  // Offset 0x28
+    little_uint16_t biosWritePtr;               // Offset 0x2c
+    std::array<uint8_t, 2> reserved2;           // Offset 0x2e
+    // UE reserved region:                         Offset 0x30
+    // Error log queue:       Offset 0x30 + UE reserved region
 
     bool operator==(const CircularBufferHeader& other) const
     {
-        return this->bmcInterfaceVersion == other.bmcInterfaceVersion &&
-               this->biosInterfaceVersion == other.biosInterfaceVersion &&
-               this->magicNumber == other.magicNumber &&
-               this->queueSize == other.queueSize &&
-               this->ueRegionSize == other.ueRegionSize &&
-               this->bmcFlags == other.bmcFlags &&
-               this->bmcReadPtr == other.bmcReadPtr &&
-               /* Skip comparing padding1 */
-               this->biosFlags == other.biosFlags &&
-               this->biosWritePtr == other.biosWritePtr;
-        /* Skip comparing padding2 */
+        /* Skip comparing reserved1 and reserved2 */
+        return std::tie(this->bmcInterfaceVersion, this->biosInterfaceVersion,
+                        this->magicNumber, this->queueSize, this->ueRegionSize,
+                        this->bmcFlags, this->bmcReadPtr, this->biosFlags,
+                        this->biosWritePtr) ==
+               std::tie(other.bmcInterfaceVersion, other.biosInterfaceVersion,
+                        other.magicNumber, other.queueSize, other.ueRegionSize,
+                        other.bmcFlags, other.bmcReadPtr, other.biosFlags,
+                        other.biosWritePtr);
     }
-} __attribute__((__packed__));
+};
+static_assert(sizeof(CircularBufferHeader) == 0x30,
+              "Size of CircularBufferHeader struct is incorrect.");
 
 /**
  * An interface class for the buffer helper APIs
@@ -61,6 +69,17 @@
     virtual void initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
                             uint16_t ueRegionSize,
                             const std::array<uint32_t, 4>& magicNumber) = 0;
+
+    /**
+     * Read the buffer header from shared buffer
+     */
+    virtual void readBufferHeader() = 0;
+
+    /**
+     * Getter API for the cached buffer header
+     * @return cached CircularBufferHeader
+     */
+    virtual struct CircularBufferHeader getCachedBufferHeader() const = 0;
 };
 
 /**
@@ -76,9 +95,12 @@
     void initialize(uint32_t bmcInterfaceVersion, uint16_t queueSize,
                     uint16_t ueRegionSize,
                     const std::array<uint32_t, 4>& magicNumber) override;
+    void readBufferHeader() override;
+    struct CircularBufferHeader getCachedBufferHeader() const override;
 
   private:
     std::unique_ptr<DataInterface> dataInterface;
+    struct CircularBufferHeader cachedBufferHeader = {};
 };
 
 } // namespace bios_bmc_smm_error_logger
diff --git a/src/buffer.cpp b/src/buffer.cpp
index a0491eb..a2b8ee4 100644
--- a/src/buffer.cpp
+++ b/src/buffer.cpp
@@ -4,6 +4,10 @@
 
 #include <fmt/format.h>
 
+#include <boost/endian/arithmetic.hpp>
+#include <boost/endian/conversion.hpp>
+
+#include <algorithm>
 #include <array>
 #include <cstdint>
 #include <memory>
@@ -32,10 +36,16 @@
 
     // Create an initial buffer header and write to it
     struct CircularBufferHeader initializationHeader = {};
-    initializationHeader.bmcInterfaceVersion = bmcInterfaceVersion;
-    initializationHeader.queueSize = queueSize;
-    initializationHeader.ueRegionSize = ueRegionSize;
-    initializationHeader.magicNumber = magicNumber;
+    initializationHeader.bmcInterfaceVersion =
+        boost::endian::native_to_little(bmcInterfaceVersion);
+    initializationHeader.queueSize = boost::endian::native_to_little(queueSize);
+    initializationHeader.ueRegionSize =
+        boost::endian::native_to_little(ueRegionSize);
+    std::transform(magicNumber.begin(), magicNumber.end(),
+                   initializationHeader.magicNumber.begin(),
+                   [](uint32_t number) -> little_uint32_t {
+                       return boost::endian::native_to_little(number);
+                   });
 
     uint8_t* initializationHeaderPtr =
         reinterpret_cast<uint8_t*>(&initializationHeader);
@@ -52,4 +62,26 @@
     }
 }
 
+void BufferImpl::readBufferHeader()
+{
+    size_t headerSize = sizeof(struct CircularBufferHeader);
+    std::vector<uint8_t> bytesRead =
+        dataInterface->read(/* offset */ 0, headerSize);
+
+    if (bytesRead.size() != headerSize)
+    {
+        throw std::runtime_error(
+            fmt::format("Buffer header read only read '{}', expected '{}'",
+                        bytesRead.size(), headerSize));
+    }
+
+    cachedBufferHeader =
+        *reinterpret_cast<CircularBufferHeader*>(bytesRead.data());
+};
+
+struct CircularBufferHeader BufferImpl::getCachedBufferHeader() const
+{
+    return cachedBufferHeader;
+}
+
 } // namespace bios_bmc_smm_error_logger
diff --git a/test/buffer_test.cpp b/test/buffer_test.cpp
index ee03163..e3d5621 100644
--- a/test/buffer_test.cpp
+++ b/test/buffer_test.cpp
@@ -1,6 +1,10 @@
 #include "buffer.hpp"
 #include "data_interface_mock.hpp"
 
+#include <boost/endian/arithmetic.hpp>
+#include <boost/endian/conversion.hpp>
+
+#include <algorithm>
 #include <array>
 #include <cstdint>
 #include <memory>
@@ -29,7 +33,11 @@
         testInitializationHeader.bmcInterfaceVersion = testBmcInterfaceVersion;
         testInitializationHeader.queueSize = testQueueSize;
         testInitializationHeader.ueRegionSize = testUeRegionSize;
-        testInitializationHeader.magicNumber = testMagicNumber;
+        std::transform(testMagicNumber.begin(), testMagicNumber.end(),
+                       testInitializationHeader.magicNumber.begin(),
+                       [](uint32_t number) -> little_uint32_t {
+                           return boost::endian::native_to_little(number);
+                       });
     }
     ~BufferTest() override = default;
 
@@ -40,12 +48,14 @@
     static constexpr uint16_t testUeRegionSize = 0x50;
     static constexpr std::array<uint32_t, 4> testMagicNumber = {
         0x12345678, 0x22345678, 0x32345678, 0x42345678};
+    static constexpr size_t bufferHeaderSize =
+        sizeof(struct CircularBufferHeader);
+
     struct CircularBufferHeader testInitializationHeader
     {};
 
     std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
     DataInterfaceMock* dataInterfaceMockPtr;
-
     std::unique_ptr<BufferImpl> bufferImpl;
 };
 
@@ -100,15 +110,44 @@
 
     uint8_t* testInitializationHeaderPtr =
         reinterpret_cast<uint8_t*>(&testInitializationHeader);
-    size_t initializationHeaderSize = sizeof(testInitializationHeader);
     EXPECT_CALL(*dataInterfaceMockPtr,
                 write(0, ElementsAreArray(testInitializationHeaderPtr,
-                                          initializationHeaderSize)))
-        .WillOnce(Return(initializationHeaderSize));
+                                          bufferHeaderSize)))
+        .WillOnce(Return(bufferHeaderSize));
     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
                                            testQueueSize, testUeRegionSize,
                                            testMagicNumber));
 }
 
+TEST_F(BufferTest, BufferHeaderReadFail)
+{
+    std::vector<std::uint8_t> testBytesRead{};
+    EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
+        .WillOnce(Return(testBytesRead));
+    EXPECT_THROW(
+        try {
+            bufferImpl->readBufferHeader();
+        } catch (const std::runtime_error& e) {
+            EXPECT_STREQ(e.what(),
+                         "Buffer header read only read '0', expected '48'");
+            throw;
+        },
+        std::runtime_error);
+}
+
+TEST_F(BufferTest, BufferHeaderReadPass)
+{
+    uint8_t* testInitializationHeaderPtr =
+        reinterpret_cast<uint8_t*>(&testInitializationHeader);
+    std::vector<uint8_t> testInitializationHeaderVector(
+        testInitializationHeaderPtr,
+        testInitializationHeaderPtr + bufferHeaderSize);
+
+    EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
+        .WillOnce(Return(testInitializationHeaderVector));
+    EXPECT_NO_THROW(bufferImpl->readBufferHeader());
+    EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
+}
+
 } // namespace
 } // namespace bios_bmc_smm_error_logger