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