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