xref: /openbmc/bios-bmc-smm-error-logger/test/buffer_test.cpp (revision d49db6ffa0013a333a33be2b48f0a422d4054c03)
1 #include "buffer.hpp"
2 #include "data_interface_mock.hpp"
3 
4 #include <boost/endian/arithmetic.hpp>
5 #include <boost/endian/conversion.hpp>
6 
7 #include <algorithm>
8 #include <array>
9 #include <cstdint>
10 #include <memory>
11 
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14 
15 namespace bios_bmc_smm_error_logger
16 {
17 namespace
18 {
19 
20 using ::testing::_;
21 using ::testing::ElementsAreArray;
22 using ::testing::InSequence;
23 using ::testing::Return;
24 
25 class BufferTest : public ::testing::Test
26 {
27   protected:
BufferTest()28     BufferTest() :
29         dataInterfaceMock(std::make_unique<DataInterfaceMock>()),
30         dataInterfaceMockPtr(dataInterfaceMock.get())
31     {
32         bufferImpl = std::make_unique<BufferImpl>(std::move(dataInterfaceMock));
33         testInitializationHeader.bmcInterfaceVersion = testBmcInterfaceVersion;
34         testInitializationHeader.queueSize = testQueueSize;
35         testInitializationHeader.ueRegionSize = testUeRegionSize;
36         std::transform(testMagicNumber.begin(), testMagicNumber.end(),
37                        testInitializationHeader.magicNumber.begin(),
38                        [](uint32_t number) -> little_uint32_t {
39                            return boost::endian::native_to_little(number);
40                        });
41     }
42     ~BufferTest() override = default;
43 
44     // CircularBufferHeader size is 0x30, ensure the test region is bigger
45     static constexpr size_t testRegionSize = 0x200;
46     static constexpr uint32_t testBmcInterfaceVersion = 123;
47     static constexpr uint32_t testQueueSize = 0x200;
48     static constexpr uint16_t testUeRegionSize = 0x50;
49     static constexpr std::array<uint32_t, 4> testMagicNumber = {
50         0x12345678, 0x22345678, 0x32345678, 0x42345678};
51     static constexpr size_t bufferHeaderSize =
52         sizeof(struct CircularBufferHeader);
53 
54     struct CircularBufferHeader testInitializationHeader{};
55 
56     std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
57     DataInterfaceMock* dataInterfaceMockPtr;
58     std::unique_ptr<BufferImpl> bufferImpl;
59 };
60 
TEST_F(BufferTest,BufferInitializeEraseFail)61 TEST_F(BufferTest, BufferInitializeEraseFail)
62 {
63     InSequence s;
64     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
65         .WillOnce(Return(testRegionSize));
66     EXPECT_THROW(
67         try {
68             // Test too big of a proposed buffer compared to the memori size
69             uint16_t bigQueueSize = 0x201;
70             uint16_t bigUeRegionSize = 0x50;
71             bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
72                                    bigUeRegionSize, testMagicNumber);
73         } catch (const std::runtime_error& e) {
74             EXPECT_STREQ(
75                 e.what(),
76                 "[initialize] Proposed queue size '513' is bigger than the BMC's allocated MMIO region of '512'");
77             throw;
78         },
79         std::runtime_error);
80     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
81 
82     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
83         .WillOnce(Return(testRegionSize));
84     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
85     // Return a smaller write than the intended testRegionSize to test the error
86     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
87         .WillOnce(Return(testQueueSize - 1));
88     EXPECT_THROW(
89         try {
90             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
91                                    testUeRegionSize, testMagicNumber);
92         } catch (const std::runtime_error& e) {
93             EXPECT_STREQ(e.what(), "[initialize] Only erased '511'");
94             throw;
95         },
96         std::runtime_error);
97     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
98 
99     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
100         .WillOnce(Return(testRegionSize));
101     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
102         .WillOnce(Return(testQueueSize));
103     // Return a smaller write than the intended initializationHeader to test the
104     // error
105     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
106     EXPECT_THROW(
107         try {
108             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
109                                    testUeRegionSize, testMagicNumber);
110         } catch (const std::runtime_error& e) {
111             EXPECT_STREQ(e.what(),
112                          "[initialize] Only wrote '0' bytes of the header");
113             throw;
114         },
115         std::runtime_error);
116     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
117 }
118 
TEST_F(BufferTest,BufferInitializePass)119 TEST_F(BufferTest, BufferInitializePass)
120 {
121     InSequence s;
122     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
123         .WillOnce(Return(testRegionSize));
124     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
125     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
126         .WillOnce(Return(testQueueSize));
127 
128     uint8_t* testInitializationHeaderPtr =
129         reinterpret_cast<uint8_t*>(&testInitializationHeader);
130     EXPECT_CALL(*dataInterfaceMockPtr,
131                 write(0, ElementsAreArray(testInitializationHeaderPtr,
132                                           bufferHeaderSize)))
133         .WillOnce(Return(bufferHeaderSize));
134     EXPECT_NO_THROW(
135         bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
136                                testUeRegionSize, testMagicNumber));
137     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
138 }
139 
TEST_F(BufferTest,BufferHeaderReadFail)140 TEST_F(BufferTest, BufferHeaderReadFail)
141 {
142     std::vector<std::uint8_t> testBytesRead{};
143     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
144         .WillOnce(Return(testBytesRead));
145     EXPECT_THROW(
146         try {
147             bufferImpl->readBufferHeader();
148         } catch (const std::runtime_error& e) {
149             EXPECT_STREQ(e.what(),
150                          "Buffer header read only read '0', expected '48'");
151             throw;
152         },
153         std::runtime_error);
154 }
155 
TEST_F(BufferTest,BufferHeaderReadPass)156 TEST_F(BufferTest, BufferHeaderReadPass)
157 {
158     uint8_t* testInitializationHeaderPtr =
159         reinterpret_cast<uint8_t*>(&testInitializationHeader);
160     std::vector<uint8_t> testInitializationHeaderVector(
161         testInitializationHeaderPtr,
162         testInitializationHeaderPtr + bufferHeaderSize);
163 
164     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
165         .WillOnce(Return(testInitializationHeaderVector));
166     EXPECT_NO_THROW(bufferImpl->readBufferHeader());
167     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
168 }
169 
TEST_F(BufferTest,BufferUpdateReadPtrFail)170 TEST_F(BufferTest, BufferUpdateReadPtrFail)
171 {
172     // Return write size that is not 2 which is sizeof(little_uint16_t)
173     constexpr size_t wrongWriteSize = 1;
174     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
175         .WillOnce(Return(wrongWriteSize));
176     EXPECT_THROW(
177         try {
178             bufferImpl->updateReadPtr(0);
179         } catch (const std::runtime_error& e) {
180             EXPECT_STREQ(
181                 e.what(),
182                 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
183             throw;
184         },
185         std::runtime_error);
186 }
187 
TEST_F(BufferTest,BufferUpdateReadPtrPass)188 TEST_F(BufferTest, BufferUpdateReadPtrPass)
189 {
190     constexpr size_t expectedWriteSize = 3;
191     constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
192     // Check that we truncate the highest 24bits
193     const uint32_t testNewReadPtr = 0x99881234;
194     const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
195 
196     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
197                                              ElementsAreArray(expectedReadPtr)))
198         .WillOnce(Return(expectedWriteSize));
199     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
200 
201     auto cachedHeader = bufferImpl->getCachedBufferHeader();
202     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
203               0x881234);
204 }
205 
TEST_F(BufferTest,BufferUpdateBmcFlagsFail)206 TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
207 {
208     // Return write size that is not 4 which is sizeof(little_uint32_t)
209     constexpr size_t wrongWriteSize = 1;
210     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
211         .WillOnce(Return(wrongWriteSize));
212     EXPECT_THROW(
213         try {
214             bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
215         } catch (const std::runtime_error& e) {
216             EXPECT_STREQ(
217                 e.what(),
218                 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
219             throw;
220         },
221         std::runtime_error);
222 }
223 
TEST_F(BufferTest,BufferUpdateBmcFlagsPass)224 TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
225 {
226     constexpr size_t expectedWriteSize = 4;
227     constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
228     const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
229 
230     EXPECT_CALL(*dataInterfaceMockPtr,
231                 write(expectedBmcReadPtrOffset,
232                       ElementsAreArray(expectedNewBmcFlagsVector)))
233         .WillOnce(Return(expectedWriteSize));
234     EXPECT_NO_THROW(
235         bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
236 
237     auto cachedHeader = bufferImpl->getCachedBufferHeader();
238     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
239               static_cast<uint32_t>(BmcFlags::ready));
240 }
241 
TEST_F(BufferTest,GetMaxOffsetQueueSizeFail)242 TEST_F(BufferTest, GetMaxOffsetQueueSizeFail)
243 {
244     InSequence s;
245     static constexpr size_t wrongQueueSize = testQueueSize - 1;
246     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
247         .WillOnce(Return(testRegionSize));
248     const std::vector<uint8_t> emptyArray(wrongQueueSize, 0);
249     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
250         .WillOnce(Return(wrongQueueSize));
251 
252     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
253         .WillOnce(Return(bufferHeaderSize));
254     EXPECT_NO_THROW(
255         bufferImpl->initialize(testBmcInterfaceVersion, wrongQueueSize,
256                                testUeRegionSize, testMagicNumber));
257     EXPECT_THROW(
258         try {
259             bufferImpl->getMaxOffset();
260         } catch (const std::runtime_error& e) {
261             EXPECT_STREQ(e.what(),
262                          "[getMaxOffset] runtime queueSize '511' did not match "
263                          "compile-time queueSize '512'. This indicates that the"
264                          " buffer was corrupted");
265             throw;
266         },
267         std::runtime_error);
268 }
269 
TEST_F(BufferTest,GetMaxOffsetUeRegionSizeFail)270 TEST_F(BufferTest, GetMaxOffsetUeRegionSizeFail)
271 {
272     InSequence s;
273     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
274         .WillOnce(Return(testRegionSize));
275     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
276     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
277         .WillOnce(Return(testQueueSize));
278 
279     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
280         .WillOnce(Return(bufferHeaderSize));
281     EXPECT_NO_THROW(
282         bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
283                                testUeRegionSize + 1, testMagicNumber));
284     EXPECT_THROW(
285         try {
286             bufferImpl->getMaxOffset();
287         } catch (const std::runtime_error& e) {
288             EXPECT_STREQ(
289                 e.what(),
290                 "[getMaxOffset] runtime ueRegionSize '81' did not match "
291                 "compile-time ueRegionSize '80'. This indicates that the"
292                 " buffer was corrupted");
293             throw;
294         },
295         std::runtime_error);
296 }
297 
TEST_F(BufferTest,GetOffsetUeRegionSizeFail)298 TEST_F(BufferTest, GetOffsetUeRegionSizeFail)
299 {
300     InSequence s;
301     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
302         .WillOnce(Return(testRegionSize));
303     const std::vector<uint8_t> emptyArray(testQueueSize, 0);
304     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
305         .WillOnce(Return(testQueueSize));
306 
307     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
308         .WillOnce(Return(bufferHeaderSize));
309     EXPECT_NO_THROW(
310         bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
311                                testUeRegionSize - 1, testMagicNumber));
312     EXPECT_THROW(
313         try {
314             bufferImpl->getQueueOffset();
315         } catch (const std::runtime_error& e) {
316             EXPECT_STREQ(
317                 e.what(),
318                 "[getQueueOffset] runtime ueRegionSize '79' did not match "
319                 "compile-time ueRegionSize '80'. This indicates that the"
320                 " buffer was corrupted");
321             throw;
322         },
323         std::runtime_error);
324 }
325 
TEST_F(BufferTest,ReadUeLog_NoUeRegionConfigured)326 TEST_F(BufferTest, ReadUeLog_NoUeRegionConfigured)
327 {
328     struct CircularBufferHeader header = testInitializationHeader;
329     header.ueRegionSize =
330         boost::endian::native_to_little<uint16_t>(0); // No UE region
331 
332     uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
333     std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
334     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
335         .WillOnce(Return(headerBytes));
336 
337     auto result = bufferImpl->readUeLogFromReservedRegion();
338     EXPECT_TRUE(result.empty());
339 }
340 
TEST_F(BufferTest,ReadUeLog_NotPresentDueToFlags)341 TEST_F(BufferTest, ReadUeLog_NotPresentDueToFlags)
342 {
343     struct CircularBufferHeader header = testInitializationHeader;
344     header.ueRegionSize = boost::endian::native_to_little<uint16_t>(0x20);
345     // Flags are the same, so no new UE log
346     header.biosFlags = boost::endian::native_to_little<uint32_t>(
347         static_cast<uint32_t>((BufferFlags::ueSwitch)));
348     header.bmcFlags = boost::endian::native_to_little<uint32_t>(
349         static_cast<uint32_t>(BufferFlags::ueSwitch));
350 
351     uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
352     std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
353     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
354         .WillOnce(Return(headerBytes));
355 
356     auto result = bufferImpl->readUeLogFromReservedRegion();
357     EXPECT_TRUE(result.empty());
358 }
359 
TEST_F(BufferTest,ReadUeLog_PresentAndSuccessfullyRead)360 TEST_F(BufferTest, ReadUeLog_PresentAndSuccessfullyRead)
361 {
362     struct CircularBufferHeader header = testInitializationHeader;
363     uint16_t ueSize = 0x20;
364     header.ueRegionSize = boost::endian::native_to_little(ueSize);
365     header.biosFlags = boost::endian::native_to_little<uint32_t>(
366         static_cast<uint32_t>(BufferFlags::ueSwitch));
367     header.bmcFlags =
368         boost::endian::native_to_little<uint32_t>(0); // BIOS set, BMC not yet
369 
370     uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
371     std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
372     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
373         .WillOnce(Return(headerBytes));
374 
375     size_t ueRegionOffset = bufferHeaderSize;
376     std::vector<uint8_t> ueData(ueSize, 0xAA);
377     EXPECT_CALL(*dataInterfaceMockPtr, read(ueRegionOffset, ueSize))
378         .WillOnce(Return(ueData));
379 
380     auto result = bufferImpl->readUeLogFromReservedRegion();
381     ASSERT_FALSE(result.empty());
382     EXPECT_THAT(result, ElementsAreArray(ueData));
383 
384     // The initial bmcFlags (0) should remain unchanged in the cache
385     struct CircularBufferHeader cachedHeaderAfterRead =
386         bufferImpl->getCachedBufferHeader();
387     EXPECT_EQ(boost::endian::little_to_native(cachedHeaderAfterRead.bmcFlags),
388               0);
389 }
390 
TEST_F(BufferTest,ReadUeLog_PresentButReadFails)391 TEST_F(BufferTest, ReadUeLog_PresentButReadFails)
392 {
393     struct CircularBufferHeader header = testInitializationHeader;
394     uint16_t ueSize = 0x20;
395     header.ueRegionSize = boost::endian::native_to_little(ueSize);
396     header.biosFlags = boost::endian::native_to_little<uint32_t>(
397         static_cast<uint32_t>(BufferFlags::ueSwitch));
398     header.bmcFlags = boost::endian::native_to_little<uint32_t>(0);
399 
400     uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
401     std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
402     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
403         .WillOnce(Return(headerBytes));
404 
405     size_t ueRegionOffset = bufferHeaderSize;
406     std::vector<uint8_t> shortUeData(ueSize - 1, 0xAA); // Short read
407     EXPECT_CALL(*dataInterfaceMockPtr, read(ueRegionOffset, ueSize))
408         .WillOnce(Return(shortUeData));
409 
410     // Expect an exception due to short read, which is treated as corruption for
411     // UE log
412     EXPECT_THROW(
413         try {
414             bufferImpl->readUeLogFromReservedRegion();
415         } catch (const std::runtime_error& e) {
416             EXPECT_THAT(e.what(),
417                         ::testing::HasSubstr("Failed to read full UE log"));
418             throw;
419         },
420         std::runtime_error);
421 }
422 
TEST_F(BufferTest,CheckOverflow_NotPresentDueToFlags)423 TEST_F(BufferTest, CheckOverflow_NotPresentDueToFlags)
424 {
425     struct CircularBufferHeader header = testInitializationHeader;
426     // Flags are the same, so no new overflow
427     header.biosFlags = boost::endian::native_to_little<uint32_t>(
428         static_cast<uint32_t>(BufferFlags::overflow));
429     header.bmcFlags = boost::endian::native_to_little<uint32_t>(
430         static_cast<uint32_t>(BufferFlags::overflow));
431 
432     uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
433     std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
434     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
435         .WillOnce(Return(headerBytes));
436 
437     bool overflowDetected = bufferImpl->checkForOverflowAndAcknowledge();
438     ASSERT_FALSE(overflowDetected);
439 }
440 
TEST_F(BufferTest,CheckOverflow_PresentAndAcknowledged)441 TEST_F(BufferTest, CheckOverflow_PresentAndAcknowledged)
442 {
443     struct CircularBufferHeader header = testInitializationHeader;
444     header.biosFlags = boost::endian::native_to_little<uint32_t>(
445         static_cast<uint32_t>(BufferFlags::overflow));
446     header.bmcFlags =
447         boost::endian::native_to_little<uint32_t>(0); // BIOS set, BMC not yet
448 
449     uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
450     std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
451     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
452         .WillOnce(Return(headerBytes));
453 
454     uint32_t expectedNewBmcFlags =
455         static_cast<uint32_t>(BufferFlags::overflow); // BMC toggles its bit
456     little_uint32_t littleExpectedNewBmcFlags =
457         boost::endian::native_to_little(expectedNewBmcFlags);
458     uint8_t* flagPtr = reinterpret_cast<uint8_t*>(&littleExpectedNewBmcFlags);
459     std::vector<uint8_t> expectedFlagWrite(flagPtr,
460                                            flagPtr + sizeof(little_uint32_t));
461     constexpr uint8_t bmcFlagsOffset =
462         offsetof(struct CircularBufferHeader, bmcFlags);
463 
464     EXPECT_CALL(*dataInterfaceMockPtr,
465                 write(bmcFlagsOffset, ElementsAreArray(expectedFlagWrite)))
466         .WillOnce(Return(sizeof(little_uint32_t)));
467 
468     bool overflowDetected = bufferImpl->checkForOverflowAndAcknowledge();
469     ASSERT_TRUE(overflowDetected);
470 
471     struct CircularBufferHeader updatedCachedHeader =
472         bufferImpl->getCachedBufferHeader();
473     EXPECT_EQ(boost::endian::little_to_native(updatedCachedHeader.bmcFlags),
474               expectedNewBmcFlags);
475 }
476 
477 class BufferWraparoundReadTest : public BufferTest
478 {
479   protected:
BufferWraparoundReadTest()480     BufferWraparoundReadTest()
481     {
482         initializeFuncMock();
483     }
initializeFuncMock()484     void initializeFuncMock()
485     {
486         // Initialize the memory and the cachedBufferHeader
487         InSequence s;
488         EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
489             .WillOnce(Return(testRegionSize));
490         const std::vector<uint8_t> emptyArray(testQueueSize, 0);
491         EXPECT_CALL(*dataInterfaceMockPtr,
492                     write(0, ElementsAreArray(emptyArray)))
493             .WillOnce(Return(testQueueSize));
494 
495         EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
496             .WillOnce(Return(bufferHeaderSize));
497         EXPECT_NO_THROW(
498             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
499                                    testUeRegionSize, testMagicNumber));
500     }
501     static constexpr size_t expectedWriteSize = 3;
502     static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
503     static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
504 
505     static constexpr size_t testMaxOffset =
506         testQueueSize - testUeRegionSize - sizeof(struct CircularBufferHeader);
507     uint8_t* testInitializationHeaderPtr =
508         reinterpret_cast<uint8_t*>(&testInitializationHeader);
509 };
510 
TEST_F(BufferWraparoundReadTest,GetMaxOffsetPassTest)511 TEST_F(BufferWraparoundReadTest, GetMaxOffsetPassTest)
512 {
513     EXPECT_EQ(bufferImpl->getMaxOffset(), testMaxOffset);
514 }
515 
TEST_F(BufferWraparoundReadTest,GetQueueOffsetPassTest)516 TEST_F(BufferWraparoundReadTest, GetQueueOffsetPassTest)
517 {
518     EXPECT_EQ(bufferImpl->getQueueOffset(),
519               bufferHeaderSize + testUeRegionSize);
520 }
521 
TEST_F(BufferWraparoundReadTest,ParamsTooBigFail)522 TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
523 {
524     InSequence s;
525     size_t tooBigOffset = testMaxOffset + 1;
526     EXPECT_THROW(
527         try {
528             bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
529         } catch (const std::runtime_error& e) {
530             EXPECT_STREQ(
531                 e.what(),
532                 "[wraparoundRead] relativeOffset '385' was bigger than maxOffset '384'");
533             throw;
534         },
535         std::runtime_error);
536 
537     size_t tooBigLength = testMaxOffset + 1;
538     EXPECT_THROW(
539         try {
540             bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
541         } catch (const std::runtime_error& e) {
542             EXPECT_STREQ(e.what(), "[wraparoundRead] length '385' was bigger "
543                                    "than maxOffset '384'");
544             throw;
545         },
546         std::runtime_error);
547 }
548 
TEST_F(BufferWraparoundReadTest,NoWrapAroundReadFails)549 TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
550 {
551     InSequence s;
552     size_t testLength = 0x10;
553     size_t testOffset = 0x20;
554 
555     // Fail the first read
556     std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
557     EXPECT_CALL(*dataInterfaceMockPtr,
558                 read(testOffset + expectedqueueOffset, testLength))
559         .WillOnce(Return(shortTestBytesRead));
560 
561     EXPECT_THROW(
562         try {
563             bufferImpl->wraparoundRead(testOffset, testLength);
564         } catch (const std::runtime_error& e) {
565             EXPECT_STREQ(e.what(),
566                          "[wraparoundRead] Read '15' which was not the "
567                          "requested length of '16'");
568             throw;
569         },
570         std::runtime_error);
571 }
572 
TEST_F(BufferWraparoundReadTest,NoWrapAroundReadPass)573 TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
574 {
575     InSequence s;
576     size_t testLength = 0x10;
577     size_t testOffset = 0x20;
578 
579     // Successfully read all the requested length without a wrap around
580     std::vector<std::uint8_t> testBytesRead(testLength);
581     EXPECT_CALL(*dataInterfaceMockPtr,
582                 read(testOffset + expectedqueueOffset, testLength))
583         .WillOnce(Return(testBytesRead));
584 
585     // Call to updateReadPtr is triggered
586     const std::vector<uint8_t> expectedReadPtr{
587         static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
588     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
589                                              ElementsAreArray(expectedReadPtr)))
590         .WillOnce(Return(expectedWriteSize));
591 
592     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
593                 ElementsAreArray(testBytesRead));
594     struct CircularBufferHeader cachedBufferHeader =
595         bufferImpl->getCachedBufferHeader();
596     // The bmcReadPtr should have been updated
597     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
598               testOffset + testLength);
599 }
600 
TEST_F(BufferWraparoundReadTest,WrapAroundReadFails)601 TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
602 {
603     InSequence s;
604     size_t testBytesLeft = 3;
605     size_t testLength = 0x10;
606     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
607 
608     // Read until the end of the queue
609     std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
610     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
611                                             testLength - testBytesLeft))
612         .WillOnce(Return(testBytesReadShort));
613 
614     // Read 1 byte short after wraparound
615     std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
616     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
617         .WillOnce(Return(testBytesLeftReadShort));
618 
619     EXPECT_THROW(
620         try {
621             bufferImpl->wraparoundRead(testOffset, testLength);
622         } catch (const std::runtime_error& e) {
623             EXPECT_STREQ(
624                 e.what(),
625                 "[wraparoundRead] Buffer wrapped around but read '2' which was "
626                 "not the requested lenght of '3'");
627             throw;
628         },
629         std::runtime_error);
630 }
631 
TEST_F(BufferWraparoundReadTest,WrapAroundReadPasses)632 TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
633 {
634     InSequence s;
635     size_t testBytesLeft = 3;
636     size_t testLength = 0x10;
637     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
638 
639     // Read to the end of the queue
640     std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
641                                                  9,  8,  7,  6,  5,  4};
642     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
643                                             testLength - testBytesLeft))
644         .WillOnce(Return(testBytesReadFirst));
645 
646     std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
647     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
648         .WillOnce(Return(testBytesReadSecond));
649 
650     // Call to updateReadPtr is triggered
651     const std::vector<uint8_t> expectedReadPtr{
652         static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
653     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
654                                              ElementsAreArray(expectedReadPtr)))
655         .WillOnce(Return(expectedWriteSize));
656 
657     std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
658                                                8,  7,  6,  5,  4,  3,  2,  1};
659     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
660                 ElementsAreArray(expectedBytes));
661     struct CircularBufferHeader cachedBufferHeader =
662         bufferImpl->getCachedBufferHeader();
663     // The bmcReadPtr should have been updated to reflect the wraparound
664     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
665               testBytesLeft);
666 }
667 
TEST_F(BufferWraparoundReadTest,WrapAroundCornerCasePass)668 TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
669 {
670     InSequence s;
671     size_t testBytesLeft = 0;
672     size_t testLength = 4;
673     size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
674 
675     // Read to the very end of the queue
676     std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
677     EXPECT_CALL(*dataInterfaceMockPtr,
678                 read(testOffset + expectedqueueOffset, testLength))
679         .WillOnce(Return(testBytes));
680 
681     // Call to updateReadPtr is triggered, since we read to the very end of the
682     // buffer, update the readPtr up around to 0
683     const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
684     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
685                                              ElementsAreArray(expectedReadPtr)))
686         .WillOnce(Return(expectedWriteSize));
687 
688     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
689                 ElementsAreArray(testBytes));
690     struct CircularBufferHeader cachedBufferHeader =
691         bufferImpl->getCachedBufferHeader();
692     // The bmcReadPtr should have been updated to reflect the wraparound
693     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
694               0);
695 }
696 
697 class BufferEntryTest : public BufferWraparoundReadTest
698 {
699   protected:
BufferEntryTest()700     BufferEntryTest()
701     {
702         testEntryHeader.sequenceId = testSequenceId;
703         testEntryHeader.entrySize = testEntrySize;
704         testEntryHeader.checksum = testChecksum;
705         testEntryHeader.rdeCommandType = testRdeCommandType;
706     }
707     ~BufferEntryTest() override = default;
708 
wraparoundReadMock(const uint32_t relativeOffset,std::span<std::uint8_t> expetedBytesOutput)709     void wraparoundReadMock(const uint32_t relativeOffset,
710                             std::span<std::uint8_t> expetedBytesOutput)
711     {
712         InSequence s;
713         const uint32_t queueSizeToQueueEnd = testMaxOffset - relativeOffset;
714 
715         // This will wrap, split the read mocks in 2
716         if (expetedBytesOutput.size() > queueSizeToQueueEnd)
717         {
718             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
719                 .WillOnce(Return(std::vector<std::uint8_t>(
720                     expetedBytesOutput.begin(),
721                     expetedBytesOutput.begin() + queueSizeToQueueEnd)));
722             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
723                 .WillOnce(Return(std::vector<std::uint8_t>(
724                     expetedBytesOutput.begin() + queueSizeToQueueEnd,
725                     expetedBytesOutput.end())));
726         }
727         else
728         {
729             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
730                 .WillOnce(Return(std::vector<std::uint8_t>(
731                     expetedBytesOutput.begin(), expetedBytesOutput.end())));
732         }
733 
734         EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
735             .WillOnce(Return(expectedWriteSize));
736     }
737 
738     static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
739     static constexpr uint16_t testSequenceId = 0;
740     static constexpr uint16_t testEntrySize = 0x20;
741     static constexpr uint8_t testRdeCommandType = 0x01;
742     // Calculated checksum for the header
743     static constexpr uint8_t testChecksum =
744         (testSequenceId ^ testEntrySize ^ testRdeCommandType);
745     size_t testOffset = 0x0;
746 
747     struct QueueEntryHeader testEntryHeader{};
748 };
749 
TEST_F(BufferEntryTest,ReadEntryHeaderPass)750 TEST_F(BufferEntryTest, ReadEntryHeaderPass)
751 {
752     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
753     std::vector<uint8_t> testEntryHeaderVector(
754         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
755     wraparoundReadMock(testOffset, testEntryHeaderVector);
756     EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
757     // Check the bmcReadPtr
758     struct CircularBufferHeader cachedBufferHeader =
759         bufferImpl->getCachedBufferHeader();
760     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
761               testOffset + testEntryHeaderVector.size());
762 }
763 
TEST_F(BufferEntryTest,ReadEntryChecksumFail)764 TEST_F(BufferEntryTest, ReadEntryChecksumFail)
765 {
766     InSequence s;
767     std::vector<uint8_t> testEntryVector(testEntrySize);
768     // Offset the checksum by 1
769     testEntryHeader.checksum += 1;
770     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
771     std::vector<uint8_t> testEntryHeaderVector(
772         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
773     wraparoundReadMock(testOffset, testEntryHeaderVector);
774     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
775     EXPECT_THROW(
776         try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
777             // Calculation: testChecksum (0x21) XOR (0x22) = 3
778             EXPECT_STREQ(e.what(),
779                          "[readEntry] Checksum was '3', expected '0'");
780             throw;
781         },
782         std::runtime_error);
783 }
784 
TEST_F(BufferEntryTest,ReadEntryPassWraparound)785 TEST_F(BufferEntryTest, ReadEntryPassWraparound)
786 {
787     InSequence s;
788     // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
789     // (20 times) = 0 therefore leave the checksum as is
790     std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
791     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
792     std::vector<uint8_t> testEntryHeaderVector(
793         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
794     // Set testOffset so that we can test the wraparound here at the header and
795     // update the readPtr
796     testOffset = testMaxOffset - 1;
797     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
798         .WillOnce(Return(expectedWriteSize));
799     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
800 
801     wraparoundReadMock(testOffset, testEntryHeaderVector);
802     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
803 
804     EntryPair testedEntryPair;
805     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
806     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
807     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
808     struct CircularBufferHeader cachedBufferHeader =
809         bufferImpl->getCachedBufferHeader();
810     // The bmcReadPtr should have been updated to reflect the wraparound
811     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
812               entryHeaderSize + testEntrySize - 1);
813 
814     // Set testOffset so that we can test the wraparound here as well on our
815     // second read for the entry (by 1 byte)
816     testOffset = testMaxOffset - entryHeaderSize - 1;
817     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
818         .WillOnce(Return(expectedWriteSize));
819     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
820 
821     wraparoundReadMock(testOffset, testEntryHeaderVector);
822     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
823 
824     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
825     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
826     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
827     cachedBufferHeader = bufferImpl->getCachedBufferHeader();
828     // The bmcReadPtr should have been updated to reflect the wraparound
829     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
830               testEntrySize - 1);
831 }
832 
833 class BufferReadErrorLogsTest : public BufferEntryTest
834 {
835   protected:
836     BufferReadErrorLogsTest() = default;
837 
838     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
839     size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
840 };
841 
TEST_F(BufferReadErrorLogsTest,PtrsTooBigFail)842 TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
843 {
844     InSequence s;
845     // Set the biosWritePtr too big
846     testInitializationHeader.biosWritePtr =
847         boost::endian::native_to_little((testMaxOffset + 1));
848     initializeFuncMock();
849 
850     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
851         .WillOnce(Return(std::vector<uint8_t>(
852             testInitializationHeaderPtr,
853             testInitializationHeaderPtr + bufferHeaderSize)));
854     EXPECT_THROW(
855         try {
856             bufferImpl->readErrorLogs();
857         } catch (const std::runtime_error& e) {
858             EXPECT_STREQ(e.what(),
859                          "[readErrorLogs] currentBiosWritePtr was '385' "
860                          "which was bigger than maxOffset '384'");
861             throw;
862         },
863         std::runtime_error);
864 
865     // Reset the biosWritePtr and set the bmcReadPtr too big
866     testInitializationHeader.biosWritePtr = 0;
867     initializeFuncMock();
868     testInitializationHeader.bmcReadPtr =
869         boost::endian::native_to_little((testMaxOffset + 1));
870     initializeFuncMock();
871 
872     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
873         .WillOnce(Return(std::vector<uint8_t>(
874             testInitializationHeaderPtr,
875             testInitializationHeaderPtr + bufferHeaderSize)));
876     EXPECT_THROW(
877         try {
878             bufferImpl->readErrorLogs();
879         } catch (const std::runtime_error& e) {
880             EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '385' "
881                                    "which was bigger than maxOffset '384'");
882             throw;
883         },
884         std::runtime_error);
885 }
886 
TEST_F(BufferReadErrorLogsTest,IdenticalPtrsPass)887 TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
888 {
889     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
890         .WillOnce(Return(std::vector<uint8_t>(
891             testInitializationHeaderPtr,
892             testInitializationHeaderPtr + bufferHeaderSize)));
893     EXPECT_NO_THROW(bufferImpl->readErrorLogs());
894 }
895 
TEST_F(BufferReadErrorLogsTest,NoWraparoundPass)896 TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
897 {
898     InSequence s;
899     // Set the biosWritePtr to 1 entryHeader + entry size
900     testInitializationHeader.biosWritePtr =
901         boost::endian::native_to_little((entryAndHeaderSize));
902     initializeFuncMock();
903     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
904         .WillOnce(Return(std::vector<uint8_t>(
905             testInitializationHeaderPtr,
906             testInitializationHeaderPtr + bufferHeaderSize)));
907     std::vector<uint8_t> testEntryHeaderVector(
908         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
909     std::vector<uint8_t> testEntryVector(testEntrySize);
910     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
911     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
912 
913     std::vector<EntryPair> entryPairs;
914     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
915 
916     // Check that we only read one entryPair and that the content is correct
917     EXPECT_EQ(entryPairs.size(), 1U);
918     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
919     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
920 }
921 
TEST_F(BufferReadErrorLogsTest,WraparoundMultiplEntryPass)922 TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
923 {
924     InSequence s;
925     // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
926     uint32_t entryAndHeaderSizeAwayFromEnd = testMaxOffset - entryAndHeaderSize;
927     testInitializationHeader.bmcReadPtr =
928         boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
929     // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
930     testInitializationHeader.biosWritePtr = entryAndHeaderSize;
931     initializeFuncMock();
932     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
933         .WillOnce(Return(std::vector<uint8_t>(
934             testInitializationHeaderPtr,
935             testInitializationHeaderPtr + bufferHeaderSize)));
936 
937     std::vector<uint8_t> testEntryHeaderVector(
938         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
939     std::vector<uint8_t> testEntryVector(testEntrySize);
940     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
941                        testEntryHeaderVector);
942     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
943                            entryHeaderSize,
944                        testEntryVector);
945     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
946                        testEntryHeaderVector);
947     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
948                            entryHeaderSize,
949                        testEntryVector);
950 
951     std::vector<EntryPair> entryPairs;
952     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
953 
954     // Check that we only read one entryPair and that the content is correct
955     EXPECT_EQ(entryPairs.size(), 2);
956     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
957     EXPECT_EQ(entryPairs[1].first, testEntryHeader);
958     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
959     EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
960 }
961 
TEST_F(BufferReadErrorLogsTest,WraparoundMismatchingPtrsFail)962 TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
963 {
964     InSequence s;
965     testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
966     // Make the biosWritePtr intentially 1 smaller than expected
967     testInitializationHeader.biosWritePtr =
968         boost::endian::native_to_little(entryAndHeaderSize - 1);
969     initializeFuncMock();
970     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
971         .WillOnce(Return(std::vector<uint8_t>(
972             testInitializationHeaderPtr,
973             testInitializationHeaderPtr + bufferHeaderSize)));
974 
975     std::vector<uint8_t> testEntryHeaderVector(
976         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
977     std::vector<uint8_t> testEntryVector(testEntrySize);
978     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
979     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
980 
981     EXPECT_THROW(
982         try {
983             bufferImpl->readErrorLogs();
984         } catch (const std::runtime_error& e) {
985             EXPECT_STREQ(
986                 e.what(),
987                 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
988                 "are not identical after reading through all the logs");
989             throw;
990         },
991         std::runtime_error);
992 }
993 
994 } // namespace
995 } // namespace bios_bmc_smm_error_logger
996