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:
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 uint16_t testQueueSize = 0x100;
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 
57     std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
58     DataInterfaceMock* dataInterfaceMockPtr;
59     std::unique_ptr<BufferImpl> bufferImpl;
60 };
61 
62 TEST_F(BufferTest, BufferInitializeEraseFail)
63 {
64     InSequence s;
65     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
66         .WillOnce(Return(testRegionSize));
67     EXPECT_THROW(
68         try {
69             // Test too big of a proposed buffer compared to the memori size
70             uint16_t bigQueueSize = 0x181;
71             uint16_t bigUeRegionSize = 0x50;
72             bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
73                                    bigUeRegionSize, testMagicNumber);
74         } catch (const std::runtime_error& e) {
75             EXPECT_STREQ(
76                 e.what(),
77                 "[initialize] Proposed region size '513' "
78                 "is bigger than the BMC's allocated MMIO region of '512'");
79             throw;
80         },
81         std::runtime_error);
82     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
83 
84     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
85         .WillOnce(Return(testRegionSize));
86     size_t testProposedBufferSize =
87         sizeof(struct CircularBufferHeader) + testUeRegionSize + testQueueSize;
88     const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
89     // Return a smaller write than the intended testRegionSize to test the error
90     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
91         .WillOnce(Return(testProposedBufferSize - 1));
92     EXPECT_THROW(
93         try {
94             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
95                                    testUeRegionSize, testMagicNumber);
96         } catch (const std::runtime_error& e) {
97             EXPECT_STREQ(e.what(), "[initialize] Only erased '383'");
98             throw;
99         },
100         std::runtime_error);
101     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
102 
103     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
104         .WillOnce(Return(testRegionSize));
105     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
106         .WillOnce(Return(testProposedBufferSize));
107     // Return a smaller write than the intended initializationHeader to test the
108     // error
109     EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
110     EXPECT_THROW(
111         try {
112             bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
113                                    testUeRegionSize, testMagicNumber);
114         } catch (const std::runtime_error& e) {
115             EXPECT_STREQ(e.what(),
116                          "[initialize] Only wrote '0' bytes of the header");
117             throw;
118         },
119         std::runtime_error);
120     EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
121 }
122 
123 TEST_F(BufferTest, BufferInitializePass)
124 {
125     InSequence s;
126     EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
127         .WillOnce(Return(testRegionSize));
128     size_t testProposedBufferSize =
129         sizeof(struct CircularBufferHeader) + testUeRegionSize + testQueueSize;
130     const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
131     EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
132         .WillOnce(Return(testProposedBufferSize));
133 
134     uint8_t* testInitializationHeaderPtr =
135         reinterpret_cast<uint8_t*>(&testInitializationHeader);
136     EXPECT_CALL(*dataInterfaceMockPtr,
137                 write(0, ElementsAreArray(testInitializationHeaderPtr,
138                                           bufferHeaderSize)))
139         .WillOnce(Return(bufferHeaderSize));
140     EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
141                                            testQueueSize, testUeRegionSize,
142                                            testMagicNumber));
143     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
144 }
145 
146 TEST_F(BufferTest, BufferHeaderReadFail)
147 {
148     std::vector<std::uint8_t> testBytesRead{};
149     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
150         .WillOnce(Return(testBytesRead));
151     EXPECT_THROW(
152         try {
153             bufferImpl->readBufferHeader();
154         } catch (const std::runtime_error& e) {
155             EXPECT_STREQ(e.what(),
156                          "Buffer header read only read '0', expected '48'");
157             throw;
158         },
159         std::runtime_error);
160 }
161 
162 TEST_F(BufferTest, BufferHeaderReadPass)
163 {
164     uint8_t* testInitializationHeaderPtr =
165         reinterpret_cast<uint8_t*>(&testInitializationHeader);
166     std::vector<uint8_t> testInitializationHeaderVector(
167         testInitializationHeaderPtr,
168         testInitializationHeaderPtr + bufferHeaderSize);
169 
170     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
171         .WillOnce(Return(testInitializationHeaderVector));
172     EXPECT_NO_THROW(bufferImpl->readBufferHeader());
173     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
174 }
175 
176 TEST_F(BufferTest, BufferUpdateReadPtrFail)
177 {
178     // Return write size that is not 2 which is sizeof(little_uint16_t)
179     constexpr size_t wrongWriteSize = 1;
180     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
181         .WillOnce(Return(wrongWriteSize));
182     EXPECT_THROW(
183         try {
184             bufferImpl->updateReadPtr(0);
185         } catch (const std::runtime_error& e) {
186             EXPECT_STREQ(
187                 e.what(),
188                 "[updateReadPtr] Wrote '1' bytes, instead of expected '2'");
189             throw;
190         },
191         std::runtime_error);
192 }
193 
194 TEST_F(BufferTest, BufferUpdateReadPtrPass)
195 {
196     constexpr size_t expectedWriteSize = 2;
197     constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
198     // Check that we truncate the highest 16bits
199     const uint32_t testNewReadPtr = 0x99881234;
200     const std::vector<uint8_t> expectedReadPtr{0x34, 0x12};
201 
202     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
203                                              ElementsAreArray(expectedReadPtr)))
204         .WillOnce(Return(expectedWriteSize));
205     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
206 }
207 
208 class BufferWraparoundReadTest : public BufferTest
209 {
210   protected:
211     BufferWraparoundReadTest()
212     {
213         initializeFuncMock();
214     }
215     void initializeFuncMock()
216     {
217         // Initialize the memory and the cachedBufferHeader
218         InSequence s;
219         EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
220             .WillOnce(Return(testRegionSize));
221         size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) +
222                                         testUeRegionSize + testQueueSize;
223         const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
224         EXPECT_CALL(*dataInterfaceMockPtr,
225                     write(0, ElementsAreArray(emptyArray)))
226             .WillOnce(Return(testProposedBufferSize));
227 
228         EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
229             .WillOnce(Return(bufferHeaderSize));
230         EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
231                                                testQueueSize, testUeRegionSize,
232                                                testMagicNumber));
233     }
234     static constexpr size_t expectedWriteSize = 2;
235     static constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
236     static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
237 
238     uint8_t* testInitializationHeaderPtr =
239         reinterpret_cast<uint8_t*>(&testInitializationHeader);
240 };
241 
242 TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
243 {
244     InSequence s;
245 
246     size_t tooBigOffset = testQueueSize + 1;
247     EXPECT_THROW(
248         try {
249             bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
250         } catch (const std::runtime_error& e) {
251             EXPECT_STREQ(
252                 e.what(),
253                 "[wraparoundRead] relativeOffset '257' was bigger than queueSize '256'");
254             throw;
255         },
256         std::runtime_error);
257 
258     size_t tooBigLength = testQueueSize + 1;
259     EXPECT_THROW(
260         try {
261             bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
262         } catch (const std::runtime_error& e) {
263             EXPECT_STREQ(e.what(), "[wraparoundRead] length '257' was bigger "
264                                    "than queueSize '256'");
265             throw;
266         },
267         std::runtime_error);
268 }
269 
270 TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
271 {
272     InSequence s;
273     size_t testLength = 0x10;
274     size_t testOffset = 0x20;
275 
276     // Fail the first read
277     std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
278     EXPECT_CALL(*dataInterfaceMockPtr,
279                 read(testOffset + expectedqueueOffset, testLength))
280         .WillOnce(Return(shortTestBytesRead));
281 
282     EXPECT_THROW(
283         try {
284             bufferImpl->wraparoundRead(testOffset, testLength);
285         } catch (const std::runtime_error& e) {
286             EXPECT_STREQ(e.what(),
287                          "[wraparoundRead] Read '15' which was not the "
288                          "requested length of '16'");
289             throw;
290         },
291         std::runtime_error);
292 }
293 
294 TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
295 {
296     InSequence s;
297     size_t testLength = 0x10;
298     size_t testOffset = 0x20;
299 
300     // Successfully read all the requested length without a wrap around
301     std::vector<std::uint8_t> testBytesRead(testLength);
302     EXPECT_CALL(*dataInterfaceMockPtr,
303                 read(testOffset + expectedqueueOffset, testLength))
304         .WillOnce(Return(testBytesRead));
305 
306     // Call to updateReadPtr is triggered
307     const std::vector<uint8_t> expectedReadPtr{
308         static_cast<uint8_t>(testOffset + testLength), 0x0};
309     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
310                                              ElementsAreArray(expectedReadPtr)))
311         .WillOnce(Return(expectedWriteSize));
312 
313     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
314                 ElementsAreArray(testBytesRead));
315     struct CircularBufferHeader cachedBufferHeader =
316         bufferImpl->getCachedBufferHeader();
317     // The bmcReadPtr should have been updated
318     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
319               testOffset + testLength);
320 }
321 
322 TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
323 {
324     InSequence s;
325     size_t testBytesLeft = 3;
326     size_t testLength = 0x10;
327     size_t testOffset = testQueueSize - (testLength - testBytesLeft);
328 
329     // Read until the end of the queue
330     std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
331     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
332                                             testLength - testBytesLeft))
333         .WillOnce(Return(testBytesReadShort));
334 
335     // Read 1 byte short after wraparound
336     std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
337     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
338         .WillOnce(Return(testBytesLeftReadShort));
339 
340     EXPECT_THROW(
341         try {
342             bufferImpl->wraparoundRead(testOffset, testLength);
343         } catch (const std::runtime_error& e) {
344             EXPECT_STREQ(
345                 e.what(),
346                 "[wraparoundRead] Buffer wrapped around but read '2' which was "
347                 "not the requested lenght of '3'");
348             throw;
349         },
350         std::runtime_error);
351 }
352 
353 TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
354 {
355     InSequence s;
356     size_t testBytesLeft = 3;
357     size_t testLength = 0x10;
358     size_t testOffset = testQueueSize - (testLength - testBytesLeft);
359 
360     // Read to the end of the queue
361     std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
362                                                  9,  8,  7,  6,  5,  4};
363     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
364                                             testLength - testBytesLeft))
365         .WillOnce(Return(testBytesReadFirst));
366 
367     std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
368     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
369         .WillOnce(Return(testBytesReadSecond));
370 
371     // Call to updateReadPtr is triggered
372     const std::vector<uint8_t> expectedReadPtr{
373         static_cast<uint8_t>(testBytesLeft), 0x0};
374     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
375                                              ElementsAreArray(expectedReadPtr)))
376         .WillOnce(Return(expectedWriteSize));
377 
378     std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
379                                                8,  7,  6,  5,  4,  3,  2,  1};
380     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
381                 ElementsAreArray(expectedBytes));
382     struct CircularBufferHeader cachedBufferHeader =
383         bufferImpl->getCachedBufferHeader();
384     // The bmcReadPtr should have been updated to reflect the wraparound
385     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
386               testBytesLeft);
387 }
388 
389 TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
390 {
391     InSequence s;
392     size_t testBytesLeft = 0;
393     size_t testLength = 4;
394     size_t testOffset = testQueueSize - (testLength - testBytesLeft);
395 
396     // Read to the very end of the queue
397     std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
398     EXPECT_CALL(*dataInterfaceMockPtr,
399                 read(testOffset + expectedqueueOffset, testLength))
400         .WillOnce(Return(testBytes));
401 
402     // Call to updateReadPtr is triggered, since we read to the very end of the
403     // buffer, update the readPtr up around to 0
404     const std::vector<uint8_t> expectedReadPtr{0x0, 0x0};
405     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
406                                              ElementsAreArray(expectedReadPtr)))
407         .WillOnce(Return(expectedWriteSize));
408 
409     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
410                 ElementsAreArray(testBytes));
411     struct CircularBufferHeader cachedBufferHeader =
412         bufferImpl->getCachedBufferHeader();
413     // The bmcReadPtr should have been updated to reflect the wraparound
414     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
415               0);
416 }
417 
418 class BufferEntryTest : public BufferWraparoundReadTest
419 {
420   protected:
421     BufferEntryTest()
422     {
423         testEntryHeader.sequenceId = testSequenceId;
424         testEntryHeader.entrySize = testEntrySize;
425         testEntryHeader.checksum = testChecksum;
426         testEntryHeader.rdeCommandType = testRdeCommandType;
427     }
428     ~BufferEntryTest() override = default;
429 
430     void wraparoundReadMock(const uint32_t relativeOffset,
431                             std::span<std::uint8_t> expetedBytesOutput)
432     {
433         InSequence s;
434         const uint32_t queueSizeToQueueEnd = testQueueSize - relativeOffset;
435 
436         // This will wrap, split the read mocks in 2
437         if (expetedBytesOutput.size() > queueSizeToQueueEnd)
438         {
439             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
440                 .WillOnce(Return(std::vector<std::uint8_t>(
441                     expetedBytesOutput.begin(),
442                     expetedBytesOutput.begin() + queueSizeToQueueEnd)));
443             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
444                 .WillOnce(Return(std::vector<std::uint8_t>(
445                     expetedBytesOutput.begin() + queueSizeToQueueEnd,
446                     expetedBytesOutput.end())));
447         }
448         else
449         {
450             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
451                 .WillOnce(Return(std::vector<std::uint8_t>(
452                     expetedBytesOutput.begin(), expetedBytesOutput.end())));
453         }
454 
455         EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
456             .WillOnce(Return(expectedWriteSize));
457     }
458 
459     static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
460     static constexpr uint16_t testSequenceId = 0;
461     static constexpr uint16_t testEntrySize = 0x20;
462     static constexpr uint8_t testRdeCommandType = 0x01;
463     // Calculated checksum for the header
464     static constexpr uint8_t testChecksum =
465         (testSequenceId ^ testEntrySize ^ testRdeCommandType);
466     size_t testOffset = 0x20;
467 
468     struct QueueEntryHeader testEntryHeader
469     {};
470 };
471 
472 TEST_F(BufferEntryTest, ReadEntryHeaderPass)
473 {
474     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
475     std::vector<uint8_t> testEntryHeaderVector(
476         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
477     wraparoundReadMock(testOffset, testEntryHeaderVector);
478     EXPECT_EQ(bufferImpl->readEntryHeader(testOffset), testEntryHeader);
479     // Check the bmcReadPtr
480     struct CircularBufferHeader cachedBufferHeader =
481         bufferImpl->getCachedBufferHeader();
482     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
483               testOffset + testEntryHeaderVector.size());
484 }
485 
486 TEST_F(BufferEntryTest, ReadEntryChecksumFail)
487 {
488     InSequence s;
489     std::vector<uint8_t> testEntryVector(testEntrySize);
490     // Offset the checksum by 1
491     testEntryHeader.checksum += 1;
492     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
493     std::vector<uint8_t> testEntryHeaderVector(
494         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
495     wraparoundReadMock(testOffset, testEntryHeaderVector);
496     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
497     EXPECT_THROW(
498         try {
499             bufferImpl->readEntry(testOffset);
500         } catch (const std::runtime_error& e) {
501             // Calculation: testChecksum (0x21) XOR (0x22) = 3
502             EXPECT_STREQ(e.what(),
503                          "[readEntry] Checksum was '3', expected '0'");
504             throw;
505         },
506         std::runtime_error);
507 }
508 
509 TEST_F(BufferEntryTest, ReadEntryPass)
510 {
511     InSequence s;
512     // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
513     // (20 times) = 0 therefore leave the checksum as is
514     std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
515     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
516     std::vector<uint8_t> testEntryHeaderVector(
517         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
518     // Set testOffset so that we can test the wraparound here as well on our
519     // second read for the entry (by 1 byte)
520     testOffset = testQueueSize - entryHeaderSize - 1;
521     wraparoundReadMock(testOffset, testEntryHeaderVector);
522     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
523 
524     EntryPair testedEntryPair;
525     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry(testOffset));
526     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
527     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
528     struct CircularBufferHeader cachedBufferHeader =
529         bufferImpl->getCachedBufferHeader();
530     // The bmcReadPtr should have been updated to reflect the wraparound
531     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
532               testEntrySize - 1);
533 }
534 
535 class BufferReadErrorLogsTest : public BufferEntryTest
536 {
537   protected:
538     BufferReadErrorLogsTest() = default;
539 
540     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
541     size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
542 };
543 
544 TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
545 {
546     InSequence s;
547     // Set the biosWritePtr too big
548     testInitializationHeader.biosWritePtr =
549         boost::endian::native_to_little((testQueueSize + 1));
550     initializeFuncMock();
551 
552     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
553         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
554                                               testInitializationHeaderPtr +
555                                                   bufferHeaderSize)));
556     EXPECT_THROW(
557         try {
558             bufferImpl->readErrorLogs();
559         } catch (const std::runtime_error& e) {
560             EXPECT_STREQ(e.what(),
561                          "[readErrorLogs] currentBiosWritePtr was '257' "
562                          "which was bigger than queueSize '256'");
563             throw;
564         },
565         std::runtime_error);
566 
567     // Reset the biosWritePtr and set the bmcReadPtr too big
568     testInitializationHeader.biosWritePtr = 0;
569     initializeFuncMock();
570     testInitializationHeader.bmcReadPtr =
571         boost::endian::native_to_little((testQueueSize + 1));
572     initializeFuncMock();
573 
574     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
575         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
576                                               testInitializationHeaderPtr +
577                                                   bufferHeaderSize)));
578     EXPECT_THROW(
579         try {
580             bufferImpl->readErrorLogs();
581         } catch (const std::runtime_error& e) {
582             EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '257' "
583                                    "which was bigger than queueSize '256'");
584             throw;
585         },
586         std::runtime_error);
587 }
588 
589 TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
590 {
591     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
592         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
593                                               testInitializationHeaderPtr +
594                                                   bufferHeaderSize)));
595     EXPECT_NO_THROW(bufferImpl->readErrorLogs());
596 }
597 
598 TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
599 {
600     InSequence s;
601     // Set the biosWritePtr to 1 entryHeader + entry size
602     testInitializationHeader.biosWritePtr =
603         boost::endian::native_to_little((entryAndHeaderSize));
604     initializeFuncMock();
605     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
606         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
607                                               testInitializationHeaderPtr +
608                                                   bufferHeaderSize)));
609     std::vector<uint8_t> testEntryHeaderVector(
610         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
611     std::vector<uint8_t> testEntryVector(testEntrySize);
612     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
613     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
614 
615     std::vector<EntryPair> entryPairs;
616     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
617 
618     // Check that we only read one entryPair and that the content is correct
619     EXPECT_EQ(entryPairs.size(), 1);
620     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
621     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
622 }
623 
624 TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
625 {
626     InSequence s;
627     // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
628     uint32_t entryAndHeaderSizeAwayFromEnd = testQueueSize - entryAndHeaderSize;
629     testInitializationHeader.bmcReadPtr =
630         boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
631     // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
632     testInitializationHeader.biosWritePtr = entryAndHeaderSize;
633     initializeFuncMock();
634     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
635         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
636                                               testInitializationHeaderPtr +
637                                                   bufferHeaderSize)));
638 
639     std::vector<uint8_t> testEntryHeaderVector(
640         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
641     std::vector<uint8_t> testEntryVector(testEntrySize);
642     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
643                        testEntryHeaderVector);
644     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
645                            entryHeaderSize,
646                        testEntryVector);
647     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
648                        testEntryHeaderVector);
649     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
650                            entryHeaderSize,
651                        testEntryVector);
652 
653     std::vector<EntryPair> entryPairs;
654     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
655 
656     // Check that we only read one entryPair and that the content is correct
657     EXPECT_EQ(entryPairs.size(), 2);
658     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
659     EXPECT_EQ(entryPairs[1].first, testEntryHeader);
660     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
661     EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
662 }
663 
664 TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
665 {
666     InSequence s;
667     testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
668     // Make the biosWritePtr intentially 1 smaller than expected
669     testInitializationHeader.biosWritePtr =
670         boost::endian::native_to_little(entryAndHeaderSize - 1);
671     initializeFuncMock();
672     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
673         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
674                                               testInitializationHeaderPtr +
675                                                   bufferHeaderSize)));
676 
677     std::vector<uint8_t> testEntryHeaderVector(
678         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
679     std::vector<uint8_t> testEntryVector(testEntrySize);
680     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
681     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
682 
683     EXPECT_THROW(
684         try {
685             bufferImpl->readErrorLogs();
686         } catch (const std::runtime_error& e) {
687             EXPECT_STREQ(
688                 e.what(),
689                 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
690                 "are not identical after reading through all the logs");
691             throw;
692         },
693         std::runtime_error);
694 }
695 
696 } // namespace
697 } // namespace bios_bmc_smm_error_logger
698