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