buffer: Implement "readEntry"
readEntry reads the entryHeader to figure out how much of the buffer to
read for the entry.
Tested: Unit tested
Signed-off-by: Brandon Kim <brandonkim@google.com>
Change-Id: I390e77c088439c74d100ef4b4cb35e746facd495
diff --git a/include/buffer.hpp b/include/buffer.hpp
index d199a94..240845c 100644
--- a/include/buffer.hpp
+++ b/include/buffer.hpp
@@ -17,6 +17,10 @@
using boost::endian::little_uint32_t;
using boost::endian::little_uint64_t;
+// EntryPair.first = QueueEntryHeader
+// EntryPair.second = Error entry in vector of bytes
+using EntryPair = std::pair<struct QueueEntryHeader, std::vector<uint8_t>>;
+
struct CircularBufferHeader
{
little_uint32_t bmcInterfaceVersion; // Offset 0x0
@@ -124,6 +128,14 @@
* @return the entry header
*/
virtual struct QueueEntryHeader readEntryHeader(size_t offset) = 0;
+
+ /**
+ * Read the queue entry from the error log queue
+ *
+ * @param[in] offset - offset to read from
+ * @return entry header and entry pair read from buffer
+ */
+ virtual EntryPair readEntry(size_t offset) = 0;
};
/**
@@ -146,6 +158,7 @@
wraparoundRead(const uint32_t offset, const uint32_t length,
const uint32_t additionalBoundaryCheck = 0) override;
struct QueueEntryHeader readEntryHeader(size_t offset) override;
+ EntryPair readEntry(size_t offset) override;
private:
/** @brief The Error log queue starts after the UE region, which is where
diff --git a/src/buffer.cpp b/src/buffer.cpp
index 87d9fa2..fca8837 100644
--- a/src/buffer.cpp
+++ b/src/buffer.cpp
@@ -12,6 +12,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
+#include <numeric>
#include <span>
#include <vector>
@@ -170,4 +171,31 @@
return *reinterpret_cast<struct QueueEntryHeader*>(bytesRead.data());
}
+EntryPair BufferImpl::readEntry(size_t offset)
+{
+ struct QueueEntryHeader entryHeader = readEntryHeader(offset);
+
+ size_t entrySize = entryHeader.entrySize;
+
+ // wraparonudRead may throw if entrySize was bigger than the buffer or if it
+ // was not able to read all bytes, let it propagate up the stack
+ // - Use additionalBoundaryCheck parameter to tighten the boundary check
+ std::vector<uint8_t> entry =
+ wraparoundRead(offset + sizeof(struct QueueEntryHeader), entrySize,
+ sizeof(struct QueueEntryHeader));
+
+ // Calculate the checksum
+ uint8_t* entryHeaderPtr = reinterpret_cast<uint8_t*>(&entryHeader);
+ uint8_t checksum = std::accumulate(
+ entryHeaderPtr, entryHeaderPtr + sizeof(struct QueueEntryHeader), 0);
+ checksum = std::accumulate(std::begin(entry), std::end(entry), checksum);
+ if (checksum != 0)
+ {
+ throw std::runtime_error(fmt::format(
+ "[readEntry] Checksum was '{}', expected '0'", checksum));
+ }
+
+ return {entryHeader, entry};
+}
+
} // namespace bios_bmc_smm_error_logger
diff --git a/test/buffer_test.cpp b/test/buffer_test.cpp
index 9042f67..7218f8d 100644
--- a/test/buffer_test.cpp
+++ b/test/buffer_test.cpp
@@ -343,17 +343,17 @@
ElementsAreArray(expectedBytes));
}
-class BufferEntryHeaderTest : public BufferWraparoundReadTest
+class BufferEntryTest : public BufferWraparoundReadTest
{
protected:
- BufferEntryHeaderTest()
+ BufferEntryTest()
{
testEntryHeader.sequenceId = testSequenceId;
testEntryHeader.entrySize = testEntrySize;
testEntryHeader.checksum = testChecksum;
testEntryHeader.rdeCommandType = testRdeCommandType;
}
- ~BufferEntryHeaderTest() override = default;
+ ~BufferEntryTest() override = default;
void wraparoundReadMock(std::span<std::uint8_t> expetedBytesOutput)
{
@@ -370,7 +370,8 @@
static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
static constexpr uint16_t testSequenceId = 0;
static constexpr uint16_t testEntrySize = 0x20;
- static constexpr uint8_t testChecksum = 1;
+ // Calculated checksum for the header (0x100 - 0 - 0x20 - 0x01) & 0xff
+ static constexpr uint8_t testChecksum = 0xdf;
static constexpr uint8_t testRdeCommandType = 0x01;
size_t testOffset = 0x50;
@@ -378,7 +379,7 @@
{};
};
-TEST_F(BufferEntryHeaderTest, ReadEntryHeaderPass)
+TEST_F(BufferEntryTest, ReadEntryHeaderPass)
{
uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
std::vector<uint8_t> testEntryHeaderVector(
@@ -387,5 +388,47 @@
EXPECT_EQ(bufferImpl->readEntryHeader(testOffset), testEntryHeader);
}
+TEST_F(BufferEntryTest, ReadEntryChecksumFail)
+{
+ InSequence s;
+ // We expect this will bump checksum up by "testEntrySize" = 0x20
+ std::vector<uint8_t> testEntryVector(testEntrySize, 1);
+ // Offset the checksum by 1
+ testEntryHeader.checksum -= (0x20 - 1);
+ uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
+ std::vector<uint8_t> testEntryHeaderVector(
+ testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
+ wraparoundReadMock(testEntryHeaderVector);
+
+ wraparoundReadMock(testEntryVector);
+ EXPECT_THROW(
+ try {
+ bufferImpl->readEntry(testOffset);
+ } catch (const std::runtime_error& e) {
+ EXPECT_STREQ(e.what(),
+ "[readEntry] Checksum was '1', expected '0'");
+ throw;
+ },
+ std::runtime_error);
+}
+
+TEST_F(BufferEntryTest, ReadEntryPass)
+{
+ InSequence s;
+ // We expect this will bump checksum up by "testEntrySize" = 0x40
+ std::vector<uint8_t> testEntryVector(testEntrySize, 2);
+ testEntryHeader.checksum -= (0x40);
+ uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
+ std::vector<uint8_t> testEntryHeaderVector(
+ testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
+ wraparoundReadMock(testEntryHeaderVector);
+ wraparoundReadMock(testEntryVector);
+
+ EntryPair testedEntryPair;
+ EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry(testOffset));
+ EXPECT_EQ(testedEntryPair.first, testEntryHeader);
+ EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
+}
+
} // namespace
} // namespace bios_bmc_smm_error_logger