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 uint32_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 '3'");
189             throw;
190         },
191         std::runtime_error);
192 }
193 
194 TEST_F(BufferTest, BufferUpdateReadPtrPass)
195 {
196     constexpr size_t expectedWriteSize = 3;
197     constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
198     // Check that we truncate the highest 24bits
199     const uint32_t testNewReadPtr = 0x99881234;
200     const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
201 
202     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
203                                              ElementsAreArray(expectedReadPtr)))
204         .WillOnce(Return(expectedWriteSize));
205     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
206 
207     auto cachedHeader = bufferImpl->getCachedBufferHeader();
208     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
209               0x881234);
210 }
211 
212 TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
213 {
214     // Return write size that is not 4 which is sizeof(little_uint32_t)
215     constexpr size_t wrongWriteSize = 1;
216     EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
217         .WillOnce(Return(wrongWriteSize));
218     EXPECT_THROW(
219         try {
220             bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
221         } catch (const std::runtime_error& e) {
222             EXPECT_STREQ(
223                 e.what(),
224                 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
225             throw;
226         },
227         std::runtime_error);
228 }
229 
230 TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
231 {
232     constexpr size_t expectedWriteSize = 4;
233     constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
234     const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
235 
236     EXPECT_CALL(*dataInterfaceMockPtr,
237                 write(expectedBmcReadPtrOffset,
238                       ElementsAreArray(expectedNewBmcFlagsVector)))
239         .WillOnce(Return(expectedWriteSize));
240     EXPECT_NO_THROW(
241         bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
242 
243     auto cachedHeader = bufferImpl->getCachedBufferHeader();
244     EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
245               static_cast<uint32_t>(BmcFlags::ready));
246 }
247 
248 class BufferWraparoundReadTest : public BufferTest
249 {
250   protected:
251     BufferWraparoundReadTest()
252     {
253         initializeFuncMock();
254     }
255     void initializeFuncMock()
256     {
257         // Initialize the memory and the cachedBufferHeader
258         InSequence s;
259         EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
260             .WillOnce(Return(testRegionSize));
261         size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) +
262                                         testUeRegionSize + testQueueSize;
263         const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0);
264         EXPECT_CALL(*dataInterfaceMockPtr,
265                     write(0, ElementsAreArray(emptyArray)))
266             .WillOnce(Return(testProposedBufferSize));
267 
268         EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
269             .WillOnce(Return(bufferHeaderSize));
270         EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion,
271                                                testQueueSize, testUeRegionSize,
272                                                testMagicNumber));
273     }
274     static constexpr size_t expectedWriteSize = 3;
275     static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
276     static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
277 
278     uint8_t* testInitializationHeaderPtr =
279         reinterpret_cast<uint8_t*>(&testInitializationHeader);
280 };
281 
282 TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
283 {
284     InSequence s;
285 
286     size_t tooBigOffset = testQueueSize + 1;
287     EXPECT_THROW(
288         try {
289             bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
290         } catch (const std::runtime_error& e) {
291             EXPECT_STREQ(
292                 e.what(),
293                 "[wraparoundRead] relativeOffset '257' was bigger than queueSize '256'");
294             throw;
295         },
296         std::runtime_error);
297 
298     size_t tooBigLength = testQueueSize + 1;
299     EXPECT_THROW(
300         try {
301             bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
302         } catch (const std::runtime_error& e) {
303             EXPECT_STREQ(e.what(), "[wraparoundRead] length '257' was bigger "
304                                    "than queueSize '256'");
305             throw;
306         },
307         std::runtime_error);
308 }
309 
310 TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
311 {
312     InSequence s;
313     size_t testLength = 0x10;
314     size_t testOffset = 0x20;
315 
316     // Fail the first read
317     std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
318     EXPECT_CALL(*dataInterfaceMockPtr,
319                 read(testOffset + expectedqueueOffset, testLength))
320         .WillOnce(Return(shortTestBytesRead));
321 
322     EXPECT_THROW(
323         try {
324             bufferImpl->wraparoundRead(testOffset, testLength);
325         } catch (const std::runtime_error& e) {
326             EXPECT_STREQ(e.what(),
327                          "[wraparoundRead] Read '15' which was not the "
328                          "requested length of '16'");
329             throw;
330         },
331         std::runtime_error);
332 }
333 
334 TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
335 {
336     InSequence s;
337     size_t testLength = 0x10;
338     size_t testOffset = 0x20;
339 
340     // Successfully read all the requested length without a wrap around
341     std::vector<std::uint8_t> testBytesRead(testLength);
342     EXPECT_CALL(*dataInterfaceMockPtr,
343                 read(testOffset + expectedqueueOffset, testLength))
344         .WillOnce(Return(testBytesRead));
345 
346     // Call to updateReadPtr is triggered
347     const std::vector<uint8_t> expectedReadPtr{
348         static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
349     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
350                                              ElementsAreArray(expectedReadPtr)))
351         .WillOnce(Return(expectedWriteSize));
352 
353     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
354                 ElementsAreArray(testBytesRead));
355     struct CircularBufferHeader cachedBufferHeader =
356         bufferImpl->getCachedBufferHeader();
357     // The bmcReadPtr should have been updated
358     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
359               testOffset + testLength);
360 }
361 
362 TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
363 {
364     InSequence s;
365     size_t testBytesLeft = 3;
366     size_t testLength = 0x10;
367     size_t testOffset = testQueueSize - (testLength - testBytesLeft);
368 
369     // Read until the end of the queue
370     std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
371     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
372                                             testLength - testBytesLeft))
373         .WillOnce(Return(testBytesReadShort));
374 
375     // Read 1 byte short after wraparound
376     std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
377     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
378         .WillOnce(Return(testBytesLeftReadShort));
379 
380     EXPECT_THROW(
381         try {
382             bufferImpl->wraparoundRead(testOffset, testLength);
383         } catch (const std::runtime_error& e) {
384             EXPECT_STREQ(
385                 e.what(),
386                 "[wraparoundRead] Buffer wrapped around but read '2' which was "
387                 "not the requested lenght of '3'");
388             throw;
389         },
390         std::runtime_error);
391 }
392 
393 TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
394 {
395     InSequence s;
396     size_t testBytesLeft = 3;
397     size_t testLength = 0x10;
398     size_t testOffset = testQueueSize - (testLength - testBytesLeft);
399 
400     // Read to the end of the queue
401     std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
402                                                  9,  8,  7,  6,  5,  4};
403     EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
404                                             testLength - testBytesLeft))
405         .WillOnce(Return(testBytesReadFirst));
406 
407     std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
408     EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
409         .WillOnce(Return(testBytesReadSecond));
410 
411     // Call to updateReadPtr is triggered
412     const std::vector<uint8_t> expectedReadPtr{
413         static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
414     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
415                                              ElementsAreArray(expectedReadPtr)))
416         .WillOnce(Return(expectedWriteSize));
417 
418     std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
419                                                8,  7,  6,  5,  4,  3,  2,  1};
420     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
421                 ElementsAreArray(expectedBytes));
422     struct CircularBufferHeader cachedBufferHeader =
423         bufferImpl->getCachedBufferHeader();
424     // The bmcReadPtr should have been updated to reflect the wraparound
425     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
426               testBytesLeft);
427 }
428 
429 TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
430 {
431     InSequence s;
432     size_t testBytesLeft = 0;
433     size_t testLength = 4;
434     size_t testOffset = testQueueSize - (testLength - testBytesLeft);
435 
436     // Read to the very end of the queue
437     std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
438     EXPECT_CALL(*dataInterfaceMockPtr,
439                 read(testOffset + expectedqueueOffset, testLength))
440         .WillOnce(Return(testBytes));
441 
442     // Call to updateReadPtr is triggered, since we read to the very end of the
443     // buffer, update the readPtr up around to 0
444     const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
445     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
446                                              ElementsAreArray(expectedReadPtr)))
447         .WillOnce(Return(expectedWriteSize));
448 
449     EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
450                 ElementsAreArray(testBytes));
451     struct CircularBufferHeader cachedBufferHeader =
452         bufferImpl->getCachedBufferHeader();
453     // The bmcReadPtr should have been updated to reflect the wraparound
454     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
455               0);
456 }
457 
458 class BufferEntryTest : public BufferWraparoundReadTest
459 {
460   protected:
461     BufferEntryTest()
462     {
463         testEntryHeader.sequenceId = testSequenceId;
464         testEntryHeader.entrySize = testEntrySize;
465         testEntryHeader.checksum = testChecksum;
466         testEntryHeader.rdeCommandType = testRdeCommandType;
467     }
468     ~BufferEntryTest() override = default;
469 
470     void wraparoundReadMock(const uint32_t relativeOffset,
471                             std::span<std::uint8_t> expetedBytesOutput)
472     {
473         InSequence s;
474         const uint32_t queueSizeToQueueEnd = testQueueSize - relativeOffset;
475 
476         // This will wrap, split the read mocks in 2
477         if (expetedBytesOutput.size() > queueSizeToQueueEnd)
478         {
479             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
480                 .WillOnce(Return(std::vector<std::uint8_t>(
481                     expetedBytesOutput.begin(),
482                     expetedBytesOutput.begin() + queueSizeToQueueEnd)));
483             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
484                 .WillOnce(Return(std::vector<std::uint8_t>(
485                     expetedBytesOutput.begin() + queueSizeToQueueEnd,
486                     expetedBytesOutput.end())));
487         }
488         else
489         {
490             EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
491                 .WillOnce(Return(std::vector<std::uint8_t>(
492                     expetedBytesOutput.begin(), expetedBytesOutput.end())));
493         }
494 
495         EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
496             .WillOnce(Return(expectedWriteSize));
497     }
498 
499     static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
500     static constexpr uint16_t testSequenceId = 0;
501     static constexpr uint16_t testEntrySize = 0x20;
502     static constexpr uint8_t testRdeCommandType = 0x01;
503     // Calculated checksum for the header
504     static constexpr uint8_t testChecksum =
505         (testSequenceId ^ testEntrySize ^ testRdeCommandType);
506     size_t testOffset = 0x0;
507 
508     struct QueueEntryHeader testEntryHeader
509     {};
510 };
511 
512 TEST_F(BufferEntryTest, ReadEntryHeaderPass)
513 {
514     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
515     std::vector<uint8_t> testEntryHeaderVector(
516         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
517     wraparoundReadMock(testOffset, testEntryHeaderVector);
518     EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
519     // Check the bmcReadPtr
520     struct CircularBufferHeader cachedBufferHeader =
521         bufferImpl->getCachedBufferHeader();
522     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
523               testOffset + testEntryHeaderVector.size());
524 }
525 
526 TEST_F(BufferEntryTest, ReadEntryChecksumFail)
527 {
528     InSequence s;
529     std::vector<uint8_t> testEntryVector(testEntrySize);
530     // Offset the checksum by 1
531     testEntryHeader.checksum += 1;
532     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
533     std::vector<uint8_t> testEntryHeaderVector(
534         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
535     wraparoundReadMock(testOffset, testEntryHeaderVector);
536     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
537     EXPECT_THROW(
538         try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
539             // Calculation: testChecksum (0x21) XOR (0x22) = 3
540             EXPECT_STREQ(e.what(),
541                          "[readEntry] Checksum was '3', expected '0'");
542             throw;
543         },
544         std::runtime_error);
545 }
546 
547 TEST_F(BufferEntryTest, ReadEntryPassWraparound)
548 {
549     InSequence s;
550     // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
551     // (20 times) = 0 therefore leave the checksum as is
552     std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
553     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
554     std::vector<uint8_t> testEntryHeaderVector(
555         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
556     // Set testOffset so that we can test the wraparound here at the header and
557     // update the readPtr
558     testOffset = testQueueSize - 1;
559     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
560         .WillOnce(Return(expectedWriteSize));
561     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
562 
563     wraparoundReadMock(testOffset, testEntryHeaderVector);
564     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
565 
566     EntryPair testedEntryPair;
567     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
568     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
569     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
570     struct CircularBufferHeader cachedBufferHeader =
571         bufferImpl->getCachedBufferHeader();
572     // The bmcReadPtr should have been updated to reflect the wraparound
573     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
574               entryHeaderSize + testEntrySize - 1);
575 
576     // Set testOffset so that we can test the wraparound here as well on our
577     // second read for the entry (by 1 byte)
578     testOffset = testQueueSize - entryHeaderSize - 1;
579     EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
580         .WillOnce(Return(expectedWriteSize));
581     EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
582 
583     wraparoundReadMock(testOffset, testEntryHeaderVector);
584     wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
585 
586     EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
587     EXPECT_EQ(testedEntryPair.first, testEntryHeader);
588     EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
589     cachedBufferHeader = bufferImpl->getCachedBufferHeader();
590     // The bmcReadPtr should have been updated to reflect the wraparound
591     EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
592               testEntrySize - 1);
593 }
594 
595 class BufferReadErrorLogsTest : public BufferEntryTest
596 {
597   protected:
598     BufferReadErrorLogsTest() = default;
599 
600     uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
601     size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
602 };
603 
604 TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
605 {
606     InSequence s;
607     // Set the biosWritePtr too big
608     testInitializationHeader.biosWritePtr =
609         boost::endian::native_to_little((testQueueSize + 1));
610     initializeFuncMock();
611 
612     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
613         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
614                                               testInitializationHeaderPtr +
615                                                   bufferHeaderSize)));
616     EXPECT_THROW(
617         try {
618             bufferImpl->readErrorLogs();
619         } catch (const std::runtime_error& e) {
620             EXPECT_STREQ(e.what(),
621                          "[readErrorLogs] currentBiosWritePtr was '257' "
622                          "which was bigger than queueSize '256'");
623             throw;
624         },
625         std::runtime_error);
626 
627     // Reset the biosWritePtr and set the bmcReadPtr too big
628     testInitializationHeader.biosWritePtr = 0;
629     initializeFuncMock();
630     testInitializationHeader.bmcReadPtr =
631         boost::endian::native_to_little((testQueueSize + 1));
632     initializeFuncMock();
633 
634     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
635         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
636                                               testInitializationHeaderPtr +
637                                                   bufferHeaderSize)));
638     EXPECT_THROW(
639         try {
640             bufferImpl->readErrorLogs();
641         } catch (const std::runtime_error& e) {
642             EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '257' "
643                                    "which was bigger than queueSize '256'");
644             throw;
645         },
646         std::runtime_error);
647 }
648 
649 TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
650 {
651     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
652         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
653                                               testInitializationHeaderPtr +
654                                                   bufferHeaderSize)));
655     EXPECT_NO_THROW(bufferImpl->readErrorLogs());
656 }
657 
658 TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
659 {
660     InSequence s;
661     // Set the biosWritePtr to 1 entryHeader + entry size
662     testInitializationHeader.biosWritePtr =
663         boost::endian::native_to_little((entryAndHeaderSize));
664     initializeFuncMock();
665     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
666         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
667                                               testInitializationHeaderPtr +
668                                                   bufferHeaderSize)));
669     std::vector<uint8_t> testEntryHeaderVector(
670         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
671     std::vector<uint8_t> testEntryVector(testEntrySize);
672     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
673     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
674 
675     std::vector<EntryPair> entryPairs;
676     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
677 
678     // Check that we only read one entryPair and that the content is correct
679     EXPECT_EQ(entryPairs.size(), 1U);
680     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
681     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
682 }
683 
684 TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
685 {
686     InSequence s;
687     // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
688     uint32_t entryAndHeaderSizeAwayFromEnd = testQueueSize - entryAndHeaderSize;
689     testInitializationHeader.bmcReadPtr =
690         boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
691     // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
692     testInitializationHeader.biosWritePtr = entryAndHeaderSize;
693     initializeFuncMock();
694     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
695         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
696                                               testInitializationHeaderPtr +
697                                                   bufferHeaderSize)));
698 
699     std::vector<uint8_t> testEntryHeaderVector(
700         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
701     std::vector<uint8_t> testEntryVector(testEntrySize);
702     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
703                        testEntryHeaderVector);
704     wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
705                            entryHeaderSize,
706                        testEntryVector);
707     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
708                        testEntryHeaderVector);
709     wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
710                            entryHeaderSize,
711                        testEntryVector);
712 
713     std::vector<EntryPair> entryPairs;
714     EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
715 
716     // Check that we only read one entryPair and that the content is correct
717     EXPECT_EQ(entryPairs.size(), 2);
718     EXPECT_EQ(entryPairs[0].first, testEntryHeader);
719     EXPECT_EQ(entryPairs[1].first, testEntryHeader);
720     EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
721     EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
722 }
723 
724 TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
725 {
726     InSequence s;
727     testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
728     // Make the biosWritePtr intentially 1 smaller than expected
729     testInitializationHeader.biosWritePtr =
730         boost::endian::native_to_little(entryAndHeaderSize - 1);
731     initializeFuncMock();
732     EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
733         .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr,
734                                               testInitializationHeaderPtr +
735                                                   bufferHeaderSize)));
736 
737     std::vector<uint8_t> testEntryHeaderVector(
738         testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
739     std::vector<uint8_t> testEntryVector(testEntrySize);
740     wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
741     wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
742 
743     EXPECT_THROW(
744         try {
745             bufferImpl->readErrorLogs();
746         } catch (const std::runtime_error& e) {
747             EXPECT_STREQ(
748                 e.what(),
749                 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
750                 "are not identical after reading through all the logs");
751             throw;
752         },
753         std::runtime_error);
754 }
755 
756 } // namespace
757 } // namespace bios_bmc_smm_error_logger
758