1 #include "buffer.hpp" 2 #include "data_interface_mock.hpp" 3 4 #include <boost/endian/arithmetic.hpp> 5 #include <boost/endian/conversion.hpp> 6 7 #include <algorithm> 8 #include <array> 9 #include <cstdint> 10 #include <memory> 11 12 #include <gmock/gmock.h> 13 #include <gtest/gtest.h> 14 15 namespace bios_bmc_smm_error_logger 16 { 17 namespace 18 { 19 20 using ::testing::_; 21 using ::testing::ElementsAreArray; 22 using ::testing::InSequence; 23 using ::testing::Return; 24 25 class BufferTest : public ::testing::Test 26 { 27 protected: 28 BufferTest() : 29 dataInterfaceMock(std::make_unique<DataInterfaceMock>()), 30 dataInterfaceMockPtr(dataInterfaceMock.get()) 31 { 32 bufferImpl = std::make_unique<BufferImpl>(std::move(dataInterfaceMock)); 33 testInitializationHeader.bmcInterfaceVersion = testBmcInterfaceVersion; 34 testInitializationHeader.queueSize = testQueueSize; 35 testInitializationHeader.ueRegionSize = testUeRegionSize; 36 std::transform(testMagicNumber.begin(), testMagicNumber.end(), 37 testInitializationHeader.magicNumber.begin(), 38 [](uint32_t number) -> little_uint32_t { 39 return boost::endian::native_to_little(number); 40 }); 41 } 42 ~BufferTest() override = default; 43 44 // CircularBufferHeader size is 0x30, ensure the test region is bigger 45 static constexpr size_t testRegionSize = 0x200; 46 static constexpr uint32_t testBmcInterfaceVersion = 123; 47 static constexpr uint16_t testQueueSize = 0x100; 48 static constexpr uint16_t testUeRegionSize = 0x50; 49 static constexpr std::array<uint32_t, 4> testMagicNumber = { 50 0x12345678, 0x22345678, 0x32345678, 0x42345678}; 51 static constexpr size_t bufferHeaderSize = 52 sizeof(struct CircularBufferHeader); 53 54 struct CircularBufferHeader testInitializationHeader 55 {}; 56 57 std::unique_ptr<DataInterfaceMock> dataInterfaceMock; 58 DataInterfaceMock* dataInterfaceMockPtr; 59 std::unique_ptr<BufferImpl> bufferImpl; 60 }; 61 62 TEST_F(BufferTest, BufferInitializeEraseFail) 63 { 64 InSequence s; 65 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize()) 66 .WillOnce(Return(testRegionSize)); 67 EXPECT_THROW( 68 try { 69 // Test too big of a proposed buffer compared to the memori size 70 uint16_t bigQueueSize = 0x181; 71 uint16_t bigUeRegionSize = 0x50; 72 bufferImpl->initialize(testBmcInterfaceVersion, bigQueueSize, 73 bigUeRegionSize, testMagicNumber); 74 } catch (const std::runtime_error& e) { 75 EXPECT_STREQ( 76 e.what(), 77 "[initialize] Proposed region size '513' " 78 "is bigger than the BMC's allocated MMIO region of '512'"); 79 throw; 80 }, 81 std::runtime_error); 82 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader); 83 84 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize()) 85 .WillOnce(Return(testRegionSize)); 86 size_t testProposedBufferSize = 87 sizeof(struct CircularBufferHeader) + testUeRegionSize + testQueueSize; 88 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0); 89 // Return a smaller write than the intended testRegionSize to test the error 90 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray))) 91 .WillOnce(Return(testProposedBufferSize - 1)); 92 EXPECT_THROW( 93 try { 94 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize, 95 testUeRegionSize, testMagicNumber); 96 } catch (const std::runtime_error& e) { 97 EXPECT_STREQ(e.what(), "[initialize] Only erased '383'"); 98 throw; 99 }, 100 std::runtime_error); 101 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader); 102 103 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize()) 104 .WillOnce(Return(testRegionSize)); 105 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray))) 106 .WillOnce(Return(testProposedBufferSize)); 107 // Return a smaller write than the intended initializationHeader to test the 108 // error 109 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)).WillOnce(Return(0)); 110 EXPECT_THROW( 111 try { 112 bufferImpl->initialize(testBmcInterfaceVersion, testQueueSize, 113 testUeRegionSize, testMagicNumber); 114 } catch (const std::runtime_error& e) { 115 EXPECT_STREQ(e.what(), 116 "[initialize] Only wrote '0' bytes of the header"); 117 throw; 118 }, 119 std::runtime_error); 120 EXPECT_NE(bufferImpl->getCachedBufferHeader(), testInitializationHeader); 121 } 122 123 TEST_F(BufferTest, BufferInitializePass) 124 { 125 InSequence s; 126 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize()) 127 .WillOnce(Return(testRegionSize)); 128 size_t testProposedBufferSize = 129 sizeof(struct CircularBufferHeader) + testUeRegionSize + testQueueSize; 130 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0); 131 EXPECT_CALL(*dataInterfaceMockPtr, write(0, ElementsAreArray(emptyArray))) 132 .WillOnce(Return(testProposedBufferSize)); 133 134 uint8_t* testInitializationHeaderPtr = 135 reinterpret_cast<uint8_t*>(&testInitializationHeader); 136 EXPECT_CALL(*dataInterfaceMockPtr, 137 write(0, ElementsAreArray(testInitializationHeaderPtr, 138 bufferHeaderSize))) 139 .WillOnce(Return(bufferHeaderSize)); 140 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion, 141 testQueueSize, testUeRegionSize, 142 testMagicNumber)); 143 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader); 144 } 145 146 TEST_F(BufferTest, BufferHeaderReadFail) 147 { 148 std::vector<std::uint8_t> testBytesRead{}; 149 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 150 .WillOnce(Return(testBytesRead)); 151 EXPECT_THROW( 152 try { 153 bufferImpl->readBufferHeader(); 154 } catch (const std::runtime_error& e) { 155 EXPECT_STREQ(e.what(), 156 "Buffer header read only read '0', expected '48'"); 157 throw; 158 }, 159 std::runtime_error); 160 } 161 162 TEST_F(BufferTest, BufferHeaderReadPass) 163 { 164 uint8_t* testInitializationHeaderPtr = 165 reinterpret_cast<uint8_t*>(&testInitializationHeader); 166 std::vector<uint8_t> testInitializationHeaderVector( 167 testInitializationHeaderPtr, 168 testInitializationHeaderPtr + bufferHeaderSize); 169 170 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 171 .WillOnce(Return(testInitializationHeaderVector)); 172 EXPECT_NO_THROW(bufferImpl->readBufferHeader()); 173 EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader); 174 } 175 176 TEST_F(BufferTest, BufferUpdateReadPtrFail) 177 { 178 // Return write size that is not 2 which is sizeof(little_uint16_t) 179 constexpr size_t wrongWriteSize = 1; 180 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _)) 181 .WillOnce(Return(wrongWriteSize)); 182 EXPECT_THROW( 183 try { 184 bufferImpl->updateReadPtr(0); 185 } catch (const std::runtime_error& e) { 186 EXPECT_STREQ( 187 e.what(), 188 "[updateReadPtr] Wrote '1' bytes, instead of expected '2'"); 189 throw; 190 }, 191 std::runtime_error); 192 } 193 194 TEST_F(BufferTest, BufferUpdateReadPtrPass) 195 { 196 constexpr size_t expectedWriteSize = 2; 197 constexpr uint8_t expectedBmcReadPtrOffset = 0x20; 198 // Check that we truncate the highest 16bits 199 const uint32_t testNewReadPtr = 0x99881234; 200 const std::vector<uint8_t> expectedReadPtr{0x34, 0x12}; 201 202 EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset, 203 ElementsAreArray(expectedReadPtr))) 204 .WillOnce(Return(expectedWriteSize)); 205 EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr)); 206 207 auto cachedHeader = bufferImpl->getCachedBufferHeader(); 208 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcReadPtr), 0x1234); 209 } 210 211 TEST_F(BufferTest, BufferUpdateBmcFlagsFail) 212 { 213 // Return write size that is not 4 which is sizeof(little_uint32_t) 214 constexpr size_t wrongWriteSize = 1; 215 EXPECT_CALL(*dataInterfaceMockPtr, write(_, _)) 216 .WillOnce(Return(wrongWriteSize)); 217 EXPECT_THROW( 218 try { 219 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready)); 220 } catch (const std::runtime_error& e) { 221 EXPECT_STREQ( 222 e.what(), 223 "[updateBmcFlags] Wrote '1' bytes, instead of expected '4'"); 224 throw; 225 }, 226 std::runtime_error); 227 } 228 229 TEST_F(BufferTest, BufferUpdateBmcFlagsPass) 230 { 231 constexpr size_t expectedWriteSize = 4; 232 constexpr uint8_t expectedBmcReadPtrOffset = 0x1c; 233 const std::vector<uint8_t> expectedNewBmcFlagsVector{0x04, 0x0, 0x0, 0x00}; 234 235 EXPECT_CALL(*dataInterfaceMockPtr, 236 write(expectedBmcReadPtrOffset, 237 ElementsAreArray(expectedNewBmcFlagsVector))) 238 .WillOnce(Return(expectedWriteSize)); 239 EXPECT_NO_THROW( 240 bufferImpl->updateBmcFlags(static_cast<uint32_t>(BmcFlags::ready))); 241 242 auto cachedHeader = bufferImpl->getCachedBufferHeader(); 243 EXPECT_EQ(boost::endian::little_to_native(cachedHeader.bmcFlags), 244 static_cast<uint32_t>(BmcFlags::ready)); 245 } 246 247 class BufferWraparoundReadTest : public BufferTest 248 { 249 protected: 250 BufferWraparoundReadTest() 251 { 252 initializeFuncMock(); 253 } 254 void initializeFuncMock() 255 { 256 // Initialize the memory and the cachedBufferHeader 257 InSequence s; 258 EXPECT_CALL(*dataInterfaceMockPtr, getMemoryRegionSize()) 259 .WillOnce(Return(testRegionSize)); 260 size_t testProposedBufferSize = sizeof(struct CircularBufferHeader) + 261 testUeRegionSize + testQueueSize; 262 const std::vector<uint8_t> emptyArray(testProposedBufferSize, 0); 263 EXPECT_CALL(*dataInterfaceMockPtr, 264 write(0, ElementsAreArray(emptyArray))) 265 .WillOnce(Return(testProposedBufferSize)); 266 267 EXPECT_CALL(*dataInterfaceMockPtr, write(0, _)) 268 .WillOnce(Return(bufferHeaderSize)); 269 EXPECT_NO_THROW(bufferImpl->initialize(testBmcInterfaceVersion, 270 testQueueSize, testUeRegionSize, 271 testMagicNumber)); 272 } 273 static constexpr size_t expectedWriteSize = 2; 274 static constexpr uint8_t expectedBmcReadPtrOffset = 0x20; 275 static constexpr size_t expectedqueueOffset = 0x30 + testUeRegionSize; 276 277 uint8_t* testInitializationHeaderPtr = 278 reinterpret_cast<uint8_t*>(&testInitializationHeader); 279 }; 280 281 TEST_F(BufferWraparoundReadTest, ParamsTooBigFail) 282 { 283 InSequence s; 284 285 size_t tooBigOffset = testQueueSize + 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 '257' was bigger than queueSize '256'"); 293 throw; 294 }, 295 std::runtime_error); 296 297 size_t tooBigLength = testQueueSize + 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 '257' was bigger " 303 "than queueSize '256'"); 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}; 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 = testQueueSize - (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 = testQueueSize - (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}; 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 = testQueueSize - (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}; 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 = testQueueSize - 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 = 0x20; 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(testOffset), 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 { 538 bufferImpl->readEntry(testOffset); 539 } catch (const std::runtime_error& e) { 540 // Calculation: testChecksum (0x21) XOR (0x22) = 3 541 EXPECT_STREQ(e.what(), 542 "[readEntry] Checksum was '3', expected '0'"); 543 throw; 544 }, 545 std::runtime_error); 546 } 547 548 TEST_F(BufferEntryTest, ReadEntryPass) 549 { 550 InSequence s; 551 // We expect this will bump checksum up by "testEntrySize" = 0xff ^ 0xff ... 552 // (20 times) = 0 therefore leave the checksum as is 553 std::vector<uint8_t> testEntryVector(testEntrySize, 0xff); 554 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader); 555 std::vector<uint8_t> testEntryHeaderVector( 556 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize); 557 // Set testOffset so that we can test the wraparound here as well on our 558 // second read for the entry (by 1 byte) 559 testOffset = testQueueSize - entryHeaderSize - 1; 560 wraparoundReadMock(testOffset, testEntryHeaderVector); 561 wraparoundReadMock(testOffset + entryHeaderSize, testEntryVector); 562 563 EntryPair testedEntryPair; 564 EXPECT_NO_THROW(testedEntryPair = bufferImpl->readEntry(testOffset)); 565 EXPECT_EQ(testedEntryPair.first, testEntryHeader); 566 EXPECT_THAT(testedEntryPair.second, ElementsAreArray(testEntryVector)); 567 struct CircularBufferHeader cachedBufferHeader = 568 bufferImpl->getCachedBufferHeader(); 569 // The bmcReadPtr should have been updated to reflect the wraparound 570 EXPECT_EQ(boost::endian::little_to_native(cachedBufferHeader.bmcReadPtr), 571 testEntrySize - 1); 572 } 573 574 class BufferReadErrorLogsTest : public BufferEntryTest 575 { 576 protected: 577 BufferReadErrorLogsTest() = default; 578 579 uint8_t* testEntryHeaderPtr = reinterpret_cast<uint8_t*>(&testEntryHeader); 580 size_t entryAndHeaderSize = entryHeaderSize + testEntrySize; 581 }; 582 583 TEST_F(BufferReadErrorLogsTest, PtrsTooBigFail) 584 { 585 InSequence s; 586 // Set the biosWritePtr too big 587 testInitializationHeader.biosWritePtr = 588 boost::endian::native_to_little((testQueueSize + 1)); 589 initializeFuncMock(); 590 591 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 592 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr, 593 testInitializationHeaderPtr + 594 bufferHeaderSize))); 595 EXPECT_THROW( 596 try { 597 bufferImpl->readErrorLogs(); 598 } catch (const std::runtime_error& e) { 599 EXPECT_STREQ(e.what(), 600 "[readErrorLogs] currentBiosWritePtr was '257' " 601 "which was bigger than queueSize '256'"); 602 throw; 603 }, 604 std::runtime_error); 605 606 // Reset the biosWritePtr and set the bmcReadPtr too big 607 testInitializationHeader.biosWritePtr = 0; 608 initializeFuncMock(); 609 testInitializationHeader.bmcReadPtr = 610 boost::endian::native_to_little((testQueueSize + 1)); 611 initializeFuncMock(); 612 613 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 614 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr, 615 testInitializationHeaderPtr + 616 bufferHeaderSize))); 617 EXPECT_THROW( 618 try { 619 bufferImpl->readErrorLogs(); 620 } catch (const std::runtime_error& e) { 621 EXPECT_STREQ(e.what(), "[readErrorLogs] currentReadPtr was '257' " 622 "which was bigger than queueSize '256'"); 623 throw; 624 }, 625 std::runtime_error); 626 } 627 628 TEST_F(BufferReadErrorLogsTest, IdenticalPtrsPass) 629 { 630 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 631 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr, 632 testInitializationHeaderPtr + 633 bufferHeaderSize))); 634 EXPECT_NO_THROW(bufferImpl->readErrorLogs()); 635 } 636 637 TEST_F(BufferReadErrorLogsTest, NoWraparoundPass) 638 { 639 InSequence s; 640 // Set the biosWritePtr to 1 entryHeader + entry size 641 testInitializationHeader.biosWritePtr = 642 boost::endian::native_to_little((entryAndHeaderSize)); 643 initializeFuncMock(); 644 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 645 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr, 646 testInitializationHeaderPtr + 647 bufferHeaderSize))); 648 std::vector<uint8_t> testEntryHeaderVector( 649 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize); 650 std::vector<uint8_t> testEntryVector(testEntrySize); 651 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector); 652 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector); 653 654 std::vector<EntryPair> entryPairs; 655 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs()); 656 657 // Check that we only read one entryPair and that the content is correct 658 EXPECT_EQ(entryPairs.size(), 1U); 659 EXPECT_EQ(entryPairs[0].first, testEntryHeader); 660 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector)); 661 } 662 663 TEST_F(BufferReadErrorLogsTest, WraparoundMultiplEntryPass) 664 { 665 InSequence s; 666 // Set the bmcReadPtr to 1 entryHeader + entry size from the "end" exactly 667 uint32_t entryAndHeaderSizeAwayFromEnd = testQueueSize - entryAndHeaderSize; 668 testInitializationHeader.bmcReadPtr = 669 boost::endian::native_to_little(entryAndHeaderSizeAwayFromEnd); 670 // Set the biosWritePtr to 1 entryHeader + entry size from the "beginning" 671 testInitializationHeader.biosWritePtr = entryAndHeaderSize; 672 initializeFuncMock(); 673 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 674 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr, 675 testInitializationHeaderPtr + 676 bufferHeaderSize))); 677 678 std::vector<uint8_t> testEntryHeaderVector( 679 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize); 680 std::vector<uint8_t> testEntryVector(testEntrySize); 681 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd, 682 testEntryHeaderVector); 683 wraparoundReadMock(/*relativeOffset=*/entryAndHeaderSizeAwayFromEnd + 684 entryHeaderSize, 685 testEntryVector); 686 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize, 687 testEntryHeaderVector); 688 wraparoundReadMock(/*relativeOffset=*/0 + entryAndHeaderSize + 689 entryHeaderSize, 690 testEntryVector); 691 692 std::vector<EntryPair> entryPairs; 693 EXPECT_NO_THROW(entryPairs = bufferImpl->readErrorLogs()); 694 695 // Check that we only read one entryPair and that the content is correct 696 EXPECT_EQ(entryPairs.size(), 2); 697 EXPECT_EQ(entryPairs[0].first, testEntryHeader); 698 EXPECT_EQ(entryPairs[1].first, testEntryHeader); 699 EXPECT_THAT(entryPairs[0].second, ElementsAreArray(testEntryVector)); 700 EXPECT_THAT(entryPairs[1].second, ElementsAreArray(testEntryVector)); 701 } 702 703 TEST_F(BufferReadErrorLogsTest, WraparoundMismatchingPtrsFail) 704 { 705 InSequence s; 706 testInitializationHeader.bmcReadPtr = boost::endian::native_to_little(0); 707 // Make the biosWritePtr intentially 1 smaller than expected 708 testInitializationHeader.biosWritePtr = 709 boost::endian::native_to_little(entryAndHeaderSize - 1); 710 initializeFuncMock(); 711 EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize)) 712 .WillOnce(Return(std::vector<uint8_t>(testInitializationHeaderPtr, 713 testInitializationHeaderPtr + 714 bufferHeaderSize))); 715 716 std::vector<uint8_t> testEntryHeaderVector( 717 testEntryHeaderPtr, testEntryHeaderPtr + entryHeaderSize); 718 std::vector<uint8_t> testEntryVector(testEntrySize); 719 wraparoundReadMock(/*relativeOffset=*/0, testEntryHeaderVector); 720 wraparoundReadMock(/*relativeOffset=*/0 + entryHeaderSize, testEntryVector); 721 722 EXPECT_THROW( 723 try { 724 bufferImpl->readErrorLogs(); 725 } catch (const std::runtime_error& e) { 726 EXPECT_STREQ( 727 e.what(), 728 "[readErrorLogs] biosWritePtr '37' and bmcReaddPtr '38' " 729 "are not identical after reading through all the logs"); 730 throw; 731 }, 732 std::runtime_error); 733 } 734 735 } // namespace 736 } // namespace bios_bmc_smm_error_logger 737