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