xref: /openbmc/bios-bmc-smm-error-logger/test/buffer_test.cpp (revision c20d1216540b3c7668b2b62732ac78cad671c149)
1fcbc3db1SBrandon Kim #include "buffer.hpp"
2fcbc3db1SBrandon Kim #include "data_interface_mock.hpp"
3fcbc3db1SBrandon Kim 
417ee1a93SBrandon Kim #include <boost/endian/arithmetic.hpp>
517ee1a93SBrandon Kim #include <boost/endian/conversion.hpp>
617ee1a93SBrandon Kim 
717ee1a93SBrandon Kim #include <algorithm>
8fcbc3db1SBrandon Kim #include <array>
9fcbc3db1SBrandon Kim #include <cstdint>
10fcbc3db1SBrandon Kim #include <memory>
11fcbc3db1SBrandon Kim 
12fcbc3db1SBrandon Kim #include <gmock/gmock.h>
13fcbc3db1SBrandon Kim #include <gtest/gtest.h>
14fcbc3db1SBrandon Kim 
15fcbc3db1SBrandon Kim namespace bios_bmc_smm_error_logger
16fcbc3db1SBrandon Kim {
17fcbc3db1SBrandon Kim namespace
18fcbc3db1SBrandon Kim {
19fcbc3db1SBrandon Kim 
20fcbc3db1SBrandon Kim using ::testing::_;
21fcbc3db1SBrandon Kim using ::testing::ElementsAreArray;
22fcbc3db1SBrandon Kim using ::testing::InSequence;
23fcbc3db1SBrandon Kim using ::testing::Return;
24fcbc3db1SBrandon Kim 
25fcbc3db1SBrandon Kim class BufferTest : public ::testing::Test
26fcbc3db1SBrandon Kim {
27fcbc3db1SBrandon Kim   protected:
BufferTest()28fcbc3db1SBrandon Kim     BufferTest() :
29fcbc3db1SBrandon Kim         dataInterfaceMock(std::make_unique<DataInterfaceMock>()),
30fcbc3db1SBrandon Kim         dataInterfaceMockPtr(dataInterfaceMock.get())
31fcbc3db1SBrandon Kim     {
32fcbc3db1SBrandon Kim         bufferImpl = std::make_unique<BufferImpl>(std::move(dataInterfaceMock));
33fcbc3db1SBrandon Kim         testInitializationHeader.bmcInterfaceVersion = testBmcInterfaceVersion;
34fcbc3db1SBrandon Kim         testInitializationHeader.queueSize = testQueueSize;
35fcbc3db1SBrandon Kim         testInitializationHeader.ueRegionSize = testUeRegionSize;
3617ee1a93SBrandon Kim         std::transform(testMagicNumber.begin(), testMagicNumber.end(),
3717ee1a93SBrandon Kim                        testInitializationHeader.magicNumber.begin(),
3817ee1a93SBrandon Kim                        [](uint32_t number) -> little_uint32_t {
3917ee1a93SBrandon Kim                            return boost::endian::native_to_little(number);
4017ee1a93SBrandon Kim                        });
41fcbc3db1SBrandon Kim     }
42fcbc3db1SBrandon Kim     ~BufferTest() override = default;
43fcbc3db1SBrandon Kim 
44fcbc3db1SBrandon Kim     // CircularBufferHeader size is 0x30, ensure the test region is bigger
45fcbc3db1SBrandon Kim     static constexpr size_t testRegionSize = 0x200;
46fcbc3db1SBrandon Kim     static constexpr uint32_t testBmcInterfaceVersion = 123;
473def3c8eSBrandon Kim     static constexpr uint32_t testQueueSize = 0x200;
48fcbc3db1SBrandon Kim     static constexpr uint16_t testUeRegionSize = 0x50;
49fcbc3db1SBrandon Kim     static constexpr std::array<uint32_t, 4> testMagicNumber = {
50fcbc3db1SBrandon Kim         0x12345678, 0x22345678, 0x32345678, 0x42345678};
5117ee1a93SBrandon Kim     static constexpr size_t bufferHeaderSize =
5217ee1a93SBrandon Kim         sizeof(struct CircularBufferHeader);
5317ee1a93SBrandon Kim 
54*c20d1216SPatrick Williams     struct CircularBufferHeader testInitializationHeader{};
55fcbc3db1SBrandon Kim 
56fcbc3db1SBrandon Kim     std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
57fcbc3db1SBrandon Kim     DataInterfaceMock* dataInterfaceMockPtr;
58fcbc3db1SBrandon Kim     std::unique_ptr<BufferImpl> bufferImpl;
59fcbc3db1SBrandon Kim };
60fcbc3db1SBrandon Kim 
TEST_F(BufferTest,BufferInitializeEraseFail)61fcbc3db1SBrandon Kim TEST_F(BufferTest, BufferInitializeEraseFail)
62fcbc3db1SBrandon Kim {
63fcbc3db1SBrandon Kim     InSequence s;
6426660e9bSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
6526660e9bSBrandon Kim         .WillOnce(Return(testRegionSize));
6626660e9bSBrandon Kim     EXPECT_THROW(
6726660e9bSBrandon Kim         try {
6826660e9bSBrandon Kim             // Test too big of a proposed buffer compared to the memori size
693def3c8eSBrandon Kim             uint16_t bigQueueSize = 0x201;
7026660e9bSBrandon Kim             uint16_t bigUeRegionSize = 0x50;
7126660e9bSBrandon Kim             bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
7226660e9bSBrandon Kim                                    bigUeRegionSize, testMagicNumber);
7326660e9bSBrandon Kim         } catch (const std::runtime_error& e) {
7426660e9bSBrandon Kim             EXPECT_STREQ(
7526660e9bSBrandon Kim                 e.what(),
763def3c8eSBrandon Kim                 "[initialize] Proposed queue size '513' is bigger than the BMC's allocated MMIO region of '512'");
7726660e9bSBrandon Kim             throw;
7826660e9bSBrandon Kim         },
7926660e9bSBrandon Kim         std::runtime_error);
8026660e9bSBrandon Kim     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
81fcbc3db1SBrandon Kim 
82fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
83fcbc3db1SBrandon Kim         .WillOnce(Return(testRegionSize));
843def3c8eSBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
85fcbc3db1SBrandon Kim     // Return a smaller write than the intended testRegionSize to test the error
86fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
873def3c8eSBrandon Kim         .WillOnce(Return(testQueueSize - 1));
88fcbc3db1SBrandon Kim     EXPECT_THROW(
89fcbc3db1SBrandon Kim         try {
90fcbc3db1SBrandon Kim             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
91fcbc3db1SBrandon Kim                                    testUeRegionSize, testMagicNumber);
92fcbc3db1SBrandon Kim         } catch (const std::runtime_error& e) {
933def3c8eSBrandon Kim             EXPECT_STREQ(e.what(), "[initialize] Only erased '511'");
94fcbc3db1SBrandon Kim             throw;
95fcbc3db1SBrandon Kim         },
96fcbc3db1SBrandon Kim         std::runtime_error);
9760cab57fSBrandon Kim     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
98fcbc3db1SBrandon Kim 
99fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
100fcbc3db1SBrandon Kim         .WillOnce(Return(testRegionSize));
101fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
1023def3c8eSBrandon Kim         .WillOnce(Return(testQueueSize));
103fcbc3db1SBrandon Kim     // Return a smaller write than the intended initializationHeader to test the
104fcbc3db1SBrandon Kim     // error
105fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
106fcbc3db1SBrandon Kim     EXPECT_THROW(
107fcbc3db1SBrandon Kim         try {
108fcbc3db1SBrandon Kim             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
109fcbc3db1SBrandon Kim                                    testUeRegionSize, testMagicNumber);
110fcbc3db1SBrandon Kim         } catch (const std::runtime_error& e) {
11126660e9bSBrandon Kim             EXPECT_STREQ(e.what(),
11226660e9bSBrandon Kim                          "[initialize] Only wrote '0' bytes of the header");
113fcbc3db1SBrandon Kim             throw;
114fcbc3db1SBrandon Kim         },
115fcbc3db1SBrandon Kim         std::runtime_error);
11660cab57fSBrandon Kim     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
117fcbc3db1SBrandon Kim }
118fcbc3db1SBrandon Kim 
TEST_F(BufferTest,BufferInitializePass)119fcbc3db1SBrandon Kim TEST_F(BufferTest, BufferInitializePass)
120fcbc3db1SBrandon Kim {
121fcbc3db1SBrandon Kim     InSequence s;
122fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
123fcbc3db1SBrandon Kim         .WillOnce(Return(testRegionSize));
1243def3c8eSBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
125fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
1263def3c8eSBrandon Kim         .WillOnce(Return(testQueueSize));
127fcbc3db1SBrandon Kim 
128fcbc3db1SBrandon Kim     uint8_t* testInitializationHeaderPtr =
129fcbc3db1SBrandon Kim         reinterpret_cast<uint8_t*>(&testInitializationHeader);
130fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
131fcbc3db1SBrandon Kim                 write(0, ElementsAreArray(testInitializationHeaderPtr,
13217ee1a93SBrandon Kim                                           bufferHeaderSize)))
13317ee1a93SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
1341a64356fSPatrick Williams     EXPECT_NO_THROW(
1351a64356fSPatrick Williams         bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
1361a64356fSPatrick Williams                                testUeRegionSize, testMagicNumber));
13760cab57fSBrandon Kim     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
138fcbc3db1SBrandon Kim }
139fcbc3db1SBrandon Kim 
TEST_F(BufferTest,BufferHeaderReadFail)14017ee1a93SBrandon Kim TEST_F(BufferTest, BufferHeaderReadFail)
14117ee1a93SBrandon Kim {
14217ee1a93SBrandon Kim     std::vector<std::uint8_t> testBytesRead{};
14317ee1a93SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
14417ee1a93SBrandon Kim         .WillOnce(Return(testBytesRead));
14517ee1a93SBrandon Kim     EXPECT_THROW(
14617ee1a93SBrandon Kim         try {
14717ee1a93SBrandon Kim             bufferImpl->readBufferHeader();
14817ee1a93SBrandon Kim         } catch (const std::runtime_error& e) {
14917ee1a93SBrandon Kim             EXPECT_STREQ(e.what(),
15017ee1a93SBrandon Kim                          "Buffer header read only read '0', expected '48'");
15117ee1a93SBrandon Kim             throw;
15217ee1a93SBrandon Kim         },
15317ee1a93SBrandon Kim         std::runtime_error);
15417ee1a93SBrandon Kim }
15517ee1a93SBrandon Kim 
TEST_F(BufferTest,BufferHeaderReadPass)15617ee1a93SBrandon Kim TEST_F(BufferTest, BufferHeaderReadPass)
15717ee1a93SBrandon Kim {
15817ee1a93SBrandon Kim     uint8_t* testInitializationHeaderPtr =
15917ee1a93SBrandon Kim         reinterpret_cast<uint8_t*>(&testInitializationHeader);
16017ee1a93SBrandon Kim     std::vector<uint8_t> testInitializationHeaderVector(
16117ee1a93SBrandon Kim         testInitializationHeaderPtr,
16217ee1a93SBrandon Kim         testInitializationHeaderPtr + bufferHeaderSize);
16317ee1a93SBrandon Kim 
16417ee1a93SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
16517ee1a93SBrandon Kim         .WillOnce(Return(testInitializationHeaderVector));
16617ee1a93SBrandon Kim     EXPECT_NO_THROW(bufferImpl->readBufferHeader());
16717ee1a93SBrandon Kim     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
16817ee1a93SBrandon Kim }
16917ee1a93SBrandon Kim 
TEST_F(BufferTest,BufferUpdateReadPtrFail)170cf0b9752SBrandon Kim TEST_F(BufferTest, BufferUpdateReadPtrFail)
171cf0b9752SBrandon Kim {
172cf0b9752SBrandon Kim     // Return write size that is not 2 which is sizeof(little_uint16_t)
173cf0b9752SBrandon Kim     constexpr size_t wrongWriteSize = 1;
174cf0b9752SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
175cf0b9752SBrandon Kim         .WillOnce(Return(wrongWriteSize));
176cf0b9752SBrandon Kim     EXPECT_THROW(
177cf0b9752SBrandon Kim         try {
178cf0b9752SBrandon Kim             bufferImpl->updateReadPtr(0);
179cf0b9752SBrandon Kim         } catch (const std::runtime_error& e) {
180cf0b9752SBrandon Kim             EXPECT_STREQ(
181cf0b9752SBrandon Kim                 e.what(),
182271d2313SBrandon Kim                 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
183cf0b9752SBrandon Kim             throw;
184cf0b9752SBrandon Kim         },
185cf0b9752SBrandon Kim         std::runtime_error);
186cf0b9752SBrandon Kim }
187cf0b9752SBrandon Kim 
TEST_F(BufferTest,BufferUpdateReadPtrPass)188cf0b9752SBrandon Kim TEST_F(BufferTest, BufferUpdateReadPtrPass)
189cf0b9752SBrandon Kim {
190271d2313SBrandon Kim     constexpr size_t expectedWriteSize = 3;
191271d2313SBrandon Kim     constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
192271d2313SBrandon Kim     // Check that we truncate the highest 24bits
193cf0b9752SBrandon Kim     const uint32_t testNewReadPtr = 0x99881234;
194271d2313SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
195cf0b9752SBrandon Kim 
196cf0b9752SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
197cf0b9752SBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
198cf0b9752SBrandon Kim         .WillOnce(Return(expectedWriteSize));
199cf0b9752SBrandon Kim     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
200c49284b6SBrandon Kim 
201c49284b6SBrandon Kim     auto cachedHeader = bufferImpl->getCachedBufferHeader();
202271d2313SBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
203271d2313SBrandon Kim               0x881234);
204c49284b6SBrandon Kim }
205c49284b6SBrandon Kim 
TEST_F(BufferTest,BufferUpdateBmcFlagsFail)206c49284b6SBrandon Kim TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
207c49284b6SBrandon Kim {
208c49284b6SBrandon Kim     // Return write size that is not 4 which is sizeof(little_uint32_t)
209c49284b6SBrandon Kim     constexpr size_t wrongWriteSize = 1;
210c49284b6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
211c49284b6SBrandon Kim         .WillOnce(Return(wrongWriteSize));
212c49284b6SBrandon Kim     EXPECT_THROW(
213c49284b6SBrandon Kim         try {
214c49284b6SBrandon Kim             bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
215c49284b6SBrandon Kim         } catch (const std::runtime_error& e) {
216c49284b6SBrandon Kim             EXPECT_STREQ(
217c49284b6SBrandon Kim                 e.what(),
218c49284b6SBrandon Kim                 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
219c49284b6SBrandon Kim             throw;
220c49284b6SBrandon Kim         },
221c49284b6SBrandon Kim         std::runtime_error);
222c49284b6SBrandon Kim }
223c49284b6SBrandon Kim 
TEST_F(BufferTest,BufferUpdateBmcFlagsPass)224c49284b6SBrandon Kim TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
225c49284b6SBrandon Kim {
226c49284b6SBrandon Kim     constexpr size_t expectedWriteSize = 4;
227271d2313SBrandon Kim     constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
228c49284b6SBrandon Kim     const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
229c49284b6SBrandon Kim 
230c49284b6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
231c49284b6SBrandon Kim                 write(expectedBmcReadPtrOffset,
232c49284b6SBrandon Kim                       ElementsAreArray(expectedNewBmcFlagsVector)))
233c49284b6SBrandon Kim         .WillOnce(Return(expectedWriteSize));
234c49284b6SBrandon Kim     EXPECT_NO_THROW(
235c49284b6SBrandon Kim         bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
236c49284b6SBrandon Kim 
237c49284b6SBrandon Kim     auto cachedHeader = bufferImpl->getCachedBufferHeader();
238c49284b6SBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
239c49284b6SBrandon Kim               static_cast<uint32_t>(BmcFlags::ready));
240cf0b9752SBrandon Kim }
241cf0b9752SBrandon Kim 
TEST_F(BufferTest,GetMaxOffsetQueueSizeFail)24282ab8320SBrandon Kim TEST_F(BufferTest, GetMaxOffsetQueueSizeFail)
24382ab8320SBrandon Kim {
24482ab8320SBrandon Kim     InSequence s;
24582ab8320SBrandon Kim     static constexpr size_t wrongQueueSize = testQueueSize - 1;
24682ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
24782ab8320SBrandon Kim         .WillOnce(Return(testRegionSize));
24882ab8320SBrandon Kim     const std::vector<uint8_t> emptyArray(wrongQueueSize, 0);
24982ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
25082ab8320SBrandon Kim         .WillOnce(Return(wrongQueueSize));
25182ab8320SBrandon Kim 
25282ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
25382ab8320SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
2541a64356fSPatrick Williams     EXPECT_NO_THROW(
2551a64356fSPatrick Williams         bufferImpl->initialize(testBmcInterfaceVersion, wrongQueueSize,
2561a64356fSPatrick Williams                                testUeRegionSize, testMagicNumber));
25782ab8320SBrandon Kim     EXPECT_THROW(
25882ab8320SBrandon Kim         try {
25982ab8320SBrandon Kim             bufferImpl->getMaxOffset();
26082ab8320SBrandon Kim         } catch (const std::runtime_error& e) {
26182ab8320SBrandon Kim             EXPECT_STREQ(e.what(),
26282ab8320SBrandon Kim                          "[getMaxOffset] runtime queueSize '511' did not match "
26382ab8320SBrandon Kim                          "compile-time queueSize '512'. This indicates that the"
26482ab8320SBrandon Kim                          " buffer was corrupted");
26582ab8320SBrandon Kim             throw;
26682ab8320SBrandon Kim         },
26782ab8320SBrandon Kim         std::runtime_error);
26882ab8320SBrandon Kim }
26982ab8320SBrandon Kim 
TEST_F(BufferTest,GetMaxOffsetUeRegionSizeFail)27082ab8320SBrandon Kim TEST_F(BufferTest, GetMaxOffsetUeRegionSizeFail)
27182ab8320SBrandon Kim {
27282ab8320SBrandon Kim     InSequence s;
27382ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
27482ab8320SBrandon Kim         .WillOnce(Return(testRegionSize));
27582ab8320SBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
27682ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
27782ab8320SBrandon Kim         .WillOnce(Return(testQueueSize));
27882ab8320SBrandon Kim 
27982ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
28082ab8320SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
2811a64356fSPatrick Williams     EXPECT_NO_THROW(
2821a64356fSPatrick Williams         bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
2831a64356fSPatrick Williams                                testUeRegionSize + 1, testMagicNumber));
28482ab8320SBrandon Kim     EXPECT_THROW(
28582ab8320SBrandon Kim         try {
28682ab8320SBrandon Kim             bufferImpl->getMaxOffset();
28782ab8320SBrandon Kim         } catch (const std::runtime_error& e) {
28882ab8320SBrandon Kim             EXPECT_STREQ(
28982ab8320SBrandon Kim                 e.what(),
29082ab8320SBrandon Kim                 "[getMaxOffset] runtime ueRegionSize '81' did not match "
29182ab8320SBrandon Kim                 "compile-time ueRegionSize '80'. This indicates that the"
29282ab8320SBrandon Kim                 " buffer was corrupted");
29382ab8320SBrandon Kim             throw;
29482ab8320SBrandon Kim         },
29582ab8320SBrandon Kim         std::runtime_error);
29682ab8320SBrandon Kim }
29782ab8320SBrandon Kim 
TEST_F(BufferTest,GetOffsetUeRegionSizeFail)29882ab8320SBrandon Kim TEST_F(BufferTest, GetOffsetUeRegionSizeFail)
29982ab8320SBrandon Kim {
30082ab8320SBrandon Kim     InSequence s;
30182ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
30282ab8320SBrandon Kim         .WillOnce(Return(testRegionSize));
30382ab8320SBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
30482ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
30582ab8320SBrandon Kim         .WillOnce(Return(testQueueSize));
30682ab8320SBrandon Kim 
30782ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
30882ab8320SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
3091a64356fSPatrick Williams     EXPECT_NO_THROW(
3101a64356fSPatrick Williams         bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
3111a64356fSPatrick Williams                                testUeRegionSize - 1, testMagicNumber));
31282ab8320SBrandon Kim     EXPECT_THROW(
31382ab8320SBrandon Kim         try {
31482ab8320SBrandon Kim             bufferImpl->getQueueOffset();
31582ab8320SBrandon Kim         } catch (const std::runtime_error& e) {
31682ab8320SBrandon Kim             EXPECT_STREQ(
31782ab8320SBrandon Kim                 e.what(),
31882ab8320SBrandon Kim                 "[getQueueOffset] runtime ueRegionSize '79' did not match "
31982ab8320SBrandon Kim                 "compile-time ueRegionSize '80'. This indicates that the"
32082ab8320SBrandon Kim                 " buffer was corrupted");
32182ab8320SBrandon Kim             throw;
32282ab8320SBrandon Kim         },
32382ab8320SBrandon Kim         std::runtime_error);
32482ab8320SBrandon Kim }
32582ab8320SBrandon Kim 
3269836cfa6SBrandon Kim class BufferWraparoundReadTest : public BufferTest
3279836cfa6SBrandon Kim {
3289836cfa6SBrandon Kim   protected:
BufferWraparoundReadTest()3299836cfa6SBrandon Kim     BufferWraparoundReadTest()
3309836cfa6SBrandon Kim     {
3314662b1bdSBrandon Kim         initializeFuncMock();
3324662b1bdSBrandon Kim     }
initializeFuncMock()3334662b1bdSBrandon Kim     void initializeFuncMock()
3344662b1bdSBrandon Kim     {
3359836cfa6SBrandon Kim         // Initialize the memory and the cachedBufferHeader
3369836cfa6SBrandon Kim         InSequence s;
3379836cfa6SBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
3389836cfa6SBrandon Kim             .WillOnce(Return(testRegionSize));
3393def3c8eSBrandon Kim         const std::vector<uint8_t> emptyArray(testQueueSize, 0);
3409836cfa6SBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr,
3419836cfa6SBrandon Kim                     write(0, ElementsAreArray(emptyArray)))
3423def3c8eSBrandon Kim             .WillOnce(Return(testQueueSize));
3439836cfa6SBrandon Kim 
3444662b1bdSBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
3459836cfa6SBrandon Kim             .WillOnce(Return(bufferHeaderSize));
3461a64356fSPatrick Williams         EXPECT_NO_THROW(
3471a64356fSPatrick Williams             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
3481a64356fSPatrick Williams                                    testUeRegionSize, testMagicNumber));
3499836cfa6SBrandon Kim     }
350271d2313SBrandon Kim     static constexpr size_t expectedWriteSize = 3;
351271d2313SBrandon Kim     static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
3529836cfa6SBrandon Kim     static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
3534662b1bdSBrandon Kim 
3541a64356fSPatrick Williams     static constexpr size_t testMaxOffset =
3551a64356fSPatrick Williams         testQueueSize - testUeRegionSize - sizeof(struct CircularBufferHeader);
3564662b1bdSBrandon Kim     uint8_t* testInitializationHeaderPtr =
3574662b1bdSBrandon Kim         reinterpret_cast<uint8_t*>(&testInitializationHeader);
3589836cfa6SBrandon Kim };
3599836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,GetMaxOffsetPassTest)36082ab8320SBrandon Kim TEST_F(BufferWraparoundReadTest, GetMaxOffsetPassTest)
3613def3c8eSBrandon Kim {
3623def3c8eSBrandon Kim     EXPECT_EQ(bufferImpl->getMaxOffset(), testMaxOffset);
3633def3c8eSBrandon Kim }
3643def3c8eSBrandon Kim 
TEST_F(BufferWraparoundReadTest,GetQueueOffsetPassTest)36582ab8320SBrandon Kim TEST_F(BufferWraparoundReadTest, GetQueueOffsetPassTest)
36682ab8320SBrandon Kim {
36782ab8320SBrandon Kim     EXPECT_EQ(bufferImpl->getQueueOffset(),
36882ab8320SBrandon Kim               bufferHeaderSize + testUeRegionSize);
36982ab8320SBrandon Kim }
37082ab8320SBrandon Kim 
TEST_F(BufferWraparoundReadTest,ParamsTooBigFail)37135d4335eSBrandon Kim TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
3729836cfa6SBrandon Kim {
3739836cfa6SBrandon Kim     InSequence s;
3743def3c8eSBrandon Kim     size_t tooBigOffset = testMaxOffset + 1;
3759836cfa6SBrandon Kim     EXPECT_THROW(
3769836cfa6SBrandon Kim         try {
37735d4335eSBrandon Kim             bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
3789836cfa6SBrandon Kim         } catch (const std::runtime_error& e) {
37926660e9bSBrandon Kim             EXPECT_STREQ(
38026660e9bSBrandon Kim                 e.what(),
3813def3c8eSBrandon Kim                 "[wraparoundRead] relativeOffset '385' was bigger than maxOffset '384'");
38235d4335eSBrandon Kim             throw;
38335d4335eSBrandon Kim         },
38435d4335eSBrandon Kim         std::runtime_error);
38535d4335eSBrandon Kim 
3863def3c8eSBrandon Kim     size_t tooBigLength = testMaxOffset + 1;
38735d4335eSBrandon Kim     EXPECT_THROW(
38835d4335eSBrandon Kim         try {
38935d4335eSBrandon Kim             bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
39035d4335eSBrandon Kim         } catch (const std::runtime_error& e) {
3913def3c8eSBrandon Kim             EXPECT_STREQ(e.what(), "[wraparoundRead] length '385' was bigger "
3923def3c8eSBrandon Kim                                    "than maxOffset '384'");
3939836cfa6SBrandon Kim             throw;
3949836cfa6SBrandon Kim         },
3959836cfa6SBrandon Kim         std::runtime_error);
3969836cfa6SBrandon Kim }
3979836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,NoWrapAroundReadFails)39835d4335eSBrandon Kim TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
3999836cfa6SBrandon Kim {
4009836cfa6SBrandon Kim     InSequence s;
40135d4335eSBrandon Kim     size_t testLength = 0x10;
40235d4335eSBrandon Kim     size_t testOffset = 0x20;
40335d4335eSBrandon Kim 
40435d4335eSBrandon Kim     // Fail the first read
40535d4335eSBrandon Kim     std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
40635d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
40735d4335eSBrandon Kim                 read(testOffset + expectedqueueOffset, testLength))
40835d4335eSBrandon Kim         .WillOnce(Return(shortTestBytesRead));
40935d4335eSBrandon Kim 
4109836cfa6SBrandon Kim     EXPECT_THROW(
4119836cfa6SBrandon Kim         try {
41235d4335eSBrandon Kim             bufferImpl->wraparoundRead(testOffset, testLength);
4139836cfa6SBrandon Kim         } catch (const std::runtime_error& e) {
41435d4335eSBrandon Kim             EXPECT_STREQ(e.what(),
41535d4335eSBrandon Kim                          "[wraparoundRead] Read '15' which was not the "
41635d4335eSBrandon Kim                          "requested length of '16'");
4179836cfa6SBrandon Kim             throw;
4189836cfa6SBrandon Kim         },
4199836cfa6SBrandon Kim         std::runtime_error);
4209836cfa6SBrandon Kim }
4219836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,NoWrapAroundReadPass)4229836cfa6SBrandon Kim TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
4239836cfa6SBrandon Kim {
4249836cfa6SBrandon Kim     InSequence s;
4259836cfa6SBrandon Kim     size_t testLength = 0x10;
42635d4335eSBrandon Kim     size_t testOffset = 0x20;
4279836cfa6SBrandon Kim 
4289836cfa6SBrandon Kim     // Successfully read all the requested length without a wrap around
4299836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesRead(testLength);
43035d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
43135d4335eSBrandon Kim                 read(testOffset + expectedqueueOffset, testLength))
4329836cfa6SBrandon Kim         .WillOnce(Return(testBytesRead));
4339836cfa6SBrandon Kim 
4349836cfa6SBrandon Kim     // Call to updateReadPtr is triggered
4359836cfa6SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{
436271d2313SBrandon Kim         static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
4379836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
4389836cfa6SBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
4399836cfa6SBrandon Kim         .WillOnce(Return(expectedWriteSize));
4409836cfa6SBrandon Kim 
4419836cfa6SBrandon Kim     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
4429836cfa6SBrandon Kim                 ElementsAreArray(testBytesRead));
44335d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
44435d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
44535d4335eSBrandon Kim     // The bmcReadPtr should have been updated
44635d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
44735d4335eSBrandon Kim               testOffset + testLength);
4489836cfa6SBrandon Kim }
4499836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,WrapAroundReadFails)4509836cfa6SBrandon Kim TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
4519836cfa6SBrandon Kim {
4529836cfa6SBrandon Kim     InSequence s;
4539836cfa6SBrandon Kim     size_t testBytesLeft = 3;
4549836cfa6SBrandon Kim     size_t testLength = 0x10;
4553def3c8eSBrandon Kim     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
4569836cfa6SBrandon Kim 
45735d4335eSBrandon Kim     // Read until the end of the queue
4589836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
45935d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
46035d4335eSBrandon Kim                                             testLength - testBytesLeft))
4619836cfa6SBrandon Kim         .WillOnce(Return(testBytesReadShort));
4629836cfa6SBrandon Kim 
4639836cfa6SBrandon Kim     // Read 1 byte short after wraparound
4649836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
4659836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
4669836cfa6SBrandon Kim         .WillOnce(Return(testBytesLeftReadShort));
4679836cfa6SBrandon Kim 
4689836cfa6SBrandon Kim     EXPECT_THROW(
4699836cfa6SBrandon Kim         try {
4709836cfa6SBrandon Kim             bufferImpl->wraparoundRead(testOffset, testLength);
4719836cfa6SBrandon Kim         } catch (const std::runtime_error& e) {
47235d4335eSBrandon Kim             EXPECT_STREQ(
47335d4335eSBrandon Kim                 e.what(),
47435d4335eSBrandon Kim                 "[wraparoundRead] Buffer wrapped around but read '2' which was "
47535d4335eSBrandon Kim                 "not the requested lenght of '3'");
4769836cfa6SBrandon Kim             throw;
4779836cfa6SBrandon Kim         },
4789836cfa6SBrandon Kim         std::runtime_error);
4799836cfa6SBrandon Kim }
4809836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,WrapAroundReadPasses)4819836cfa6SBrandon Kim TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
4829836cfa6SBrandon Kim {
4839836cfa6SBrandon Kim     InSequence s;
4849836cfa6SBrandon Kim     size_t testBytesLeft = 3;
4859836cfa6SBrandon Kim     size_t testLength = 0x10;
4863def3c8eSBrandon Kim     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
4879836cfa6SBrandon Kim 
48835d4335eSBrandon Kim     // Read to the end of the queue
4899836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
4909836cfa6SBrandon Kim                                                  9,  8,  7,  6,  5,  4};
49135d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
49235d4335eSBrandon Kim                                             testLength - testBytesLeft))
4939836cfa6SBrandon Kim         .WillOnce(Return(testBytesReadFirst));
4949836cfa6SBrandon Kim 
4959836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
4969836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
4979836cfa6SBrandon Kim         .WillOnce(Return(testBytesReadSecond));
4989836cfa6SBrandon Kim 
4999836cfa6SBrandon Kim     // Call to updateReadPtr is triggered
5009836cfa6SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{
501271d2313SBrandon Kim         static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
5029836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
5039836cfa6SBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
5049836cfa6SBrandon Kim         .WillOnce(Return(expectedWriteSize));
5059836cfa6SBrandon Kim 
5069836cfa6SBrandon Kim     std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
5079836cfa6SBrandon Kim                                                8,  7,  6,  5,  4,  3,  2,  1};
5089836cfa6SBrandon Kim     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
5099836cfa6SBrandon Kim                 ElementsAreArray(expectedBytes));
51035d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
51135d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
51235d4335eSBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
51335d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
51435d4335eSBrandon Kim               testBytesLeft);
5159836cfa6SBrandon Kim }
5169836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,WrapAroundCornerCasePass)5174662b1bdSBrandon Kim TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
5184662b1bdSBrandon Kim {
5194662b1bdSBrandon Kim     InSequence s;
5204662b1bdSBrandon Kim     size_t testBytesLeft = 0;
5214662b1bdSBrandon Kim     size_t testLength = 4;
5223def3c8eSBrandon Kim     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
5234662b1bdSBrandon Kim 
5244662b1bdSBrandon Kim     // Read to the very end of the queue
5254662b1bdSBrandon Kim     std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
5264662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
5274662b1bdSBrandon Kim                 read(testOffset + expectedqueueOffset, testLength))
5284662b1bdSBrandon Kim         .WillOnce(Return(testBytes));
5294662b1bdSBrandon Kim 
5304662b1bdSBrandon Kim     // Call to updateReadPtr is triggered, since we read to the very end of the
5314662b1bdSBrandon Kim     // buffer, update the readPtr up around to 0
532271d2313SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
5334662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
5344662b1bdSBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
5354662b1bdSBrandon Kim         .WillOnce(Return(expectedWriteSize));
5364662b1bdSBrandon Kim 
5374662b1bdSBrandon Kim     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
5384662b1bdSBrandon Kim                 ElementsAreArray(testBytes));
5394662b1bdSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
5404662b1bdSBrandon Kim         bufferImpl->getCachedBufferHeader();
5414662b1bdSBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
5424662b1bdSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
5434662b1bdSBrandon Kim               0);
5444662b1bdSBrandon Kim }
5454662b1bdSBrandon Kim 
54640ce08e1SBrandon Kim class BufferEntryTest : public BufferWraparoundReadTest
5477bac2d69SBrandon Kim {
5487bac2d69SBrandon Kim   protected:
BufferEntryTest()54940ce08e1SBrandon Kim     BufferEntryTest()
5507bac2d69SBrandon Kim     {
5517bac2d69SBrandon Kim         testEntryHeader.sequenceId = testSequenceId;
5527bac2d69SBrandon Kim         testEntryHeader.entrySize = testEntrySize;
5537bac2d69SBrandon Kim         testEntryHeader.checksum = testChecksum;
5547bac2d69SBrandon Kim         testEntryHeader.rdeCommandType = testRdeCommandType;
5557bac2d69SBrandon Kim     }
55640ce08e1SBrandon Kim     ~BufferEntryTest() override = default;
5577bac2d69SBrandon Kim 
wraparoundReadMock(const uint32_t relativeOffset,std::span<std::uint8_t> expetedBytesOutput)55835d4335eSBrandon Kim     void wraparoundReadMock(const uint32_t relativeOffset,
55935d4335eSBrandon Kim                             std::span<std::uint8_t> expetedBytesOutput)
56035d4335eSBrandon Kim     {
56135d4335eSBrandon Kim         InSequence s;
5623def3c8eSBrandon Kim         const uint32_t queueSizeToQueueEnd = testMaxOffset - relativeOffset;
56335d4335eSBrandon Kim 
56435d4335eSBrandon Kim         // This will wrap, split the read mocks in 2
56535d4335eSBrandon Kim         if (expetedBytesOutput.size() > queueSizeToQueueEnd)
56635d4335eSBrandon Kim         {
56735d4335eSBrandon Kim             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
56835d4335eSBrandon Kim                 .WillOnce(Return(std::vector<std::uint8_t>(
56935d4335eSBrandon Kim                     expetedBytesOutput.begin(),
57035d4335eSBrandon Kim                     expetedBytesOutput.begin() + queueSizeToQueueEnd)));
57135d4335eSBrandon Kim             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
57235d4335eSBrandon Kim                 .WillOnce(Return(std::vector<std::uint8_t>(
57335d4335eSBrandon Kim                     expetedBytesOutput.begin() + queueSizeToQueueEnd,
57435d4335eSBrandon Kim                     expetedBytesOutput.end())));
57535d4335eSBrandon Kim         }
57635d4335eSBrandon Kim         else
5777bac2d69SBrandon Kim         {
5787bac2d69SBrandon Kim             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
5797bac2d69SBrandon Kim                 .WillOnce(Return(std::vector<std::uint8_t>(
5807bac2d69SBrandon Kim                     expetedBytesOutput.begin(), expetedBytesOutput.end())));
58135d4335eSBrandon Kim         }
5827bac2d69SBrandon Kim 
5837bac2d69SBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
5847bac2d69SBrandon Kim             .WillOnce(Return(expectedWriteSize));
5857bac2d69SBrandon Kim     }
5867bac2d69SBrandon Kim 
5877bac2d69SBrandon Kim     static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
5887bac2d69SBrandon Kim     static constexpr uint16_t testSequenceId = 0;
5897bac2d69SBrandon Kim     static constexpr uint16_t testEntrySize = 0x20;
5907bac2d69SBrandon Kim     static constexpr uint8_t testRdeCommandType = 0x01;
591f0e4adc9SBrandon Kim     // Calculated checksum for the header
592f0e4adc9SBrandon Kim     static constexpr uint8_t testChecksum =
593f0e4adc9SBrandon Kim         (testSequenceId ^ testEntrySize ^ testRdeCommandType);
594613ba537SBrandon Kim     size_t testOffset = 0x0;
5957bac2d69SBrandon Kim 
596*c20d1216SPatrick Williams     struct QueueEntryHeader testEntryHeader{};
5977bac2d69SBrandon Kim };
5987bac2d69SBrandon Kim 
TEST_F(BufferEntryTest,ReadEntryHeaderPass)59940ce08e1SBrandon Kim TEST_F(BufferEntryTest, ReadEntryHeaderPass)
6007bac2d69SBrandon Kim {
6017bac2d69SBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
6027bac2d69SBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
6037bac2d69SBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
60435d4335eSBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
605613ba537SBrandon Kim     EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
60635d4335eSBrandon Kim     // Check the bmcReadPtr
60735d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
60835d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
60935d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
61035d4335eSBrandon Kim               testOffset + testEntryHeaderVector.size());
6117bac2d69SBrandon Kim }
6127bac2d69SBrandon Kim 
TEST_F(BufferEntryTest,ReadEntryChecksumFail)61340ce08e1SBrandon Kim TEST_F(BufferEntryTest, ReadEntryChecksumFail)
61440ce08e1SBrandon Kim {
61540ce08e1SBrandon Kim     InSequence s;
616f0e4adc9SBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
61740ce08e1SBrandon Kim     // Offset the checksum by 1
618f0e4adc9SBrandon Kim     testEntryHeader.checksum += 1;
61940ce08e1SBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
62040ce08e1SBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
62140ce08e1SBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
62235d4335eSBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
62335d4335eSBrandon Kim     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
62440ce08e1SBrandon Kim     EXPECT_THROW(
625613ba537SBrandon Kim         try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
626f0e4adc9SBrandon Kim             // Calculation: testChecksum (0x21) XOR (0x22) = 3
62740ce08e1SBrandon Kim             EXPECT_STREQ(e.what(),
628f0e4adc9SBrandon Kim                          "[readEntry] Checksum was '3', expected '0'");
62940ce08e1SBrandon Kim             throw;
63040ce08e1SBrandon Kim         },
63140ce08e1SBrandon Kim         std::runtime_error);
63240ce08e1SBrandon Kim }
63340ce08e1SBrandon Kim 
TEST_F(BufferEntryTest,ReadEntryPassWraparound)634613ba537SBrandon Kim TEST_F(BufferEntryTest, ReadEntryPassWraparound)
63540ce08e1SBrandon Kim {
63640ce08e1SBrandon Kim     InSequence s;
637f0e4adc9SBrandon Kim     // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
638f0e4adc9SBrandon Kim     // (20 times) = 0 therefore leave the checksum as is
63935d4335eSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
64040ce08e1SBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
64140ce08e1SBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
64240ce08e1SBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
643613ba537SBrandon Kim     // Set testOffset so that we can test the wraparound here at the header and
644613ba537SBrandon Kim     // update the readPtr
6453def3c8eSBrandon Kim     testOffset = testMaxOffset - 1;
646613ba537SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
647613ba537SBrandon Kim         .WillOnce(Return(expectedWriteSize));
648613ba537SBrandon Kim     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
649613ba537SBrandon Kim 
65035d4335eSBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
65135d4335eSBrandon Kim     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
65240ce08e1SBrandon Kim 
65340ce08e1SBrandon Kim     EntryPair testedEntryPair;
654613ba537SBrandon Kim     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
65540ce08e1SBrandon Kim     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
65640ce08e1SBrandon Kim     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
65735d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
65835d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
65935d4335eSBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
66035d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
661613ba537SBrandon Kim               entryHeaderSize + testEntrySize - 1);
662613ba537SBrandon Kim 
663613ba537SBrandon Kim     // Set testOffset so that we can test the wraparound here as well on our
664613ba537SBrandon Kim     // second read for the entry (by 1 byte)
6653def3c8eSBrandon Kim     testOffset = testMaxOffset - entryHeaderSize - 1;
666613ba537SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
667613ba537SBrandon Kim         .WillOnce(Return(expectedWriteSize));
668613ba537SBrandon Kim     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
669613ba537SBrandon Kim 
670613ba537SBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
671613ba537SBrandon Kim     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
672613ba537SBrandon Kim 
673613ba537SBrandon Kim     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
674613ba537SBrandon Kim     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
675613ba537SBrandon Kim     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
676613ba537SBrandon Kim     cachedBufferHeader = bufferImpl->getCachedBufferHeader();
677613ba537SBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
678613ba537SBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
67935d4335eSBrandon Kim               testEntrySize - 1);
68040ce08e1SBrandon Kim }
68140ce08e1SBrandon Kim 
6824662b1bdSBrandon Kim class BufferReadErrorLogsTest : public BufferEntryTest
6834662b1bdSBrandon Kim {
6844662b1bdSBrandon Kim   protected:
6854662b1bdSBrandon Kim     BufferReadErrorLogsTest() = default;
6864662b1bdSBrandon Kim 
6874662b1bdSBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
6884662b1bdSBrandon Kim     size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
6894662b1bdSBrandon Kim };
6904662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,PtrsTooBigFail)6914662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
6924662b1bdSBrandon Kim {
6934662b1bdSBrandon Kim     InSequence s;
6944662b1bdSBrandon Kim     // Set the biosWritePtr too big
6954662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr =
6963def3c8eSBrandon Kim         boost::endian::native_to_little((testMaxOffset + 1));
6974662b1bdSBrandon Kim     initializeFuncMock();
6984662b1bdSBrandon Kim 
6994662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7001a64356fSPatrick Williams         .WillOnce(Return(std::vector<uint8_t>(
7011a64356fSPatrick Williams             testInitializationHeaderPtr,
7021a64356fSPatrick Williams             testInitializationHeaderPtr + bufferHeaderSize)));
7034662b1bdSBrandon Kim     EXPECT_THROW(
7044662b1bdSBrandon Kim         try {
7054662b1bdSBrandon Kim             bufferImpl->readErrorLogs();
7064662b1bdSBrandon Kim         } catch (const std::runtime_error& e) {
7074662b1bdSBrandon Kim             EXPECT_STREQ(e.what(),
7083def3c8eSBrandon Kim                          "[readErrorLogs] currentBiosWritePtr was '385' "
7093def3c8eSBrandon Kim                          "which was bigger than maxOffset '384'");
7104662b1bdSBrandon Kim             throw;
7114662b1bdSBrandon Kim         },
7124662b1bdSBrandon Kim         std::runtime_error);
7134662b1bdSBrandon Kim 
7144662b1bdSBrandon Kim     // Reset the biosWritePtr and set the bmcReadPtr too big
7154662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr = 0;
7164662b1bdSBrandon Kim     initializeFuncMock();
7174662b1bdSBrandon Kim     testInitializationHeader.bmcReadPtr =
7183def3c8eSBrandon Kim         boost::endian::native_to_little((testMaxOffset + 1));
7194662b1bdSBrandon Kim     initializeFuncMock();
7204662b1bdSBrandon Kim 
7214662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7221a64356fSPatrick Williams         .WillOnce(Return(std::vector<uint8_t>(
7231a64356fSPatrick Williams             testInitializationHeaderPtr,
7241a64356fSPatrick Williams             testInitializationHeaderPtr + bufferHeaderSize)));
7254662b1bdSBrandon Kim     EXPECT_THROW(
7264662b1bdSBrandon Kim         try {
7274662b1bdSBrandon Kim             bufferImpl->readErrorLogs();
7284662b1bdSBrandon Kim         } catch (const std::runtime_error& e) {
7293def3c8eSBrandon Kim             EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '385' "
7303def3c8eSBrandon Kim                                    "which was bigger than maxOffset '384'");
7314662b1bdSBrandon Kim             throw;
7324662b1bdSBrandon Kim         },
7334662b1bdSBrandon Kim         std::runtime_error);
7344662b1bdSBrandon Kim }
7354662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,IdenticalPtrsPass)7364662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
7374662b1bdSBrandon Kim {
7384662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7391a64356fSPatrick Williams         .WillOnce(Return(std::vector<uint8_t>(
7401a64356fSPatrick Williams             testInitializationHeaderPtr,
7411a64356fSPatrick Williams             testInitializationHeaderPtr + bufferHeaderSize)));
7424662b1bdSBrandon Kim     EXPECT_NO_THROW(bufferImpl->readErrorLogs());
7434662b1bdSBrandon Kim }
7444662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,NoWraparoundPass)7454662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
7464662b1bdSBrandon Kim {
7474662b1bdSBrandon Kim     InSequence s;
7484662b1bdSBrandon Kim     // Set the biosWritePtr to 1 entryHeader + entry size
7494662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr =
7504662b1bdSBrandon Kim         boost::endian::native_to_little((entryAndHeaderSize));
7514662b1bdSBrandon Kim     initializeFuncMock();
7524662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7531a64356fSPatrick Williams         .WillOnce(Return(std::vector<uint8_t>(
7541a64356fSPatrick Williams             testInitializationHeaderPtr,
7551a64356fSPatrick Williams             testInitializationHeaderPtr + bufferHeaderSize)));
7564662b1bdSBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
7574662b1bdSBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
7584662b1bdSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
7594662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
7604662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
7614662b1bdSBrandon Kim 
7624662b1bdSBrandon Kim     std::vector<EntryPair> entryPairs;
7634662b1bdSBrandon Kim     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
7644662b1bdSBrandon Kim 
7654662b1bdSBrandon Kim     // Check that we only read one entryPair and that the content is correct
7661a3dc60dSBrandon Kim     EXPECT_EQ(entryPairs.size(), 1U);
7674662b1bdSBrandon Kim     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
7684662b1bdSBrandon Kim     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
7694662b1bdSBrandon Kim }
7704662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,WraparoundMultiplEntryPass)7714662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
7724662b1bdSBrandon Kim {
7734662b1bdSBrandon Kim     InSequence s;
7744662b1bdSBrandon Kim     // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
7753def3c8eSBrandon Kim     uint32_t entryAndHeaderSizeAwayFromEnd = testMaxOffset - entryAndHeaderSize;
7764662b1bdSBrandon Kim     testInitializationHeader.bmcReadPtr =
7774662b1bdSBrandon Kim         boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
7784662b1bdSBrandon Kim     // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
7794662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr = entryAndHeaderSize;
7804662b1bdSBrandon Kim     initializeFuncMock();
7814662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7821a64356fSPatrick Williams         .WillOnce(Return(std::vector<uint8_t>(
7831a64356fSPatrick Williams             testInitializationHeaderPtr,
7841a64356fSPatrick Williams             testInitializationHeaderPtr + bufferHeaderSize)));
7854662b1bdSBrandon Kim 
7864662b1bdSBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
7874662b1bdSBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
7884662b1bdSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
7894662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
7904662b1bdSBrandon Kim                        testEntryHeaderVector);
7914662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
7924662b1bdSBrandon Kim                            entryHeaderSize,
7934662b1bdSBrandon Kim                        testEntryVector);
7944662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
7954662b1bdSBrandon Kim                        testEntryHeaderVector);
7964662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
7974662b1bdSBrandon Kim                            entryHeaderSize,
7984662b1bdSBrandon Kim                        testEntryVector);
7994662b1bdSBrandon Kim 
8004662b1bdSBrandon Kim     std::vector<EntryPair> entryPairs;
8014662b1bdSBrandon Kim     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
8024662b1bdSBrandon Kim 
8034662b1bdSBrandon Kim     // Check that we only read one entryPair and that the content is correct
8044662b1bdSBrandon Kim     EXPECT_EQ(entryPairs.size(), 2);
8054662b1bdSBrandon Kim     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
8064662b1bdSBrandon Kim     EXPECT_EQ(entryPairs[1].first, testEntryHeader);
8074662b1bdSBrandon Kim     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
8084662b1bdSBrandon Kim     EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
8094662b1bdSBrandon Kim }
8104662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,WraparoundMismatchingPtrsFail)8114662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
8124662b1bdSBrandon Kim {
8134662b1bdSBrandon Kim     InSequence s;
8144662b1bdSBrandon Kim     testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
8154662b1bdSBrandon Kim     // Make the biosWritePtr intentially 1 smaller than expected
8164662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr =
8174662b1bdSBrandon Kim         boost::endian::native_to_little(entryAndHeaderSize - 1);
8184662b1bdSBrandon Kim     initializeFuncMock();
8194662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
8201a64356fSPatrick Williams         .WillOnce(Return(std::vector<uint8_t>(
8211a64356fSPatrick Williams             testInitializationHeaderPtr,
8221a64356fSPatrick Williams             testInitializationHeaderPtr + bufferHeaderSize)));
8234662b1bdSBrandon Kim 
8244662b1bdSBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
8254662b1bdSBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
8264662b1bdSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
8274662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
8284662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
8294662b1bdSBrandon Kim 
8304662b1bdSBrandon Kim     EXPECT_THROW(
8314662b1bdSBrandon Kim         try {
8324662b1bdSBrandon Kim             bufferImpl->readErrorLogs();
8334662b1bdSBrandon Kim         } catch (const std::runtime_error& e) {
8344662b1bdSBrandon Kim             EXPECT_STREQ(
8354662b1bdSBrandon Kim                 e.what(),
8364662b1bdSBrandon Kim                 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
8374662b1bdSBrandon Kim                 "are not identical after reading through all the logs");
8384662b1bdSBrandon Kim             throw;
8394662b1bdSBrandon Kim         },
8404662b1bdSBrandon Kim         std::runtime_error);
8414662b1bdSBrandon Kim }
8424662b1bdSBrandon Kim 
843fcbc3db1SBrandon Kim } // namespace
844fcbc3db1SBrandon Kim } // namespace bios_bmc_smm_error_logger
845