blob: d6f355a690616613b28f6c643663c1af314e18a8 [file] [log] [blame]
Brandon Kimfcbc3db2022-06-06 21:26:18 -07001#include "buffer.hpp"
2#include "data_interface_mock.hpp"
3
Brandon Kim17ee1a92022-06-07 21:04:08 -07004#include <boost/endian/arithmetic.hpp>
5#include <boost/endian/conversion.hpp>
6
7#include <algorithm>
Brandon Kimfcbc3db2022-06-06 21:26:18 -07008#include <array>
9#include <cstdint>
10#include <memory>
11
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15namespace bios_bmc_smm_error_logger
16{
17namespace
18{
19
20using ::testing::_;
21using ::testing::ElementsAreArray;
22using ::testing::InSequence;
23using ::testing::Return;
24
25class 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;
Brandon Kim17ee1a92022-06-07 21:04:08 -070036 std::transform(testMagicNumber.begin(), testMagicNumber.end(),
37 testInitializationHeader.magicNumber.begin(),
38 [](uint32_t number) -> little_uint32_t {
Patrick Williams1a643562024-08-16 15:22:05 -040039 return boost::endian::native_to_little(number);
40 });
Brandon Kimfcbc3db2022-06-06 21:26:18 -070041 }
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;
Brandon Kim3def3c82022-09-13 05:29:20 +000047 static constexpr uint32_t testQueueSize = 0x200;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070048 static constexpr uint16_t testUeRegionSize = 0x50;
49 static constexpr std::array<uint32_t, 4> testMagicNumber = {
50 0x12345678, 0x22345678, 0x32345678, 0x42345678};
Brandon Kim17ee1a92022-06-07 21:04:08 -070051 static constexpr size_t bufferHeaderSize =
52 sizeof(struct CircularBufferHeader);
53
Patrick Williamsc20d1212024-12-18 11:22:36 -050054 struct CircularBufferHeader testInitializationHeader{};
Brandon Kimfcbc3db2022-06-06 21:26:18 -070055
56 std::unique_ptr<DataInterfaceMock> dataInterfaceMock;
57 DataInterfaceMock* dataInterfaceMockPtr;
Brandon Kimfcbc3db2022-06-06 21:26:18 -070058 std::unique_ptr<BufferImpl> bufferImpl;
59};
60
61TEST_F(BufferTest, BufferInitializeEraseFail)
62{
63 InSequence s;
Brandon Kim26660e92022-06-15 16:58:44 -070064 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
65 .WillOnce(Return(testRegionSize));
66 EXPECT_THROW(
67 try {
68 // Test too big of a proposed buffer compared to the memori size
Brandon Kim3def3c82022-09-13 05:29:20 +000069 uint16_t bigQueueSize = 0x201;
Brandon Kim26660e92022-06-15 16:58:44 -070070 uint16_t bigUeRegionSize = 0x50;
71 bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize,
72 bigUeRegionSize, testMagicNumber);
73 } catch (const std::runtime_error& e) {
74 EXPECT_STREQ(
75 e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +000076 "[initialize] Proposed queue size '513' is bigger than the BMC's allocated MMIO region of '512'");
Brandon Kim26660e92022-06-15 16:58:44 -070077 throw;
78 },
79 std::runtime_error);
80 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070081
82 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
83 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +000084 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070085 // Return a smaller write than the intended testRegionSize to test the error
86 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +000087 .WillOnce(Return(testQueueSize - 1));
Brandon Kimfcbc3db2022-06-06 21:26:18 -070088 EXPECT_THROW(
89 try {
90 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
91 testUeRegionSize, testMagicNumber);
92 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +000093 EXPECT_STREQ(e.what(), "[initialize] Only erased '511'");
Brandon Kimfcbc3db2022-06-06 21:26:18 -070094 throw;
95 },
96 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -070097 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -070098
99 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
100 .WillOnce(Return(testRegionSize));
101 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000102 .WillOnce(Return(testQueueSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700103 // Return a smaller write than the intended initializationHeader to test the
104 // error
105 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0));
106 EXPECT_THROW(
107 try {
108 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
109 testUeRegionSize, testMagicNumber);
110 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700111 EXPECT_STREQ(e.what(),
112 "[initialize] Only wrote '0' bytes of the header");
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700113 throw;
114 },
115 std::runtime_error);
Brandon Kim60cab572022-06-15 14:20:05 -0700116 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700117}
118
119TEST_F(BufferTest, BufferInitializePass)
120{
121 InSequence s;
122 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
123 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +0000124 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700125 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000126 .WillOnce(Return(testQueueSize));
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700127
128 uint8_t* testInitializationHeaderPtr =
129 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700130 EXPECT_CALL(*dataInterfaceMockPtr,
131 write(0, ElementsAreArray(testInitializationHeaderPtr,
Brandon Kim17ee1a92022-06-07 21:04:08 -0700132 bufferHeaderSize)))
133 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400134 EXPECT_NO_THROW(
135 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
136 testUeRegionSize, testMagicNumber));
Brandon Kim60cab572022-06-15 14:20:05 -0700137 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700138}
139
Brandon Kim17ee1a92022-06-07 21:04:08 -0700140TEST_F(BufferTest, BufferHeaderReadFail)
141{
142 std::vector<std::uint8_t> testBytesRead{};
143 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
144 .WillOnce(Return(testBytesRead));
145 EXPECT_THROW(
146 try {
147 bufferImpl->readBufferHeader();
148 } catch (const std::runtime_error& e) {
149 EXPECT_STREQ(e.what(),
150 "Buffer header read only read '0', expected '48'");
151 throw;
152 },
153 std::runtime_error);
154}
155
156TEST_F(BufferTest, BufferHeaderReadPass)
157{
158 uint8_t* testInitializationHeaderPtr =
159 reinterpret_cast<uint8_t*>(&testInitializationHeader);
160 std::vector<uint8_t> testInitializationHeaderVector(
161 testInitializationHeaderPtr,
162 testInitializationHeaderPtr + bufferHeaderSize);
163
164 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
165 .WillOnce(Return(testInitializationHeaderVector));
166 EXPECT_NO_THROW(bufferImpl->readBufferHeader());
167 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
168}
169
Brandon Kimcf0b9752022-06-15 10:32:21 -0700170TEST_F(BufferTest, BufferUpdateReadPtrFail)
171{
172 // Return write size that is not 2 which is sizeof(little_uint16_t)
173 constexpr size_t wrongWriteSize = 1;
174 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
175 .WillOnce(Return(wrongWriteSize));
176 EXPECT_THROW(
177 try {
178 bufferImpl->updateReadPtr(0);
179 } catch (const std::runtime_error& e) {
180 EXPECT_STREQ(
181 e.what(),
Brandon Kim271d2312022-09-02 16:34:55 +0000182 "[updateReadPtr] Wrote '1' bytes, instead of expected '3'");
Brandon Kimcf0b9752022-06-15 10:32:21 -0700183 throw;
184 },
185 std::runtime_error);
186}
187
188TEST_F(BufferTest, BufferUpdateReadPtrPass)
189{
Brandon Kim271d2312022-09-02 16:34:55 +0000190 constexpr size_t expectedWriteSize = 3;
191 constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
192 // Check that we truncate the highest 24bits
Brandon Kimcf0b9752022-06-15 10:32:21 -0700193 const uint32_t testNewReadPtr = 0x99881234;
Brandon Kim271d2312022-09-02 16:34:55 +0000194 const std::vector<uint8_t> expectedReadPtr{0x34, 0x12, 0x88};
Brandon Kimcf0b9752022-06-15 10:32:21 -0700195
196 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
197 ElementsAreArray(expectedReadPtr)))
198 .WillOnce(Return(expectedWriteSize));
199 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
Brandon Kimc49284b2022-06-17 09:55:26 -0700200
201 auto cachedHeader = bufferImpl->getCachedBufferHeader();
Brandon Kim271d2312022-09-02 16:34:55 +0000202 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr),
203 0x881234);
Brandon Kimc49284b2022-06-17 09:55:26 -0700204}
205
206TEST_F(BufferTest, BufferUpdateBmcFlagsFail)
207{
208 // Return write size that is not 4 which is sizeof(little_uint32_t)
209 constexpr size_t wrongWriteSize = 1;
210 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
211 .WillOnce(Return(wrongWriteSize));
212 EXPECT_THROW(
213 try {
214 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready));
215 } catch (const std::runtime_error& e) {
216 EXPECT_STREQ(
217 e.what(),
218 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'");
219 throw;
220 },
221 std::runtime_error);
222}
223
224TEST_F(BufferTest, BufferUpdateBmcFlagsPass)
225{
226 constexpr size_t expectedWriteSize = 4;
Brandon Kim271d2312022-09-02 16:34:55 +0000227 constexpr uint8_t expectedBmcReadPtrOffset = 0x1d;
Brandon Kimc49284b2022-06-17 09:55:26 -0700228 const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00};
229
230 EXPECT_CALL(*dataInterfaceMockPtr,
231 write(expectedBmcReadPtrOffset,
232 ElementsAreArray(expectedNewBmcFlagsVector)))
233 .WillOnce(Return(expectedWriteSize));
234 EXPECT_NO_THROW(
235 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)));
236
237 auto cachedHeader = bufferImpl->getCachedBufferHeader();
238 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags),
239 static_cast<uint32_t>(BmcFlags::ready));
Brandon Kimcf0b9752022-06-15 10:32:21 -0700240}
241
Brandon Kim82ab8322023-08-17 00:50:18 +0000242TEST_F(BufferTest, GetMaxOffsetQueueSizeFail)
243{
244 InSequence s;
245 static constexpr size_t wrongQueueSize = testQueueSize - 1;
246 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
247 .WillOnce(Return(testRegionSize));
248 const std::vector<uint8_t> emptyArray(wrongQueueSize, 0);
249 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
250 .WillOnce(Return(wrongQueueSize));
251
252 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
253 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400254 EXPECT_NO_THROW(
255 bufferImpl->initialize(testBmcInterfaceVersion, wrongQueueSize,
256 testUeRegionSize, testMagicNumber));
Brandon Kim82ab8322023-08-17 00:50:18 +0000257 EXPECT_THROW(
258 try {
259 bufferImpl->getMaxOffset();
260 } catch (const std::runtime_error& e) {
261 EXPECT_STREQ(e.what(),
262 "[getMaxOffset] runtime queueSize '511' did not match "
263 "compile-time queueSize '512'. This indicates that the"
264 " buffer was corrupted");
265 throw;
266 },
267 std::runtime_error);
268}
269
270TEST_F(BufferTest, GetMaxOffsetUeRegionSizeFail)
271{
272 InSequence s;
273 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
274 .WillOnce(Return(testRegionSize));
275 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
276 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
277 .WillOnce(Return(testQueueSize));
278
279 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
280 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400281 EXPECT_NO_THROW(
282 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
283 testUeRegionSize + 1, testMagicNumber));
Brandon Kim82ab8322023-08-17 00:50:18 +0000284 EXPECT_THROW(
285 try {
286 bufferImpl->getMaxOffset();
287 } catch (const std::runtime_error& e) {
288 EXPECT_STREQ(
289 e.what(),
290 "[getMaxOffset] runtime ueRegionSize '81' did not match "
291 "compile-time ueRegionSize '80'. This indicates that the"
292 " buffer was corrupted");
293 throw;
294 },
295 std::runtime_error);
296}
297
298TEST_F(BufferTest, GetOffsetUeRegionSizeFail)
299{
300 InSequence s;
301 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
302 .WillOnce(Return(testRegionSize));
303 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
304 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray)))
305 .WillOnce(Return(testQueueSize));
306
307 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
308 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400309 EXPECT_NO_THROW(
310 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
311 testUeRegionSize - 1, testMagicNumber));
Brandon Kim82ab8322023-08-17 00:50:18 +0000312 EXPECT_THROW(
313 try {
314 bufferImpl->getQueueOffset();
315 } catch (const std::runtime_error& e) {
316 EXPECT_STREQ(
317 e.what(),
318 "[getQueueOffset] runtime ueRegionSize '79' did not match "
319 "compile-time ueRegionSize '80'. This indicates that the"
320 " buffer was corrupted");
321 throw;
322 },
323 std::runtime_error);
324}
325
Brandon Kimaec99862025-06-08 22:41:40 +0000326TEST_F(BufferTest, ReadUeLog_NoUeRegionConfigured)
327{
328 struct CircularBufferHeader header = testInitializationHeader;
329 header.ueRegionSize =
330 boost::endian::native_to_little<uint16_t>(0); // No UE region
331
332 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
333 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
334 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
335 .WillOnce(Return(headerBytes));
336
337 auto result = bufferImpl->readUeLogFromReservedRegion();
338 EXPECT_TRUE(result.empty());
339}
340
341TEST_F(BufferTest, ReadUeLog_NotPresentDueToFlags)
342{
343 struct CircularBufferHeader header = testInitializationHeader;
344 header.ueRegionSize = boost::endian::native_to_little<uint16_t>(0x20);
345 // Flags are the same, so no new UE log
346 header.biosFlags = boost::endian::native_to_little<uint32_t>(
347 static_cast<uint32_t>((BufferFlags::ueSwitch)));
348 header.bmcFlags = boost::endian::native_to_little<uint32_t>(
349 static_cast<uint32_t>(BufferFlags::ueSwitch));
350
351 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
352 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
353 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
354 .WillOnce(Return(headerBytes));
355
356 auto result = bufferImpl->readUeLogFromReservedRegion();
357 EXPECT_TRUE(result.empty());
358}
359
360TEST_F(BufferTest, ReadUeLog_PresentAndSuccessfullyRead)
361{
362 struct CircularBufferHeader header = testInitializationHeader;
363 uint16_t ueSize = 0x20;
364 header.ueRegionSize = boost::endian::native_to_little(ueSize);
365 header.biosFlags = boost::endian::native_to_little<uint32_t>(
366 static_cast<uint32_t>(BufferFlags::ueSwitch));
367 header.bmcFlags =
368 boost::endian::native_to_little<uint32_t>(0); // BIOS set, BMC not yet
369
370 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
371 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
372 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
373 .WillOnce(Return(headerBytes));
374
375 size_t ueRegionOffset = bufferHeaderSize;
376 std::vector<uint8_t> ueData(ueSize, 0xAA);
377 EXPECT_CALL(*dataInterfaceMockPtr, read(ueRegionOffset, ueSize))
378 .WillOnce(Return(ueData));
379
380 auto result = bufferImpl->readUeLogFromReservedRegion();
381 ASSERT_FALSE(result.empty());
382 EXPECT_THAT(result, ElementsAreArray(ueData));
383
384 // The initial bmcFlags (0) should remain unchanged in the cache
385 struct CircularBufferHeader cachedHeaderAfterRead =
386 bufferImpl->getCachedBufferHeader();
387 EXPECT_EQ(boost::endian::little_to_native(cachedHeaderAfterRead.bmcFlags),
388 0);
389}
390
391TEST_F(BufferTest, ReadUeLog_PresentButReadFails)
392{
393 struct CircularBufferHeader header = testInitializationHeader;
394 uint16_t ueSize = 0x20;
395 header.ueRegionSize = boost::endian::native_to_little(ueSize);
396 header.biosFlags = boost::endian::native_to_little<uint32_t>(
397 static_cast<uint32_t>(BufferFlags::ueSwitch));
398 header.bmcFlags = boost::endian::native_to_little<uint32_t>(0);
399
400 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
401 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
402 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
403 .WillOnce(Return(headerBytes));
404
405 size_t ueRegionOffset = bufferHeaderSize;
406 std::vector<uint8_t> shortUeData(ueSize - 1, 0xAA); // Short read
407 EXPECT_CALL(*dataInterfaceMockPtr, read(ueRegionOffset, ueSize))
408 .WillOnce(Return(shortUeData));
409
410 // Expect an exception due to short read, which is treated as corruption for
411 // UE log
412 EXPECT_THROW(
413 try {
414 bufferImpl->readUeLogFromReservedRegion();
415 } catch (const std::runtime_error& e) {
416 EXPECT_THAT(e.what(),
417 ::testing::HasSubstr("Failed to read full UE log"));
418 throw;
419 },
420 std::runtime_error);
421}
422
Brandon Kimd49db6f2025-06-09 00:03:59 +0000423TEST_F(BufferTest, CheckOverflow_NotPresentDueToFlags)
424{
425 struct CircularBufferHeader header = testInitializationHeader;
426 // Flags are the same, so no new overflow
427 header.biosFlags = boost::endian::native_to_little<uint32_t>(
428 static_cast<uint32_t>(BufferFlags::overflow));
429 header.bmcFlags = boost::endian::native_to_little<uint32_t>(
430 static_cast<uint32_t>(BufferFlags::overflow));
431
432 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
433 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
434 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
435 .WillOnce(Return(headerBytes));
436
437 bool overflowDetected = bufferImpl->checkForOverflowAndAcknowledge();
438 ASSERT_FALSE(overflowDetected);
439}
440
441TEST_F(BufferTest, CheckOverflow_PresentAndAcknowledged)
442{
443 struct CircularBufferHeader header = testInitializationHeader;
444 header.biosFlags = boost::endian::native_to_little<uint32_t>(
445 static_cast<uint32_t>(BufferFlags::overflow));
446 header.bmcFlags =
447 boost::endian::native_to_little<uint32_t>(0); // BIOS set, BMC not yet
448
449 uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
450 std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
451 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
452 .WillOnce(Return(headerBytes));
453
454 uint32_t expectedNewBmcFlags =
455 static_cast<uint32_t>(BufferFlags::overflow); // BMC toggles its bit
456 little_uint32_t littleExpectedNewBmcFlags =
457 boost::endian::native_to_little(expectedNewBmcFlags);
458 uint8_t* flagPtr = reinterpret_cast<uint8_t*>(&littleExpectedNewBmcFlags);
459 std::vector<uint8_t> expectedFlagWrite(flagPtr,
460 flagPtr + sizeof(little_uint32_t));
461 constexpr uint8_t bmcFlagsOffset =
462 offsetof(struct CircularBufferHeader, bmcFlags);
463
464 EXPECT_CALL(*dataInterfaceMockPtr,
465 write(bmcFlagsOffset, ElementsAreArray(expectedFlagWrite)))
466 .WillOnce(Return(sizeof(little_uint32_t)));
467
468 bool overflowDetected = bufferImpl->checkForOverflowAndAcknowledge();
469 ASSERT_TRUE(overflowDetected);
470
471 struct CircularBufferHeader updatedCachedHeader =
472 bufferImpl->getCachedBufferHeader();
473 EXPECT_EQ(boost::endian::little_to_native(updatedCachedHeader.bmcFlags),
474 expectedNewBmcFlags);
475}
476
Brandon Kim9836cfa2022-06-15 11:21:11 -0700477class BufferWraparoundReadTest : public BufferTest
478{
479 protected:
480 BufferWraparoundReadTest()
481 {
Brandon Kim4662b1b2022-06-16 21:38:02 -0700482 initializeFuncMock();
483 }
484 void initializeFuncMock()
485 {
Brandon Kim9836cfa2022-06-15 11:21:11 -0700486 // Initialize the memory and the cachedBufferHeader
487 InSequence s;
488 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize())
489 .WillOnce(Return(testRegionSize));
Brandon Kim3def3c82022-09-13 05:29:20 +0000490 const std::vector<uint8_t> emptyArray(testQueueSize, 0);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700491 EXPECT_CALL(*dataInterfaceMockPtr,
492 write(0, ElementsAreArray(emptyArray)))
Brandon Kim3def3c82022-09-13 05:29:20 +0000493 .WillOnce(Return(testQueueSize));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700494
Brandon Kim4662b1b2022-06-16 21:38:02 -0700495 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700496 .WillOnce(Return(bufferHeaderSize));
Patrick Williams1a643562024-08-16 15:22:05 -0400497 EXPECT_NO_THROW(
498 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize,
499 testUeRegionSize, testMagicNumber));
Brandon Kim9836cfa2022-06-15 11:21:11 -0700500 }
Brandon Kim271d2312022-09-02 16:34:55 +0000501 static constexpr size_t expectedWriteSize = 3;
502 static constexpr uint8_t expectedBmcReadPtrOffset = 0x21;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700503 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700504
Patrick Williams1a643562024-08-16 15:22:05 -0400505 static constexpr size_t testMaxOffset =
506 testQueueSize - testUeRegionSize - sizeof(struct CircularBufferHeader);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700507 uint8_t* testInitializationHeaderPtr =
508 reinterpret_cast<uint8_t*>(&testInitializationHeader);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700509};
510
Brandon Kim82ab8322023-08-17 00:50:18 +0000511TEST_F(BufferWraparoundReadTest, GetMaxOffsetPassTest)
Brandon Kim3def3c82022-09-13 05:29:20 +0000512{
513 EXPECT_EQ(bufferImpl->getMaxOffset(), testMaxOffset);
514}
515
Brandon Kim82ab8322023-08-17 00:50:18 +0000516TEST_F(BufferWraparoundReadTest, GetQueueOffsetPassTest)
517{
518 EXPECT_EQ(bufferImpl->getQueueOffset(),
519 bufferHeaderSize + testUeRegionSize);
520}
521
Brandon Kim35d43352022-06-16 11:13:36 -0700522TEST_F(BufferWraparoundReadTest, ParamsTooBigFail)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700523{
524 InSequence s;
Brandon Kim3def3c82022-09-13 05:29:20 +0000525 size_t tooBigOffset = testMaxOffset + 1;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700526 EXPECT_THROW(
527 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700528 bufferImpl->wraparoundRead(tooBigOffset, /* length */ 1);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700529 } catch (const std::runtime_error& e) {
Brandon Kim26660e92022-06-15 16:58:44 -0700530 EXPECT_STREQ(
531 e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +0000532 "[wraparoundRead] relativeOffset '385' was bigger than maxOffset '384'");
Brandon Kim35d43352022-06-16 11:13:36 -0700533 throw;
534 },
535 std::runtime_error);
536
Brandon Kim3def3c82022-09-13 05:29:20 +0000537 size_t tooBigLength = testMaxOffset + 1;
Brandon Kim35d43352022-06-16 11:13:36 -0700538 EXPECT_THROW(
539 try {
540 bufferImpl->wraparoundRead(/* relativeOffset */ 0, tooBigLength);
541 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +0000542 EXPECT_STREQ(e.what(), "[wraparoundRead] length '385' was bigger "
543 "than maxOffset '384'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700544 throw;
545 },
546 std::runtime_error);
547}
548
Brandon Kim35d43352022-06-16 11:13:36 -0700549TEST_F(BufferWraparoundReadTest, NoWrapAroundReadFails)
Brandon Kim9836cfa2022-06-15 11:21:11 -0700550{
551 InSequence s;
Brandon Kim35d43352022-06-16 11:13:36 -0700552 size_t testLength = 0x10;
553 size_t testOffset = 0x20;
554
555 // Fail the first read
556 std::vector<std::uint8_t> shortTestBytesRead(testLength - 1);
557 EXPECT_CALL(*dataInterfaceMockPtr,
558 read(testOffset + expectedqueueOffset, testLength))
559 .WillOnce(Return(shortTestBytesRead));
560
Brandon Kim9836cfa2022-06-15 11:21:11 -0700561 EXPECT_THROW(
562 try {
Brandon Kim35d43352022-06-16 11:13:36 -0700563 bufferImpl->wraparoundRead(testOffset, testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700564 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700565 EXPECT_STREQ(e.what(),
566 "[wraparoundRead] Read '15' which was not the "
567 "requested length of '16'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700568 throw;
569 },
570 std::runtime_error);
571}
572
573TEST_F(BufferWraparoundReadTest, NoWrapAroundReadPass)
574{
575 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700576 size_t testLength = 0x10;
Brandon Kim35d43352022-06-16 11:13:36 -0700577 size_t testOffset = 0x20;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700578
579 // Successfully read all the requested length without a wrap around
580 std::vector<std::uint8_t> testBytesRead(testLength);
Brandon Kim35d43352022-06-16 11:13:36 -0700581 EXPECT_CALL(*dataInterfaceMockPtr,
582 read(testOffset + expectedqueueOffset, testLength))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700583 .WillOnce(Return(testBytesRead));
584
585 // Call to updateReadPtr is triggered
586 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim271d2312022-09-02 16:34:55 +0000587 static_cast<uint8_t>(testOffset + testLength), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700588 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
589 ElementsAreArray(expectedReadPtr)))
590 .WillOnce(Return(expectedWriteSize));
591
592 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
593 ElementsAreArray(testBytesRead));
Brandon Kim35d43352022-06-16 11:13:36 -0700594 struct CircularBufferHeader cachedBufferHeader =
595 bufferImpl->getCachedBufferHeader();
596 // The bmcReadPtr should have been updated
597 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
598 testOffset + testLength);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700599}
600
601TEST_F(BufferWraparoundReadTest, WrapAroundReadFails)
602{
603 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700604 size_t testBytesLeft = 3;
605 size_t testLength = 0x10;
Brandon Kim3def3c82022-09-13 05:29:20 +0000606 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700607
Brandon Kim35d43352022-06-16 11:13:36 -0700608 // Read until the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700609 std::vector<std::uint8_t> testBytesReadShort(testLength - testBytesLeft);
Brandon Kim35d43352022-06-16 11:13:36 -0700610 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
611 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700612 .WillOnce(Return(testBytesReadShort));
613
614 // Read 1 byte short after wraparound
615 std::vector<std::uint8_t> testBytesLeftReadShort(testBytesLeft - 1);
616 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
617 .WillOnce(Return(testBytesLeftReadShort));
618
619 EXPECT_THROW(
620 try {
621 bufferImpl->wraparoundRead(testOffset, testLength);
622 } catch (const std::runtime_error& e) {
Brandon Kim35d43352022-06-16 11:13:36 -0700623 EXPECT_STREQ(
624 e.what(),
625 "[wraparoundRead] Buffer wrapped around but read '2' which was "
626 "not the requested lenght of '3'");
Brandon Kim9836cfa2022-06-15 11:21:11 -0700627 throw;
628 },
629 std::runtime_error);
630}
631
632TEST_F(BufferWraparoundReadTest, WrapAroundReadPasses)
633{
634 InSequence s;
Brandon Kim9836cfa2022-06-15 11:21:11 -0700635 size_t testBytesLeft = 3;
636 size_t testLength = 0x10;
Brandon Kim3def3c82022-09-13 05:29:20 +0000637 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700638
Brandon Kim35d43352022-06-16 11:13:36 -0700639 // Read to the end of the queue
Brandon Kim9836cfa2022-06-15 11:21:11 -0700640 std::vector<std::uint8_t> testBytesReadFirst{16, 15, 14, 13, 12, 11, 10,
641 9, 8, 7, 6, 5, 4};
Brandon Kim35d43352022-06-16 11:13:36 -0700642 EXPECT_CALL(*dataInterfaceMockPtr, read(testOffset + expectedqueueOffset,
643 testLength - testBytesLeft))
Brandon Kim9836cfa2022-06-15 11:21:11 -0700644 .WillOnce(Return(testBytesReadFirst));
645
646 std::vector<std::uint8_t> testBytesReadSecond{3, 2, 1};
647 EXPECT_CALL(*dataInterfaceMockPtr, read(expectedqueueOffset, testBytesLeft))
648 .WillOnce(Return(testBytesReadSecond));
649
650 // Call to updateReadPtr is triggered
651 const std::vector<uint8_t> expectedReadPtr{
Brandon Kim271d2312022-09-02 16:34:55 +0000652 static_cast<uint8_t>(testBytesLeft), 0x0, 0x0};
Brandon Kim9836cfa2022-06-15 11:21:11 -0700653 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
654 ElementsAreArray(expectedReadPtr)))
655 .WillOnce(Return(expectedWriteSize));
656
657 std::vector<std::uint8_t> expectedBytes = {16, 15, 14, 13, 12, 11, 10, 9,
658 8, 7, 6, 5, 4, 3, 2, 1};
659 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
660 ElementsAreArray(expectedBytes));
Brandon Kim35d43352022-06-16 11:13:36 -0700661 struct CircularBufferHeader cachedBufferHeader =
662 bufferImpl->getCachedBufferHeader();
663 // The bmcReadPtr should have been updated to reflect the wraparound
664 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
665 testBytesLeft);
Brandon Kim9836cfa2022-06-15 11:21:11 -0700666}
667
Brandon Kim4662b1b2022-06-16 21:38:02 -0700668TEST_F(BufferWraparoundReadTest, WrapAroundCornerCasePass)
669{
670 InSequence s;
671 size_t testBytesLeft = 0;
672 size_t testLength = 4;
Brandon Kim3def3c82022-09-13 05:29:20 +0000673 size_t testOffset = testMaxOffset - (testLength - testBytesLeft);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700674
675 // Read to the very end of the queue
676 std::vector<std::uint8_t> testBytes{4, 3, 2, 1};
677 EXPECT_CALL(*dataInterfaceMockPtr,
678 read(testOffset + expectedqueueOffset, testLength))
679 .WillOnce(Return(testBytes));
680
681 // Call to updateReadPtr is triggered, since we read to the very end of the
682 // buffer, update the readPtr up around to 0
Brandon Kim271d2312022-09-02 16:34:55 +0000683 const std::vector<uint8_t> expectedReadPtr{0x0, 0x0, 0x0};
Brandon Kim4662b1b2022-06-16 21:38:02 -0700684 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
685 ElementsAreArray(expectedReadPtr)))
686 .WillOnce(Return(expectedWriteSize));
687
688 EXPECT_THAT(bufferImpl->wraparoundRead(testOffset, testLength),
689 ElementsAreArray(testBytes));
690 struct CircularBufferHeader cachedBufferHeader =
691 bufferImpl->getCachedBufferHeader();
692 // The bmcReadPtr should have been updated to reflect the wraparound
693 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
694 0);
695}
696
Brandon Kim40ce08e2022-06-15 16:58:44 -0700697class BufferEntryTest : public BufferWraparoundReadTest
Brandon Kim7bac2d62022-06-07 21:37:51 -0700698{
699 protected:
Brandon Kim40ce08e2022-06-15 16:58:44 -0700700 BufferEntryTest()
Brandon Kim7bac2d62022-06-07 21:37:51 -0700701 {
702 testEntryHeader.sequenceId = testSequenceId;
703 testEntryHeader.entrySize = testEntrySize;
704 testEntryHeader.checksum = testChecksum;
705 testEntryHeader.rdeCommandType = testRdeCommandType;
706 }
Brandon Kim40ce08e2022-06-15 16:58:44 -0700707 ~BufferEntryTest() override = default;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700708
Brandon Kim35d43352022-06-16 11:13:36 -0700709 void wraparoundReadMock(const uint32_t relativeOffset,
710 std::span<std::uint8_t> expetedBytesOutput)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700711 {
Brandon Kim35d43352022-06-16 11:13:36 -0700712 InSequence s;
Brandon Kim3def3c82022-09-13 05:29:20 +0000713 const uint32_t queueSizeToQueueEnd = testMaxOffset - relativeOffset;
Brandon Kim35d43352022-06-16 11:13:36 -0700714
715 // This will wrap, split the read mocks in 2
716 if (expetedBytesOutput.size() > queueSizeToQueueEnd)
717 {
718 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
719 .WillOnce(Return(std::vector<std::uint8_t>(
720 expetedBytesOutput.begin(),
721 expetedBytesOutput.begin() + queueSizeToQueueEnd)));
722 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
723 .WillOnce(Return(std::vector<std::uint8_t>(
724 expetedBytesOutput.begin() + queueSizeToQueueEnd,
725 expetedBytesOutput.end())));
726 }
727 else
728 {
729 EXPECT_CALL(*dataInterfaceMockPtr, read(_, _))
730 .WillOnce(Return(std::vector<std::uint8_t>(
731 expetedBytesOutput.begin(), expetedBytesOutput.end())));
732 }
Brandon Kim7bac2d62022-06-07 21:37:51 -0700733
734 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
735 .WillOnce(Return(expectedWriteSize));
736 }
737
738 static constexpr size_t entryHeaderSize = sizeof(struct QueueEntryHeader);
739 static constexpr uint16_t testSequenceId = 0;
740 static constexpr uint16_t testEntrySize = 0x20;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700741 static constexpr uint8_t testRdeCommandType = 0x01;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700742 // Calculated checksum for the header
743 static constexpr uint8_t testChecksum =
744 (testSequenceId ^ testEntrySize ^ testRdeCommandType);
Brandon Kim613ba532022-09-12 20:55:21 +0000745 size_t testOffset = 0x0;
Brandon Kim7bac2d62022-06-07 21:37:51 -0700746
Patrick Williamsc20d1212024-12-18 11:22:36 -0500747 struct QueueEntryHeader testEntryHeader{};
Brandon Kim7bac2d62022-06-07 21:37:51 -0700748};
749
Brandon Kim40ce08e2022-06-15 16:58:44 -0700750TEST_F(BufferEntryTest, ReadEntryHeaderPass)
Brandon Kim7bac2d62022-06-07 21:37:51 -0700751{
752 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
753 std::vector<uint8_t> testEntryHeaderVector(
754 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700755 wraparoundReadMock(testOffset, testEntryHeaderVector);
Brandon Kim613ba532022-09-12 20:55:21 +0000756 EXPECT_EQ(bufferImpl->readEntryHeader(), testEntryHeader);
Brandon Kim35d43352022-06-16 11:13:36 -0700757 // Check the bmcReadPtr
758 struct CircularBufferHeader cachedBufferHeader =
759 bufferImpl->getCachedBufferHeader();
760 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
761 testOffset + testEntryHeaderVector.size());
Brandon Kim7bac2d62022-06-07 21:37:51 -0700762}
763
Brandon Kim40ce08e2022-06-15 16:58:44 -0700764TEST_F(BufferEntryTest, ReadEntryChecksumFail)
765{
766 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700767 std::vector<uint8_t> testEntryVector(testEntrySize);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700768 // Offset the checksum by 1
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700769 testEntryHeader.checksum += 1;
Brandon Kim40ce08e2022-06-15 16:58:44 -0700770 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
771 std::vector<uint8_t> testEntryHeaderVector(
772 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim35d43352022-06-16 11:13:36 -0700773 wraparoundReadMock(testOffset, testEntryHeaderVector);
774 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700775 EXPECT_THROW(
Brandon Kim613ba532022-09-12 20:55:21 +0000776 try { bufferImpl->readEntry(); } catch (const std::runtime_error& e) {
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700777 // Calculation: testChecksum (0x21) XOR (0x22) = 3
Brandon Kim40ce08e2022-06-15 16:58:44 -0700778 EXPECT_STREQ(e.what(),
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700779 "[readEntry] Checksum was '3', expected '0'");
Brandon Kim40ce08e2022-06-15 16:58:44 -0700780 throw;
781 },
782 std::runtime_error);
783}
784
Brandon Kim613ba532022-09-12 20:55:21 +0000785TEST_F(BufferEntryTest, ReadEntryPassWraparound)
Brandon Kim40ce08e2022-06-15 16:58:44 -0700786{
787 InSequence s;
Brandon Kimf0e4adc2022-06-16 23:14:25 -0700788 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ...
789 // (20 times) = 0 therefore leave the checksum as is
Brandon Kim35d43352022-06-16 11:13:36 -0700790 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700791 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
792 std::vector<uint8_t> testEntryHeaderVector(
793 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
Brandon Kim613ba532022-09-12 20:55:21 +0000794 // Set testOffset so that we can test the wraparound here at the header and
795 // update the readPtr
Brandon Kim3def3c82022-09-13 05:29:20 +0000796 testOffset = testMaxOffset - 1;
Brandon Kim613ba532022-09-12 20:55:21 +0000797 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
798 .WillOnce(Return(expectedWriteSize));
799 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
800
Brandon Kim35d43352022-06-16 11:13:36 -0700801 wraparoundReadMock(testOffset, testEntryHeaderVector);
802 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700803
804 EntryPair testedEntryPair;
Brandon Kim613ba532022-09-12 20:55:21 +0000805 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
Brandon Kim40ce08e2022-06-15 16:58:44 -0700806 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
807 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
Brandon Kim35d43352022-06-16 11:13:36 -0700808 struct CircularBufferHeader cachedBufferHeader =
809 bufferImpl->getCachedBufferHeader();
810 // The bmcReadPtr should have been updated to reflect the wraparound
811 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim613ba532022-09-12 20:55:21 +0000812 entryHeaderSize + testEntrySize - 1);
813
814 // Set testOffset so that we can test the wraparound here as well on our
815 // second read for the entry (by 1 byte)
Brandon Kim3def3c82022-09-13 05:29:20 +0000816 testOffset = testMaxOffset - entryHeaderSize - 1;
Brandon Kim613ba532022-09-12 20:55:21 +0000817 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, _))
818 .WillOnce(Return(expectedWriteSize));
819 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testOffset));
820
821 wraparoundReadMock(testOffset, testEntryHeaderVector);
822 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector);
823
824 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry());
825 EXPECT_EQ(testedEntryPair.first, testEntryHeader);
826 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector));
827 cachedBufferHeader = bufferImpl->getCachedBufferHeader();
828 // The bmcReadPtr should have been updated to reflect the wraparound
829 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr),
Brandon Kim35d43352022-06-16 11:13:36 -0700830 testEntrySize - 1);
Brandon Kim40ce08e2022-06-15 16:58:44 -0700831}
832
Brandon Kim4662b1b2022-06-16 21:38:02 -0700833class BufferReadErrorLogsTest : public BufferEntryTest
834{
835 protected:
836 BufferReadErrorLogsTest() = default;
837
838 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader);
839 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize;
840};
841
842TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail)
843{
844 InSequence s;
845 // Set the biosWritePtr too big
846 testInitializationHeader.biosWritePtr =
Brandon Kim3def3c82022-09-13 05:29:20 +0000847 boost::endian::native_to_little((testMaxOffset + 1));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700848 initializeFuncMock();
849
850 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400851 .WillOnce(Return(std::vector<uint8_t>(
852 testInitializationHeaderPtr,
853 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700854 EXPECT_THROW(
855 try {
856 bufferImpl->readErrorLogs();
857 } catch (const std::runtime_error& e) {
858 EXPECT_STREQ(e.what(),
Brandon Kim3def3c82022-09-13 05:29:20 +0000859 "[readErrorLogs] currentBiosWritePtr was '385' "
860 "which was bigger than maxOffset '384'");
Brandon Kim4662b1b2022-06-16 21:38:02 -0700861 throw;
862 },
863 std::runtime_error);
864
865 // Reset the biosWritePtr and set the bmcReadPtr too big
866 testInitializationHeader.biosWritePtr = 0;
867 initializeFuncMock();
868 testInitializationHeader.bmcReadPtr =
Brandon Kim3def3c82022-09-13 05:29:20 +0000869 boost::endian::native_to_little((testMaxOffset + 1));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700870 initializeFuncMock();
871
872 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400873 .WillOnce(Return(std::vector<uint8_t>(
874 testInitializationHeaderPtr,
875 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700876 EXPECT_THROW(
877 try {
878 bufferImpl->readErrorLogs();
879 } catch (const std::runtime_error& e) {
Brandon Kim3def3c82022-09-13 05:29:20 +0000880 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '385' "
881 "which was bigger than maxOffset '384'");
Brandon Kim4662b1b2022-06-16 21:38:02 -0700882 throw;
883 },
884 std::runtime_error);
885}
886
887TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass)
888{
889 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400890 .WillOnce(Return(std::vector<uint8_t>(
891 testInitializationHeaderPtr,
892 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700893 EXPECT_NO_THROW(bufferImpl->readErrorLogs());
894}
895
896TEST_F(BufferReadErrorLogsTest, NoWraparoundPass)
897{
898 InSequence s;
899 // Set the biosWritePtr to 1 entryHeader + entry size
900 testInitializationHeader.biosWritePtr =
901 boost::endian::native_to_little((entryAndHeaderSize));
902 initializeFuncMock();
903 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400904 .WillOnce(Return(std::vector<uint8_t>(
905 testInitializationHeaderPtr,
906 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700907 std::vector<uint8_t> testEntryHeaderVector(
908 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
909 std::vector<uint8_t> testEntryVector(testEntrySize);
910 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
911 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
912
913 std::vector<EntryPair> entryPairs;
914 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
915
916 // Check that we only read one entryPair and that the content is correct
Brandon Kim1a3dc602022-06-17 11:34:33 -0700917 EXPECT_EQ(entryPairs.size(), 1U);
Brandon Kim4662b1b2022-06-16 21:38:02 -0700918 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
919 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
920}
921
922TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass)
923{
924 InSequence s;
925 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly
Brandon Kim3def3c82022-09-13 05:29:20 +0000926 uint32_t entryAndHeaderSizeAwayFromEnd = testMaxOffset - entryAndHeaderSize;
Brandon Kim4662b1b2022-06-16 21:38:02 -0700927 testInitializationHeader.bmcReadPtr =
928 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd);
929 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning"
930 testInitializationHeader.biosWritePtr = entryAndHeaderSize;
931 initializeFuncMock();
932 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400933 .WillOnce(Return(std::vector<uint8_t>(
934 testInitializationHeaderPtr,
935 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700936
937 std::vector<uint8_t> testEntryHeaderVector(
938 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
939 std::vector<uint8_t> testEntryVector(testEntrySize);
940 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd,
941 testEntryHeaderVector);
942 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd +
943 entryHeaderSize,
944 testEntryVector);
945 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize,
946 testEntryHeaderVector);
947 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize +
948 entryHeaderSize,
949 testEntryVector);
950
951 std::vector<EntryPair> entryPairs;
952 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs());
953
954 // Check that we only read one entryPair and that the content is correct
955 EXPECT_EQ(entryPairs.size(), 2);
956 EXPECT_EQ(entryPairs[0].first, testEntryHeader);
957 EXPECT_EQ(entryPairs[1].first, testEntryHeader);
958 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector));
959 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector));
960}
961
962TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail)
963{
964 InSequence s;
965 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0);
966 // Make the biosWritePtr intentially 1 smaller than expected
967 testInitializationHeader.biosWritePtr =
968 boost::endian::native_to_little(entryAndHeaderSize - 1);
969 initializeFuncMock();
970 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
Patrick Williams1a643562024-08-16 15:22:05 -0400971 .WillOnce(Return(std::vector<uint8_t>(
972 testInitializationHeaderPtr,
973 testInitializationHeaderPtr + bufferHeaderSize)));
Brandon Kim4662b1b2022-06-16 21:38:02 -0700974
975 std::vector<uint8_t> testEntryHeaderVector(
976 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize);
977 std::vector<uint8_t> testEntryVector(testEntrySize);
978 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector);
979 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector);
980
981 EXPECT_THROW(
982 try {
983 bufferImpl->readErrorLogs();
984 } catch (const std::runtime_error& e) {
985 EXPECT_STREQ(
986 e.what(),
987 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' "
988 "are not identical after reading through all the logs");
989 throw;
990 },
991 std::runtime_error);
992}
993
Brandon Kimfcbc3db2022-06-06 21:26:18 -0700994} // namespace
995} // namespace bios_bmc_smm_error_logger