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 
54fcbc3db1SBrandon Kim     struct CircularBufferHeader testInitializationHeader
55fcbc3db1SBrandon Kim     {};
56fcbc3db1SBrandon Kim 
57fcbc3db1SBrandon Kim     std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
58fcbc3db1SBrandon Kim     DataInterfaceMock* dataInterfaceMockPtr;
59fcbc3db1SBrandon Kim     std::unique_ptr<BufferImpl> bufferImpl;
60fcbc3db1SBrandon Kim };
61fcbc3db1SBrandon Kim 
TEST_F(BufferTest,BufferInitializeEraseFail)62fcbc3db1SBrandon Kim TEST_F(BufferTest, BufferInitializeEraseFail)
63fcbc3db1SBrandon Kim {
64fcbc3db1SBrandon Kim     InSequence s;
6526660e9bSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
6626660e9bSBrandon Kim         .WillOnce(Return(testRegionSize));
6726660e9bSBrandon Kim     EXPECT_THROW(
6826660e9bSBrandon Kim         try {
6926660e9bSBrandon Kim             // Test too big of a proposed buffer compared to the memori size
703def3c8eSBrandon Kim             uint16_t bigQueueSize = 0x201;
7126660e9bSBrandon Kim             uint16_t bigUeRegionSize = 0x50;
7226660e9bSBrandon Kim             bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
7326660e9bSBrandon Kim                                    bigUeRegionSize, testMagicNumber);
7426660e9bSBrandon Kim         } catch (const std::runtime_error& e) {
7526660e9bSBrandon Kim             EXPECT_STREQ(
7626660e9bSBrandon Kim                 e.what(),
773def3c8eSBrandon Kim                 "[initialize] Proposed queue size '513' is bigger than the BMC's allocated MMIO region of '512'");
7826660e9bSBrandon Kim             throw;
7926660e9bSBrandon Kim         },
8026660e9bSBrandon Kim         std::runtime_error);
8126660e9bSBrandon Kim     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
82fcbc3db1SBrandon Kim 
83fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
84fcbc3db1SBrandon Kim         .WillOnce(Return(testRegionSize));
853def3c8eSBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
86fcbc3db1SBrandon Kim     // Return a smaller write than the intended testRegionSize to test the error
87fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
883def3c8eSBrandon Kim         .WillOnce(Return(testQueueSize - 1));
89fcbc3db1SBrandon Kim     EXPECT_THROW(
90fcbc3db1SBrandon Kim         try {
91fcbc3db1SBrandon Kim             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
92fcbc3db1SBrandon Kim                                    testUeRegionSize, testMagicNumber);
93fcbc3db1SBrandon Kim         } catch (const std::runtime_error& e) {
943def3c8eSBrandon Kim             EXPECT_STREQ(e.what(), "[initialize] Only erased '511'");
95fcbc3db1SBrandon Kim             throw;
96fcbc3db1SBrandon Kim         },
97fcbc3db1SBrandon Kim         std::runtime_error);
9860cab57fSBrandon Kim     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
99fcbc3db1SBrandon Kim 
100fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
101fcbc3db1SBrandon Kim         .WillOnce(Return(testRegionSize));
102fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
1033def3c8eSBrandon Kim         .WillOnce(Return(testQueueSize));
104fcbc3db1SBrandon Kim     // Return a smaller write than the intended initializationHeader to test the
105fcbc3db1SBrandon Kim     // error
106fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
107fcbc3db1SBrandon Kim     EXPECT_THROW(
108fcbc3db1SBrandon Kim         try {
109fcbc3db1SBrandon Kim             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
110fcbc3db1SBrandon Kim                                    testUeRegionSize, testMagicNumber);
111fcbc3db1SBrandon Kim         } catch (const std::runtime_error& e) {
11226660e9bSBrandon Kim             EXPECT_STREQ(e.what(),
11326660e9bSBrandon Kim                          "[initialize] Only wrote '0' bytes of the header");
114fcbc3db1SBrandon Kim             throw;
115fcbc3db1SBrandon Kim         },
116fcbc3db1SBrandon Kim         std::runtime_error);
11760cab57fSBrandon Kim     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
118fcbc3db1SBrandon Kim }
119fcbc3db1SBrandon Kim 
TEST_F(BufferTest,BufferInitializePass)120fcbc3db1SBrandon Kim TEST_F(BufferTest, BufferInitializePass)
121fcbc3db1SBrandon Kim {
122fcbc3db1SBrandon Kim     InSequence s;
123fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
124fcbc3db1SBrandon Kim         .WillOnce(Return(testRegionSize));
1253def3c8eSBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
126fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
1273def3c8eSBrandon Kim         .WillOnce(Return(testQueueSize));
128fcbc3db1SBrandon Kim 
129fcbc3db1SBrandon Kim     uint8_t* testInitializationHeaderPtr =
130fcbc3db1SBrandon Kim         reinterpret_cast<uint8_t*>(&testInitializationHeader);
131fcbc3db1SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
132fcbc3db1SBrandon Kim                 write(0, ElementsAreArray(testInitializationHeaderPtr,
13317ee1a93SBrandon Kim                                           bufferHeaderSize)))
13417ee1a93SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
135fcbc3db1SBrandon Kim     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
136fcbc3db1SBrandon Kim                                            testQueueSize, testUeRegionSize,
137fcbc3db1SBrandon Kim                                            testMagicNumber));
13860cab57fSBrandon Kim     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
139fcbc3db1SBrandon Kim }
140fcbc3db1SBrandon Kim 
TEST_F(BufferTest,BufferHeaderReadFail)14117ee1a93SBrandon Kim TEST_F(BufferTest, BufferHeaderReadFail)
14217ee1a93SBrandon Kim {
14317ee1a93SBrandon Kim     std::vector<std::uint8_t> testBytesRead{};
14417ee1a93SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
14517ee1a93SBrandon Kim         .WillOnce(Return(testBytesRead));
14617ee1a93SBrandon Kim     EXPECT_THROW(
14717ee1a93SBrandon Kim         try {
14817ee1a93SBrandon Kim             bufferImpl->readBufferHeader();
14917ee1a93SBrandon Kim         } catch (const std::runtime_error& e) {
15017ee1a93SBrandon Kim             EXPECT_STREQ(e.what(),
15117ee1a93SBrandon Kim                          "Buffer header read only read '0', expected '48'");
15217ee1a93SBrandon Kim             throw;
15317ee1a93SBrandon Kim         },
15417ee1a93SBrandon Kim         std::runtime_error);
15517ee1a93SBrandon Kim }
15617ee1a93SBrandon Kim 
TEST_F(BufferTest,BufferHeaderReadPass)15717ee1a93SBrandon Kim TEST_F(BufferTest, BufferHeaderReadPass)
15817ee1a93SBrandon Kim {
15917ee1a93SBrandon Kim     uint8_t* testInitializationHeaderPtr =
16017ee1a93SBrandon Kim         reinterpret_cast<uint8_t*>(&testInitializationHeader);
16117ee1a93SBrandon Kim     std::vector<uint8_t> testInitializationHeaderVector(
16217ee1a93SBrandon Kim         testInitializationHeaderPtr,
16317ee1a93SBrandon Kim         testInitializationHeaderPtr + bufferHeaderSize);
16417ee1a93SBrandon Kim 
16517ee1a93SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
16617ee1a93SBrandon Kim         .WillOnce(Return(testInitializationHeaderVector));
16717ee1a93SBrandon Kim     EXPECT_NO_THROW(bufferImpl->readBufferHeader());
16817ee1a93SBrandon Kim     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
16917ee1a93SBrandon Kim }
17017ee1a93SBrandon Kim 
TEST_F(BufferTest,BufferUpdateReadPtrFail)171cf0b9752SBrandon Kim TEST_F(BufferTest, BufferUpdateReadPtrFail)
172cf0b9752SBrandon Kim {
173cf0b9752SBrandon Kim     // Return write size that is not 2 which is sizeof(little_uint16_t)
174cf0b9752SBrandon Kim     constexpr size_t wrongWriteSize = 1;
175cf0b9752SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
176cf0b9752SBrandon Kim         .WillOnce(Return(wrongWriteSize));
177cf0b9752SBrandon Kim     EXPECT_THROW(
178cf0b9752SBrandon Kim         try {
179cf0b9752SBrandon Kim             bufferImpl->updateReadPtr(0);
180cf0b9752SBrandon Kim         } catch (const std::runtime_error& e) {
181cf0b9752SBrandon Kim             EXPECT_STREQ(
182cf0b9752SBrandon Kim                 e.what(),
183271d2313SBrandon Kim                 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
184cf0b9752SBrandon Kim             throw;
185cf0b9752SBrandon Kim         },
186cf0b9752SBrandon Kim         std::runtime_error);
187cf0b9752SBrandon Kim }
188cf0b9752SBrandon Kim 
TEST_F(BufferTest,BufferUpdateReadPtrPass)189cf0b9752SBrandon Kim TEST_F(BufferTest, BufferUpdateReadPtrPass)
190cf0b9752SBrandon Kim {
191271d2313SBrandon Kim     constexpr size_t expectedWriteSize = 3;
192271d2313SBrandon Kim     constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
193271d2313SBrandon Kim     // Check that we truncate the highest 24bits
194cf0b9752SBrandon Kim     const uint32_t testNewReadPtr = 0x99881234;
195271d2313SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
196cf0b9752SBrandon Kim 
197cf0b9752SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
198cf0b9752SBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
199cf0b9752SBrandon Kim         .WillOnce(Return(expectedWriteSize));
200cf0b9752SBrandon Kim     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
201c49284b6SBrandon Kim 
202c49284b6SBrandon Kim     auto cachedHeader = bufferImpl->getCachedBufferHeader();
203271d2313SBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
204271d2313SBrandon Kim               0x881234);
205c49284b6SBrandon Kim }
206c49284b6SBrandon Kim 
TEST_F(BufferTest,BufferUpdateBmcFlagsFail)207c49284b6SBrandon Kim TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
208c49284b6SBrandon Kim {
209c49284b6SBrandon Kim     // Return write size that is not 4 which is sizeof(little_uint32_t)
210c49284b6SBrandon Kim     constexpr size_t wrongWriteSize = 1;
211c49284b6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
212c49284b6SBrandon Kim         .WillOnce(Return(wrongWriteSize));
213c49284b6SBrandon Kim     EXPECT_THROW(
214c49284b6SBrandon Kim         try {
215c49284b6SBrandon Kim             bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
216c49284b6SBrandon Kim         } catch (const std::runtime_error& e) {
217c49284b6SBrandon Kim             EXPECT_STREQ(
218c49284b6SBrandon Kim                 e.what(),
219c49284b6SBrandon Kim                 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
220c49284b6SBrandon Kim             throw;
221c49284b6SBrandon Kim         },
222c49284b6SBrandon Kim         std::runtime_error);
223c49284b6SBrandon Kim }
224c49284b6SBrandon Kim 
TEST_F(BufferTest,BufferUpdateBmcFlagsPass)225c49284b6SBrandon Kim TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
226c49284b6SBrandon Kim {
227c49284b6SBrandon Kim     constexpr size_t expectedWriteSize = 4;
228271d2313SBrandon Kim     constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
229c49284b6SBrandon Kim     const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
230c49284b6SBrandon Kim 
231c49284b6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
232c49284b6SBrandon Kim                 write(expectedBmcReadPtrOffset,
233c49284b6SBrandon Kim                       ElementsAreArray(expectedNewBmcFlagsVector)))
234c49284b6SBrandon Kim         .WillOnce(Return(expectedWriteSize));
235c49284b6SBrandon Kim     EXPECT_NO_THROW(
236c49284b6SBrandon Kim         bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
237c49284b6SBrandon Kim 
238c49284b6SBrandon Kim     auto cachedHeader = bufferImpl->getCachedBufferHeader();
239c49284b6SBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
240c49284b6SBrandon Kim               static_cast<uint32_t>(BmcFlags::ready));
241cf0b9752SBrandon Kim }
242cf0b9752SBrandon Kim 
TEST_F(BufferTest,GetMaxOffsetQueueSizeFail)243*82ab8320SBrandon Kim TEST_F(BufferTest, GetMaxOffsetQueueSizeFail)
244*82ab8320SBrandon Kim {
245*82ab8320SBrandon Kim     InSequence s;
246*82ab8320SBrandon Kim     static constexpr size_t wrongQueueSize = testQueueSize - 1;
247*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
248*82ab8320SBrandon Kim         .WillOnce(Return(testRegionSize));
249*82ab8320SBrandon Kim     const std::vector<uint8_t> emptyArray(wrongQueueSize, 0);
250*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
251*82ab8320SBrandon Kim         .WillOnce(Return(wrongQueueSize));
252*82ab8320SBrandon Kim 
253*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
254*82ab8320SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
255*82ab8320SBrandon Kim     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
256*82ab8320SBrandon Kim                                            wrongQueueSize, testUeRegionSize,
257*82ab8320SBrandon Kim                                            testMagicNumber));
258*82ab8320SBrandon Kim     EXPECT_THROW(
259*82ab8320SBrandon Kim         try {
260*82ab8320SBrandon Kim             bufferImpl->getMaxOffset();
261*82ab8320SBrandon Kim         } catch (const std::runtime_error& e) {
262*82ab8320SBrandon Kim             EXPECT_STREQ(e.what(),
263*82ab8320SBrandon Kim                          "[getMaxOffset] runtime queueSize '511' did not match "
264*82ab8320SBrandon Kim                          "compile-time queueSize '512'. This indicates that the"
265*82ab8320SBrandon Kim                          " buffer was corrupted");
266*82ab8320SBrandon Kim             throw;
267*82ab8320SBrandon Kim         },
268*82ab8320SBrandon Kim         std::runtime_error);
269*82ab8320SBrandon Kim }
270*82ab8320SBrandon Kim 
TEST_F(BufferTest,GetMaxOffsetUeRegionSizeFail)271*82ab8320SBrandon Kim TEST_F(BufferTest, GetMaxOffsetUeRegionSizeFail)
272*82ab8320SBrandon Kim {
273*82ab8320SBrandon Kim     InSequence s;
274*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
275*82ab8320SBrandon Kim         .WillOnce(Return(testRegionSize));
276*82ab8320SBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
277*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
278*82ab8320SBrandon Kim         .WillOnce(Return(testQueueSize));
279*82ab8320SBrandon Kim 
280*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
281*82ab8320SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
282*82ab8320SBrandon Kim     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
283*82ab8320SBrandon Kim                                            testQueueSize, testUeRegionSize + 1,
284*82ab8320SBrandon Kim                                            testMagicNumber));
285*82ab8320SBrandon Kim     EXPECT_THROW(
286*82ab8320SBrandon Kim         try {
287*82ab8320SBrandon Kim             bufferImpl->getMaxOffset();
288*82ab8320SBrandon Kim         } catch (const std::runtime_error& e) {
289*82ab8320SBrandon Kim             EXPECT_STREQ(
290*82ab8320SBrandon Kim                 e.what(),
291*82ab8320SBrandon Kim                 "[getMaxOffset] runtime ueRegionSize '81' did not match "
292*82ab8320SBrandon Kim                 "compile-time ueRegionSize '80'. This indicates that the"
293*82ab8320SBrandon Kim                 " buffer was corrupted");
294*82ab8320SBrandon Kim             throw;
295*82ab8320SBrandon Kim         },
296*82ab8320SBrandon Kim         std::runtime_error);
297*82ab8320SBrandon Kim }
298*82ab8320SBrandon Kim 
TEST_F(BufferTest,GetOffsetUeRegionSizeFail)299*82ab8320SBrandon Kim TEST_F(BufferTest, GetOffsetUeRegionSizeFail)
300*82ab8320SBrandon Kim {
301*82ab8320SBrandon Kim     InSequence s;
302*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
303*82ab8320SBrandon Kim         .WillOnce(Return(testRegionSize));
304*82ab8320SBrandon Kim     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
305*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
306*82ab8320SBrandon Kim         .WillOnce(Return(testQueueSize));
307*82ab8320SBrandon Kim 
308*82ab8320SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
309*82ab8320SBrandon Kim         .WillOnce(Return(bufferHeaderSize));
310*82ab8320SBrandon Kim     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
311*82ab8320SBrandon Kim                                            testQueueSize, testUeRegionSize - 1,
312*82ab8320SBrandon Kim                                            testMagicNumber));
313*82ab8320SBrandon Kim     EXPECT_THROW(
314*82ab8320SBrandon Kim         try {
315*82ab8320SBrandon Kim             bufferImpl->getQueueOffset();
316*82ab8320SBrandon Kim         } catch (const std::runtime_error& e) {
317*82ab8320SBrandon Kim             EXPECT_STREQ(
318*82ab8320SBrandon Kim                 e.what(),
319*82ab8320SBrandon Kim                 "[getQueueOffset] runtime ueRegionSize '79' did not match "
320*82ab8320SBrandon Kim                 "compile-time ueRegionSize '80'. This indicates that the"
321*82ab8320SBrandon Kim                 " buffer was corrupted");
322*82ab8320SBrandon Kim             throw;
323*82ab8320SBrandon Kim         },
324*82ab8320SBrandon Kim         std::runtime_error);
325*82ab8320SBrandon Kim }
326*82ab8320SBrandon Kim 
3279836cfa6SBrandon Kim class BufferWraparoundReadTest : public BufferTest
3289836cfa6SBrandon Kim {
3299836cfa6SBrandon Kim   protected:
BufferWraparoundReadTest()3309836cfa6SBrandon Kim     BufferWraparoundReadTest()
3319836cfa6SBrandon Kim     {
3324662b1bdSBrandon Kim         initializeFuncMock();
3334662b1bdSBrandon Kim     }
initializeFuncMock()3344662b1bdSBrandon Kim     void initializeFuncMock()
3354662b1bdSBrandon Kim     {
3369836cfa6SBrandon Kim         // Initialize the memory and the cachedBufferHeader
3379836cfa6SBrandon Kim         InSequence s;
3389836cfa6SBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
3399836cfa6SBrandon Kim             .WillOnce(Return(testRegionSize));
3403def3c8eSBrandon Kim         const std::vector<uint8_t> emptyArray(testQueueSize, 0);
3419836cfa6SBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr,
3429836cfa6SBrandon Kim                     write(0, ElementsAreArray(emptyArray)))
3433def3c8eSBrandon Kim             .WillOnce(Return(testQueueSize));
3449836cfa6SBrandon Kim 
3454662b1bdSBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
3469836cfa6SBrandon Kim             .WillOnce(Return(bufferHeaderSize));
3479836cfa6SBrandon Kim         EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
3489836cfa6SBrandon Kim                                                testQueueSize, testUeRegionSize,
3499836cfa6SBrandon Kim                                                testMagicNumber));
3509836cfa6SBrandon Kim     }
351271d2313SBrandon Kim     static constexpr size_t expectedWriteSize = 3;
352271d2313SBrandon Kim     static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
3539836cfa6SBrandon Kim     static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
3544662b1bdSBrandon Kim 
355b8125763SPatrick Williams     static constexpr size_t testMaxOffset = testQueueSize - testUeRegionSize -
356b8125763SPatrick Williams                                             sizeof(struct CircularBufferHeader);
3574662b1bdSBrandon Kim     uint8_t* testInitializationHeaderPtr =
3584662b1bdSBrandon Kim         reinterpret_cast<uint8_t*>(&testInitializationHeader);
3599836cfa6SBrandon Kim };
3609836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,GetMaxOffsetPassTest)361*82ab8320SBrandon Kim TEST_F(BufferWraparoundReadTest, GetMaxOffsetPassTest)
3623def3c8eSBrandon Kim {
3633def3c8eSBrandon Kim     EXPECT_EQ(bufferImpl->getMaxOffset(), testMaxOffset);
3643def3c8eSBrandon Kim }
3653def3c8eSBrandon Kim 
TEST_F(BufferWraparoundReadTest,GetQueueOffsetPassTest)366*82ab8320SBrandon Kim TEST_F(BufferWraparoundReadTest, GetQueueOffsetPassTest)
367*82ab8320SBrandon Kim {
368*82ab8320SBrandon Kim     EXPECT_EQ(bufferImpl->getQueueOffset(),
369*82ab8320SBrandon Kim               bufferHeaderSize + testUeRegionSize);
370*82ab8320SBrandon Kim }
371*82ab8320SBrandon Kim 
TEST_F(BufferWraparoundReadTest,ParamsTooBigFail)37235d4335eSBrandon Kim TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
3739836cfa6SBrandon Kim {
3749836cfa6SBrandon Kim     InSequence s;
3753def3c8eSBrandon Kim     size_t tooBigOffset = testMaxOffset + 1;
3769836cfa6SBrandon Kim     EXPECT_THROW(
3779836cfa6SBrandon Kim         try {
37835d4335eSBrandon Kim             bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
3799836cfa6SBrandon Kim         } catch (const std::runtime_error& e) {
38026660e9bSBrandon Kim             EXPECT_STREQ(
38126660e9bSBrandon Kim                 e.what(),
3823def3c8eSBrandon Kim                 "[wraparoundRead] relativeOffset '385' was bigger than maxOffset '384'");
38335d4335eSBrandon Kim             throw;
38435d4335eSBrandon Kim         },
38535d4335eSBrandon Kim         std::runtime_error);
38635d4335eSBrandon Kim 
3873def3c8eSBrandon Kim     size_t tooBigLength = testMaxOffset + 1;
38835d4335eSBrandon Kim     EXPECT_THROW(
38935d4335eSBrandon Kim         try {
39035d4335eSBrandon Kim             bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
39135d4335eSBrandon Kim         } catch (const std::runtime_error& e) {
3923def3c8eSBrandon Kim             EXPECT_STREQ(e.what(), "[wraparoundRead] length '385' was bigger "
3933def3c8eSBrandon Kim                                    "than maxOffset '384'");
3949836cfa6SBrandon Kim             throw;
3959836cfa6SBrandon Kim         },
3969836cfa6SBrandon Kim         std::runtime_error);
3979836cfa6SBrandon Kim }
3989836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,NoWrapAroundReadFails)39935d4335eSBrandon Kim TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
4009836cfa6SBrandon Kim {
4019836cfa6SBrandon Kim     InSequence s;
40235d4335eSBrandon Kim     size_t testLength = 0x10;
40335d4335eSBrandon Kim     size_t testOffset = 0x20;
40435d4335eSBrandon Kim 
40535d4335eSBrandon Kim     // Fail the first read
40635d4335eSBrandon Kim     std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
40735d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
40835d4335eSBrandon Kim                 read(testOffset + expectedqueueOffset, testLength))
40935d4335eSBrandon Kim         .WillOnce(Return(shortTestBytesRead));
41035d4335eSBrandon Kim 
4119836cfa6SBrandon Kim     EXPECT_THROW(
4129836cfa6SBrandon Kim         try {
41335d4335eSBrandon Kim             bufferImpl->wraparoundRead(testOffset, testLength);
4149836cfa6SBrandon Kim         } catch (const std::runtime_error& e) {
41535d4335eSBrandon Kim             EXPECT_STREQ(e.what(),
41635d4335eSBrandon Kim                          "[wraparoundRead] Read '15' which was not the "
41735d4335eSBrandon Kim                          "requested length of '16'");
4189836cfa6SBrandon Kim             throw;
4199836cfa6SBrandon Kim         },
4209836cfa6SBrandon Kim         std::runtime_error);
4219836cfa6SBrandon Kim }
4229836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,NoWrapAroundReadPass)4239836cfa6SBrandon Kim TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
4249836cfa6SBrandon Kim {
4259836cfa6SBrandon Kim     InSequence s;
4269836cfa6SBrandon Kim     size_t testLength = 0x10;
42735d4335eSBrandon Kim     size_t testOffset = 0x20;
4289836cfa6SBrandon Kim 
4299836cfa6SBrandon Kim     // Successfully read all the requested length without a wrap around
4309836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesRead(testLength);
43135d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
43235d4335eSBrandon Kim                 read(testOffset + expectedqueueOffset, testLength))
4339836cfa6SBrandon Kim         .WillOnce(Return(testBytesRead));
4349836cfa6SBrandon Kim 
4359836cfa6SBrandon Kim     // Call to updateReadPtr is triggered
4369836cfa6SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{
437271d2313SBrandon Kim         static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
4389836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
4399836cfa6SBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
4409836cfa6SBrandon Kim         .WillOnce(Return(expectedWriteSize));
4419836cfa6SBrandon Kim 
4429836cfa6SBrandon Kim     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
4439836cfa6SBrandon Kim                 ElementsAreArray(testBytesRead));
44435d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
44535d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
44635d4335eSBrandon Kim     // The bmcReadPtr should have been updated
44735d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
44835d4335eSBrandon Kim               testOffset + testLength);
4499836cfa6SBrandon Kim }
4509836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,WrapAroundReadFails)4519836cfa6SBrandon Kim TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
4529836cfa6SBrandon Kim {
4539836cfa6SBrandon Kim     InSequence s;
4549836cfa6SBrandon Kim     size_t testBytesLeft = 3;
4559836cfa6SBrandon Kim     size_t testLength = 0x10;
4563def3c8eSBrandon Kim     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
4579836cfa6SBrandon Kim 
45835d4335eSBrandon Kim     // Read until the end of the queue
4599836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
46035d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
46135d4335eSBrandon Kim                                             testLength - testBytesLeft))
4629836cfa6SBrandon Kim         .WillOnce(Return(testBytesReadShort));
4639836cfa6SBrandon Kim 
4649836cfa6SBrandon Kim     // Read 1 byte short after wraparound
4659836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
4669836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
4679836cfa6SBrandon Kim         .WillOnce(Return(testBytesLeftReadShort));
4689836cfa6SBrandon Kim 
4699836cfa6SBrandon Kim     EXPECT_THROW(
4709836cfa6SBrandon Kim         try {
4719836cfa6SBrandon Kim             bufferImpl->wraparoundRead(testOffset, testLength);
4729836cfa6SBrandon Kim         } catch (const std::runtime_error& e) {
47335d4335eSBrandon Kim             EXPECT_STREQ(
47435d4335eSBrandon Kim                 e.what(),
47535d4335eSBrandon Kim                 "[wraparoundRead] Buffer wrapped around but read '2' which was "
47635d4335eSBrandon Kim                 "not the requested lenght of '3'");
4779836cfa6SBrandon Kim             throw;
4789836cfa6SBrandon Kim         },
4799836cfa6SBrandon Kim         std::runtime_error);
4809836cfa6SBrandon Kim }
4819836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,WrapAroundReadPasses)4829836cfa6SBrandon Kim TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
4839836cfa6SBrandon Kim {
4849836cfa6SBrandon Kim     InSequence s;
4859836cfa6SBrandon Kim     size_t testBytesLeft = 3;
4869836cfa6SBrandon Kim     size_t testLength = 0x10;
4873def3c8eSBrandon Kim     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
4889836cfa6SBrandon Kim 
48935d4335eSBrandon Kim     // Read to the end of the queue
4909836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
4919836cfa6SBrandon Kim                                                  9,  8,  7,  6,  5,  4};
49235d4335eSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
49335d4335eSBrandon Kim                                             testLength - testBytesLeft))
4949836cfa6SBrandon Kim         .WillOnce(Return(testBytesReadFirst));
4959836cfa6SBrandon Kim 
4969836cfa6SBrandon Kim     std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
4979836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
4989836cfa6SBrandon Kim         .WillOnce(Return(testBytesReadSecond));
4999836cfa6SBrandon Kim 
5009836cfa6SBrandon Kim     // Call to updateReadPtr is triggered
5019836cfa6SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{
502271d2313SBrandon Kim         static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
5039836cfa6SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
5049836cfa6SBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
5059836cfa6SBrandon Kim         .WillOnce(Return(expectedWriteSize));
5069836cfa6SBrandon Kim 
5079836cfa6SBrandon Kim     std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
5089836cfa6SBrandon Kim                                                8,  7,  6,  5,  4,  3,  2,  1};
5099836cfa6SBrandon Kim     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
5109836cfa6SBrandon Kim                 ElementsAreArray(expectedBytes));
51135d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
51235d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
51335d4335eSBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
51435d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
51535d4335eSBrandon Kim               testBytesLeft);
5169836cfa6SBrandon Kim }
5179836cfa6SBrandon Kim 
TEST_F(BufferWraparoundReadTest,WrapAroundCornerCasePass)5184662b1bdSBrandon Kim TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
5194662b1bdSBrandon Kim {
5204662b1bdSBrandon Kim     InSequence s;
5214662b1bdSBrandon Kim     size_t testBytesLeft = 0;
5224662b1bdSBrandon Kim     size_t testLength = 4;
5233def3c8eSBrandon Kim     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
5244662b1bdSBrandon Kim 
5254662b1bdSBrandon Kim     // Read to the very end of the queue
5264662b1bdSBrandon Kim     std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
5274662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr,
5284662b1bdSBrandon Kim                 read(testOffset + expectedqueueOffset, testLength))
5294662b1bdSBrandon Kim         .WillOnce(Return(testBytes));
5304662b1bdSBrandon Kim 
5314662b1bdSBrandon Kim     // Call to updateReadPtr is triggered, since we read to the very end of the
5324662b1bdSBrandon Kim     // buffer, update the readPtr up around to 0
533271d2313SBrandon Kim     const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
5344662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
5354662b1bdSBrandon Kim                                              ElementsAreArray(expectedReadPtr)))
5364662b1bdSBrandon Kim         .WillOnce(Return(expectedWriteSize));
5374662b1bdSBrandon Kim 
5384662b1bdSBrandon Kim     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
5394662b1bdSBrandon Kim                 ElementsAreArray(testBytes));
5404662b1bdSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
5414662b1bdSBrandon Kim         bufferImpl->getCachedBufferHeader();
5424662b1bdSBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
5434662b1bdSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
5444662b1bdSBrandon Kim               0);
5454662b1bdSBrandon Kim }
5464662b1bdSBrandon Kim 
54740ce08e1SBrandon Kim class BufferEntryTest : public BufferWraparoundReadTest
5487bac2d69SBrandon Kim {
5497bac2d69SBrandon Kim   protected:
BufferEntryTest()55040ce08e1SBrandon Kim     BufferEntryTest()
5517bac2d69SBrandon Kim     {
5527bac2d69SBrandon Kim         testEntryHeader.sequenceId = testSequenceId;
5537bac2d69SBrandon Kim         testEntryHeader.entrySize = testEntrySize;
5547bac2d69SBrandon Kim         testEntryHeader.checksum = testChecksum;
5557bac2d69SBrandon Kim         testEntryHeader.rdeCommandType = testRdeCommandType;
5567bac2d69SBrandon Kim     }
55740ce08e1SBrandon Kim     ~BufferEntryTest() override = default;
5587bac2d69SBrandon Kim 
wraparoundReadMock(const uint32_t relativeOffset,std::span<std::uint8_t> expetedBytesOutput)55935d4335eSBrandon Kim     void wraparoundReadMock(const uint32_t relativeOffset,
56035d4335eSBrandon Kim                             std::span<std::uint8_t> expetedBytesOutput)
56135d4335eSBrandon Kim     {
56235d4335eSBrandon Kim         InSequence s;
5633def3c8eSBrandon Kim         const uint32_t queueSizeToQueueEnd = testMaxOffset - relativeOffset;
56435d4335eSBrandon Kim 
56535d4335eSBrandon Kim         // This will wrap, split the read mocks in 2
56635d4335eSBrandon Kim         if (expetedBytesOutput.size() > queueSizeToQueueEnd)
56735d4335eSBrandon Kim         {
56835d4335eSBrandon Kim             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
56935d4335eSBrandon Kim                 .WillOnce(Return(std::vector<std::uint8_t>(
57035d4335eSBrandon Kim                     expetedBytesOutput.begin(),
57135d4335eSBrandon Kim                     expetedBytesOutput.begin() + queueSizeToQueueEnd)));
57235d4335eSBrandon Kim             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
57335d4335eSBrandon Kim                 .WillOnce(Return(std::vector<std::uint8_t>(
57435d4335eSBrandon Kim                     expetedBytesOutput.begin() + queueSizeToQueueEnd,
57535d4335eSBrandon Kim                     expetedBytesOutput.end())));
57635d4335eSBrandon Kim         }
57735d4335eSBrandon Kim         else
5787bac2d69SBrandon Kim         {
5797bac2d69SBrandon Kim             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
5807bac2d69SBrandon Kim                 .WillOnce(Return(std::vector<std::uint8_t>(
5817bac2d69SBrandon Kim                     expetedBytesOutput.begin(), expetedBytesOutput.end())));
58235d4335eSBrandon Kim         }
5837bac2d69SBrandon Kim 
5847bac2d69SBrandon Kim         EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
5857bac2d69SBrandon Kim             .WillOnce(Return(expectedWriteSize));
5867bac2d69SBrandon Kim     }
5877bac2d69SBrandon Kim 
5887bac2d69SBrandon Kim     static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
5897bac2d69SBrandon Kim     static constexpr uint16_t testSequenceId = 0;
5907bac2d69SBrandon Kim     static constexpr uint16_t testEntrySize = 0x20;
5917bac2d69SBrandon Kim     static constexpr uint8_t testRdeCommandType = 0x01;
592f0e4adc9SBrandon Kim     // Calculated checksum for the header
593f0e4adc9SBrandon Kim     static constexpr uint8_t testChecksum =
594f0e4adc9SBrandon Kim         (testSequenceId ^ testEntrySize ^ testRdeCommandType);
595613ba537SBrandon Kim     size_t testOffset = 0x0;
5967bac2d69SBrandon Kim 
5977bac2d69SBrandon Kim     struct QueueEntryHeader testEntryHeader
5987bac2d69SBrandon Kim     {};
5997bac2d69SBrandon Kim };
6007bac2d69SBrandon Kim 
TEST_F(BufferEntryTest,ReadEntryHeaderPass)60140ce08e1SBrandon Kim TEST_F(BufferEntryTest, ReadEntryHeaderPass)
6027bac2d69SBrandon Kim {
6037bac2d69SBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
6047bac2d69SBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
6057bac2d69SBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
60635d4335eSBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
607613ba537SBrandon Kim     EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
60835d4335eSBrandon Kim     // Check the bmcReadPtr
60935d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
61035d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
61135d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
61235d4335eSBrandon Kim               testOffset + testEntryHeaderVector.size());
6137bac2d69SBrandon Kim }
6147bac2d69SBrandon Kim 
TEST_F(BufferEntryTest,ReadEntryChecksumFail)61540ce08e1SBrandon Kim TEST_F(BufferEntryTest, ReadEntryChecksumFail)
61640ce08e1SBrandon Kim {
61740ce08e1SBrandon Kim     InSequence s;
618f0e4adc9SBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
61940ce08e1SBrandon Kim     // Offset the checksum by 1
620f0e4adc9SBrandon Kim     testEntryHeader.checksum += 1;
62140ce08e1SBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
62240ce08e1SBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
62340ce08e1SBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
62435d4335eSBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
62535d4335eSBrandon Kim     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
62640ce08e1SBrandon Kim     EXPECT_THROW(
627613ba537SBrandon Kim         try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
628f0e4adc9SBrandon Kim             // Calculation: testChecksum (0x21) XOR (0x22) = 3
62940ce08e1SBrandon Kim             EXPECT_STREQ(e.what(),
630f0e4adc9SBrandon Kim                          "[readEntry] Checksum was '3', expected '0'");
63140ce08e1SBrandon Kim             throw;
63240ce08e1SBrandon Kim         },
63340ce08e1SBrandon Kim         std::runtime_error);
63440ce08e1SBrandon Kim }
63540ce08e1SBrandon Kim 
TEST_F(BufferEntryTest,ReadEntryPassWraparound)636613ba537SBrandon Kim TEST_F(BufferEntryTest, ReadEntryPassWraparound)
63740ce08e1SBrandon Kim {
63840ce08e1SBrandon Kim     InSequence s;
639f0e4adc9SBrandon Kim     // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
640f0e4adc9SBrandon Kim     // (20 times) = 0 therefore leave the checksum as is
64135d4335eSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
64240ce08e1SBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
64340ce08e1SBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
64440ce08e1SBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
645613ba537SBrandon Kim     // Set testOffset so that we can test the wraparound here at the header and
646613ba537SBrandon Kim     // update the readPtr
6473def3c8eSBrandon Kim     testOffset = testMaxOffset - 1;
648613ba537SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
649613ba537SBrandon Kim         .WillOnce(Return(expectedWriteSize));
650613ba537SBrandon Kim     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
651613ba537SBrandon Kim 
65235d4335eSBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
65335d4335eSBrandon Kim     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
65440ce08e1SBrandon Kim 
65540ce08e1SBrandon Kim     EntryPair testedEntryPair;
656613ba537SBrandon Kim     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
65740ce08e1SBrandon Kim     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
65840ce08e1SBrandon Kim     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
65935d4335eSBrandon Kim     struct CircularBufferHeader cachedBufferHeader =
66035d4335eSBrandon Kim         bufferImpl->getCachedBufferHeader();
66135d4335eSBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
66235d4335eSBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
663613ba537SBrandon Kim               entryHeaderSize + testEntrySize - 1);
664613ba537SBrandon Kim 
665613ba537SBrandon Kim     // Set testOffset so that we can test the wraparound here as well on our
666613ba537SBrandon Kim     // second read for the entry (by 1 byte)
6673def3c8eSBrandon Kim     testOffset = testMaxOffset - entryHeaderSize - 1;
668613ba537SBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
669613ba537SBrandon Kim         .WillOnce(Return(expectedWriteSize));
670613ba537SBrandon Kim     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
671613ba537SBrandon Kim 
672613ba537SBrandon Kim     wraparoundReadMock(testOffset, testEntryHeaderVector);
673613ba537SBrandon Kim     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
674613ba537SBrandon Kim 
675613ba537SBrandon Kim     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
676613ba537SBrandon Kim     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
677613ba537SBrandon Kim     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
678613ba537SBrandon Kim     cachedBufferHeader = bufferImpl->getCachedBufferHeader();
679613ba537SBrandon Kim     // The bmcReadPtr should have been updated to reflect the wraparound
680613ba537SBrandon Kim     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
68135d4335eSBrandon Kim               testEntrySize - 1);
68240ce08e1SBrandon Kim }
68340ce08e1SBrandon Kim 
6844662b1bdSBrandon Kim class BufferReadErrorLogsTest : public BufferEntryTest
6854662b1bdSBrandon Kim {
6864662b1bdSBrandon Kim   protected:
6874662b1bdSBrandon Kim     BufferReadErrorLogsTest() = default;
6884662b1bdSBrandon Kim 
6894662b1bdSBrandon Kim     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
6904662b1bdSBrandon Kim     size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
6914662b1bdSBrandon Kim };
6924662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,PtrsTooBigFail)6934662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
6944662b1bdSBrandon Kim {
6954662b1bdSBrandon Kim     InSequence s;
6964662b1bdSBrandon Kim     // Set the biosWritePtr too big
6974662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr =
6983def3c8eSBrandon Kim         boost::endian::native_to_little((testMaxOffset + 1));
6994662b1bdSBrandon Kim     initializeFuncMock();
7004662b1bdSBrandon Kim 
7014662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7024662b1bdSBrandon Kim         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
7034662b1bdSBrandon Kim                                               testInitializationHeaderPtr +
7044662b1bdSBrandon Kim                                                   bufferHeaderSize)));
7054662b1bdSBrandon Kim     EXPECT_THROW(
7064662b1bdSBrandon Kim         try {
7074662b1bdSBrandon Kim             bufferImpl->readErrorLogs();
7084662b1bdSBrandon Kim         } catch (const std::runtime_error& e) {
7094662b1bdSBrandon Kim             EXPECT_STREQ(e.what(),
7103def3c8eSBrandon Kim                          "[readErrorLogs] currentBiosWritePtr was '385' "
7113def3c8eSBrandon Kim                          "which was bigger than maxOffset '384'");
7124662b1bdSBrandon Kim             throw;
7134662b1bdSBrandon Kim         },
7144662b1bdSBrandon Kim         std::runtime_error);
7154662b1bdSBrandon Kim 
7164662b1bdSBrandon Kim     // Reset the biosWritePtr and set the bmcReadPtr too big
7174662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr = 0;
7184662b1bdSBrandon Kim     initializeFuncMock();
7194662b1bdSBrandon Kim     testInitializationHeader.bmcReadPtr =
7203def3c8eSBrandon Kim         boost::endian::native_to_little((testMaxOffset + 1));
7214662b1bdSBrandon Kim     initializeFuncMock();
7224662b1bdSBrandon Kim 
7234662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7244662b1bdSBrandon Kim         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
7254662b1bdSBrandon Kim                                               testInitializationHeaderPtr +
7264662b1bdSBrandon Kim                                                   bufferHeaderSize)));
7274662b1bdSBrandon Kim     EXPECT_THROW(
7284662b1bdSBrandon Kim         try {
7294662b1bdSBrandon Kim             bufferImpl->readErrorLogs();
7304662b1bdSBrandon Kim         } catch (const std::runtime_error& e) {
7313def3c8eSBrandon Kim             EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '385' "
7323def3c8eSBrandon Kim                                    "which was bigger than maxOffset '384'");
7334662b1bdSBrandon Kim             throw;
7344662b1bdSBrandon Kim         },
7354662b1bdSBrandon Kim         std::runtime_error);
7364662b1bdSBrandon Kim }
7374662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,IdenticalPtrsPass)7384662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
7394662b1bdSBrandon Kim {
7404662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7414662b1bdSBrandon Kim         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
7424662b1bdSBrandon Kim                                               testInitializationHeaderPtr +
7434662b1bdSBrandon Kim                                                   bufferHeaderSize)));
7444662b1bdSBrandon Kim     EXPECT_NO_THROW(bufferImpl->readErrorLogs());
7454662b1bdSBrandon Kim }
7464662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,NoWraparoundPass)7474662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
7484662b1bdSBrandon Kim {
7494662b1bdSBrandon Kim     InSequence s;
7504662b1bdSBrandon Kim     // Set the biosWritePtr to 1 entryHeader + entry size
7514662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr =
7524662b1bdSBrandon Kim         boost::endian::native_to_little((entryAndHeaderSize));
7534662b1bdSBrandon Kim     initializeFuncMock();
7544662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7554662b1bdSBrandon Kim         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
7564662b1bdSBrandon Kim                                               testInitializationHeaderPtr +
7574662b1bdSBrandon Kim                                                   bufferHeaderSize)));
7584662b1bdSBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
7594662b1bdSBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
7604662b1bdSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
7614662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
7624662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
7634662b1bdSBrandon Kim 
7644662b1bdSBrandon Kim     std::vector<EntryPair> entryPairs;
7654662b1bdSBrandon Kim     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
7664662b1bdSBrandon Kim 
7674662b1bdSBrandon Kim     // Check that we only read one entryPair and that the content is correct
7681a3dc60dSBrandon Kim     EXPECT_EQ(entryPairs.size(), 1U);
7694662b1bdSBrandon Kim     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
7704662b1bdSBrandon Kim     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
7714662b1bdSBrandon Kim }
7724662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,WraparoundMultiplEntryPass)7734662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
7744662b1bdSBrandon Kim {
7754662b1bdSBrandon Kim     InSequence s;
7764662b1bdSBrandon Kim     // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
7773def3c8eSBrandon Kim     uint32_t entryAndHeaderSizeAwayFromEnd = testMaxOffset - entryAndHeaderSize;
7784662b1bdSBrandon Kim     testInitializationHeader.bmcReadPtr =
7794662b1bdSBrandon Kim         boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
7804662b1bdSBrandon Kim     // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
7814662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr = entryAndHeaderSize;
7824662b1bdSBrandon Kim     initializeFuncMock();
7834662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
7844662b1bdSBrandon Kim         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
7854662b1bdSBrandon Kim                                               testInitializationHeaderPtr +
7864662b1bdSBrandon Kim                                                   bufferHeaderSize)));
7874662b1bdSBrandon Kim 
7884662b1bdSBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
7894662b1bdSBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
7904662b1bdSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
7914662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
7924662b1bdSBrandon Kim                        testEntryHeaderVector);
7934662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
7944662b1bdSBrandon Kim                            entryHeaderSize,
7954662b1bdSBrandon Kim                        testEntryVector);
7964662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
7974662b1bdSBrandon Kim                        testEntryHeaderVector);
7984662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
7994662b1bdSBrandon Kim                            entryHeaderSize,
8004662b1bdSBrandon Kim                        testEntryVector);
8014662b1bdSBrandon Kim 
8024662b1bdSBrandon Kim     std::vector<EntryPair> entryPairs;
8034662b1bdSBrandon Kim     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
8044662b1bdSBrandon Kim 
8054662b1bdSBrandon Kim     // Check that we only read one entryPair and that the content is correct
8064662b1bdSBrandon Kim     EXPECT_EQ(entryPairs.size(), 2);
8074662b1bdSBrandon Kim     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
8084662b1bdSBrandon Kim     EXPECT_EQ(entryPairs[1].first, testEntryHeader);
8094662b1bdSBrandon Kim     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
8104662b1bdSBrandon Kim     EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
8114662b1bdSBrandon Kim }
8124662b1bdSBrandon Kim 
TEST_F(BufferReadErrorLogsTest,WraparoundMismatchingPtrsFail)8134662b1bdSBrandon Kim TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
8144662b1bdSBrandon Kim {
8154662b1bdSBrandon Kim     InSequence s;
8164662b1bdSBrandon Kim     testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
8174662b1bdSBrandon Kim     // Make the biosWritePtr intentially 1 smaller than expected
8184662b1bdSBrandon Kim     testInitializationHeader.biosWritePtr =
8194662b1bdSBrandon Kim         boost::endian::native_to_little(entryAndHeaderSize - 1);
8204662b1bdSBrandon Kim     initializeFuncMock();
8214662b1bdSBrandon Kim     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
8224662b1bdSBrandon Kim         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
8234662b1bdSBrandon Kim                                               testInitializationHeaderPtr +
8244662b1bdSBrandon Kim                                                   bufferHeaderSize)));
8254662b1bdSBrandon Kim 
8264662b1bdSBrandon Kim     std::vector<uint8_t> testEntryHeaderVector(
8274662b1bdSBrandon Kim         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
8284662b1bdSBrandon Kim     std::vector<uint8_t> testEntryVector(testEntrySize);
8294662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
8304662b1bdSBrandon Kim     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
8314662b1bdSBrandon Kim 
8324662b1bdSBrandon Kim     EXPECT_THROW(
8334662b1bdSBrandon Kim         try {
8344662b1bdSBrandon Kim             bufferImpl->readErrorLogs();
8354662b1bdSBrandon Kim         } catch (const std::runtime_error& e) {
8364662b1bdSBrandon Kim             EXPECT_STREQ(
8374662b1bdSBrandon Kim                 e.what(),
8384662b1bdSBrandon Kim                 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
8394662b1bdSBrandon Kim                 "are not identical after reading through all the logs");
8404662b1bdSBrandon Kim             throw;
8414662b1bdSBrandon Kim         },
8424662b1bdSBrandon Kim         std::runtime_error);
8434662b1bdSBrandon Kim }
8444662b1bdSBrandon Kim 
845fcbc3db1SBrandon Kim } // namespace
846fcbc3db1SBrandon Kim } // namespace bios_bmc_smm_error_logger
847