1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "extensions/openpower-pels/src.hpp" 17 #include "mocks.hpp" 18 #include "pel_utils.hpp" 19 20 #include <fstream> 21 22 #include <gtest/gtest.h> 23 24 using namespace openpower::pels; 25 using ::testing::_; 26 using ::testing::DoAll; 27 using ::testing::InvokeWithoutArgs; 28 using ::testing::NiceMock; 29 using ::testing::Return; 30 using ::testing::SetArgReferee; 31 using ::testing::Throw; 32 namespace fs = std::filesystem; 33 34 const auto testRegistry = R"( 35 { 36 "PELs": 37 [ 38 { 39 "Name": "xyz.openbmc_project.Error.Test", 40 "Subsystem": "bmc_firmware", 41 "SRC": 42 { 43 "ReasonCode": "0xABCD", 44 "Words6To9": 45 { 46 "6": 47 { 48 "Description": "Component ID", 49 "AdditionalDataPropSource": "COMPID" 50 }, 51 "7": 52 { 53 "Description": "Failure count", 54 "AdditionalDataPropSource": "FREQUENCY" 55 }, 56 "8": 57 { 58 "Description": "Time period", 59 "AdditionalDataPropSource": "DURATION" 60 }, 61 "9": 62 { 63 "Description": "Error code", 64 "AdditionalDataPropSource": "ERRORCODE" 65 } 66 } 67 }, 68 "Documentation": 69 { 70 "Description": "A Component Fault", 71 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4", 72 "MessageArgSources": 73 [ 74 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9" 75 ] 76 } 77 } 78 ] 79 } 80 )"; 81 82 class SRCTest : public ::testing::Test 83 { 84 protected: 85 static void SetUpTestCase() 86 { 87 char path[] = "/tmp/srctestXXXXXX"; 88 regDir = mkdtemp(path); 89 } 90 91 static void TearDownTestCase() 92 { 93 fs::remove_all(regDir); 94 } 95 96 static std::string writeData(const char* data) 97 { 98 fs::path path = regDir / "registry.json"; 99 std::ofstream stream{path}; 100 stream << data; 101 return path; 102 } 103 104 static fs::path regDir; 105 }; 106 107 fs::path SRCTest::regDir{}; 108 109 TEST_F(SRCTest, UnflattenFlattenTestNoCallouts) 110 { 111 auto data = pelDataFactory(TestPELType::primarySRCSection); 112 113 Stream stream{data}; 114 SRC src{stream}; 115 116 EXPECT_TRUE(src.valid()); 117 118 EXPECT_EQ(src.header().id, 0x5053); 119 EXPECT_EQ(src.header().size, 0x50); 120 EXPECT_EQ(src.header().version, 0x01); 121 EXPECT_EQ(src.header().subType, 0x01); 122 EXPECT_EQ(src.header().componentID, 0x0202); 123 124 EXPECT_EQ(src.version(), 0x02); 125 EXPECT_EQ(src.flags(), 0x00); 126 EXPECT_EQ(src.hexWordCount(), 9); 127 EXPECT_EQ(src.size(), 0x48); 128 129 const auto& hexwords = src.hexwordData(); 130 EXPECT_EQ(0x02020255, hexwords[0]); 131 EXPECT_EQ(0x03030310, hexwords[1]); 132 EXPECT_EQ(0x04040404, hexwords[2]); 133 EXPECT_EQ(0x05050505, hexwords[3]); 134 EXPECT_EQ(0x06060606, hexwords[4]); 135 EXPECT_EQ(0x07070707, hexwords[5]); 136 EXPECT_EQ(0x08080808, hexwords[6]); 137 EXPECT_EQ(0x09090909, hexwords[7]); 138 139 EXPECT_EQ(src.asciiString(), "BD8D5678 "); 140 EXPECT_FALSE(src.callouts()); 141 142 // Flatten 143 std::vector<uint8_t> newData; 144 Stream newStream{newData}; 145 146 src.flatten(newStream); 147 EXPECT_EQ(data, newData); 148 } 149 150 TEST_F(SRCTest, UnflattenFlattenTest2Callouts) 151 { 152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts); 153 154 Stream stream{data}; 155 SRC src{stream}; 156 157 EXPECT_TRUE(src.valid()); 158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC. 159 160 // Spot check the SRC fields, but they're the same as above 161 EXPECT_EQ(src.asciiString(), "BD8D5678 "); 162 163 // There should be 2 callouts 164 const auto& calloutsSection = src.callouts(); 165 ASSERT_TRUE(calloutsSection); 166 const auto& callouts = calloutsSection->callouts(); 167 EXPECT_EQ(callouts.size(), 2); 168 169 // spot check that each callout has the right substructures 170 EXPECT_TRUE(callouts.front()->fruIdentity()); 171 EXPECT_FALSE(callouts.front()->pceIdentity()); 172 EXPECT_FALSE(callouts.front()->mru()); 173 174 EXPECT_TRUE(callouts.back()->fruIdentity()); 175 EXPECT_TRUE(callouts.back()->pceIdentity()); 176 EXPECT_TRUE(callouts.back()->mru()); 177 178 // Flatten 179 std::vector<uint8_t> newData; 180 Stream newStream{newData}; 181 182 src.flatten(newStream); 183 EXPECT_EQ(data, newData); 184 } 185 186 // Create an SRC from the message registry 187 TEST_F(SRCTest, CreateTestNoCallouts) 188 { 189 message::Entry entry; 190 entry.src.type = 0xBD; 191 entry.src.reasonCode = 0xABCD; 192 entry.subsystem = 0x42; 193 entry.src.hexwordADFields = { 194 {5, {"TEST1", "DESCR1"}}, // Not a user defined word 195 {6, {"TEST1", "DESCR1"}}, 196 {7, {"TEST2", "DESCR2"}}, 197 {8, {"TEST3", "DESCR3"}}, 198 {9, {"TEST4", "DESCR4"}}}; 199 200 // Values for the SRC words pointed to above 201 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678", 202 "TEST3=0XDEF", "TEST4=Z"}; 203 AdditionalData ad{adData}; 204 NiceMock<MockDataInterface> dataIface; 205 206 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD")); 207 208 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 209 "system/entry"}; 210 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 211 .WillOnce(Return(std::vector<bool>{false, false, false})); 212 213 SRC src{entry, ad, dataIface}; 214 215 EXPECT_TRUE(src.valid()); 216 EXPECT_FALSE(src.isPowerFaultEvent()); 217 EXPECT_EQ(src.size(), baseSRCSize); 218 219 const auto& hexwords = src.hexwordData(); 220 221 // The spec always refers to SRC words 2 - 9, and as the hexwordData() 222 // array index starts at 0 use the math in the [] below to make it easier 223 // to tell what is being accessed. 224 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status 225 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type 226 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format 227 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position 228 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN 229 230 // Validate more fields here as the code starts filling them in. 231 232 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents 233 // And that none of the error status flags are set 234 EXPECT_EQ(hexwords[5 - 2], 0); 235 236 // The user defined hex word fields specifed in the additional data. 237 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1 238 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2 239 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3 240 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z' 241 242 EXPECT_EQ(src.asciiString(), "BD42ABCD "); 243 244 // No callouts 245 EXPECT_FALSE(src.callouts()); 246 247 // May as well spot check the flatten/unflatten 248 std::vector<uint8_t> data; 249 Stream stream{data}; 250 src.flatten(stream); 251 252 stream.offset(0); 253 SRC newSRC{stream}; 254 255 EXPECT_TRUE(newSRC.valid()); 256 EXPECT_EQ(newSRC.asciiString(), src.asciiString()); 257 EXPECT_FALSE(newSRC.callouts()); 258 } 259 260 // Test when the CCIN string isn't a 4 character number 261 TEST_F(SRCTest, BadCCINTest) 262 { 263 message::Entry entry; 264 entry.src.type = 0xBD; 265 entry.src.reasonCode = 0xABCD; 266 entry.subsystem = 0x42; 267 268 std::vector<std::string> adData{}; 269 AdditionalData ad{adData}; 270 NiceMock<MockDataInterface> dataIface; 271 272 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 273 "system/entry"}; 274 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 275 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 276 277 // First it isn't a number, then it is too long, 278 // then it is empty. 279 EXPECT_CALL(dataIface, getMotherboardCCIN) 280 .WillOnce(Return("X")) 281 .WillOnce(Return("12345")) 282 .WillOnce(Return("")); 283 284 // The CCIN in the first half should still be 0 each time. 285 { 286 SRC src{entry, ad, dataIface}; 287 EXPECT_TRUE(src.valid()); 288 const auto& hexwords = src.hexwordData(); 289 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 290 } 291 292 { 293 SRC src{entry, ad, dataIface}; 294 EXPECT_TRUE(src.valid()); 295 const auto& hexwords = src.hexwordData(); 296 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 297 } 298 299 { 300 SRC src{entry, ad, dataIface}; 301 EXPECT_TRUE(src.valid()); 302 const auto& hexwords = src.hexwordData(); 303 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 304 } 305 } 306 307 // Test the getErrorDetails function 308 TEST_F(SRCTest, MessageSubstitutionTest) 309 { 310 auto path = SRCTest::writeData(testRegistry); 311 message::Registry registry{path}; 312 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode); 313 314 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4", 315 "DURATION=30", "ERRORCODE=0x01ABCDEF"}; 316 AdditionalData ad{adData}; 317 NiceMock<MockDataInterface> dataIface; 318 319 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 320 "system/entry"}; 321 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 322 .WillOnce(Return(std::vector<bool>{false, false, false})); 323 324 SRC src{*entry, ad, dataIface}; 325 EXPECT_TRUE(src.valid()); 326 327 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message); 328 ASSERT_TRUE(errorDetails); 329 EXPECT_EQ(errorDetails.value(), 330 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs " 331 "with ErrorCode 0x01ABCDEF"); 332 } 333 // Test that an inventory path callout string is 334 // converted into the appropriate FRU callout. 335 TEST_F(SRCTest, InventoryCalloutTest) 336 { 337 message::Entry entry; 338 entry.src.type = 0xBD; 339 entry.src.reasonCode = 0xABCD; 340 entry.subsystem = 0x42; 341 342 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 343 AdditionalData ad{adData}; 344 NiceMock<MockDataInterface> dataIface; 345 346 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 347 .WillOnce(Return("UTMS-P1")); 348 349 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 350 .Times(1) 351 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 352 SetArgReferee<3>("123456789ABC"))); 353 354 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 355 "system/entry"}; 356 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 357 .WillOnce(Return(std::vector<bool>{false, false, false})); 358 359 SRC src{entry, ad, dataIface}; 360 EXPECT_TRUE(src.valid()); 361 362 ASSERT_TRUE(src.callouts()); 363 364 EXPECT_EQ(src.callouts()->callouts().size(), 1); 365 366 auto& callout = src.callouts()->callouts().front(); 367 368 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 369 EXPECT_EQ(callout->priority(), 'H'); 370 371 auto& fru = callout->fruIdentity(); 372 373 EXPECT_EQ(fru->getPN().value(), "1234567"); 374 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 375 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 376 377 // flatten and unflatten 378 std::vector<uint8_t> data; 379 Stream stream{data}; 380 src.flatten(stream); 381 382 stream.offset(0); 383 SRC newSRC{stream}; 384 EXPECT_TRUE(newSRC.valid()); 385 ASSERT_TRUE(src.callouts()); 386 EXPECT_EQ(src.callouts()->callouts().size(), 1); 387 } 388 389 // Test that when the location code can't be obtained that 390 // no callout is added. 391 TEST_F(SRCTest, InventoryCalloutNoLocCodeTest) 392 { 393 message::Entry entry; 394 entry.src.type = 0xBD; 395 entry.src.reasonCode = 0xABCD; 396 entry.subsystem = 0x42; 397 398 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 399 AdditionalData ad{adData}; 400 NiceMock<MockDataInterface> dataIface; 401 402 auto func = []() { 403 throw sdbusplus::exception::SdBusError(5, "Error"); 404 return std::string{}; 405 }; 406 407 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 408 .Times(1) 409 .WillOnce(InvokeWithoutArgs(func)); 410 411 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 412 "system/entry"}; 413 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 414 .WillOnce(Return(std::vector<bool>{false, false, false})); 415 416 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0); 417 418 SRC src{entry, ad, dataIface}; 419 EXPECT_TRUE(src.valid()); 420 421 ASSERT_FALSE(src.callouts()); 422 423 // flatten and unflatten 424 std::vector<uint8_t> data; 425 Stream stream{data}; 426 src.flatten(stream); 427 428 stream.offset(0); 429 SRC newSRC{stream}; 430 EXPECT_TRUE(newSRC.valid()); 431 ASSERT_FALSE(src.callouts()); 432 } 433 434 // Test that when the VPD can't be obtained that 435 // a callout is still created. 436 TEST_F(SRCTest, InventoryCalloutNoVPDTest) 437 { 438 message::Entry entry; 439 entry.src.type = 0xBD; 440 entry.src.reasonCode = 0xABCD; 441 entry.subsystem = 0x42; 442 443 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 444 AdditionalData ad{adData}; 445 NiceMock<MockDataInterface> dataIface; 446 447 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 448 .Times(1) 449 .WillOnce(Return("UTMS-P10")); 450 451 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); }; 452 453 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 454 .Times(1) 455 .WillOnce(InvokeWithoutArgs(func)); 456 457 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 458 "system/entry"}; 459 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 460 .WillOnce(Return(std::vector<bool>{false, false, false})); 461 462 SRC src{entry, ad, dataIface}; 463 EXPECT_TRUE(src.valid()); 464 ASSERT_TRUE(src.callouts()); 465 EXPECT_EQ(src.callouts()->callouts().size(), 1); 466 467 auto& callout = src.callouts()->callouts().front(); 468 EXPECT_EQ(callout->locationCode(), "UTMS-P10"); 469 EXPECT_EQ(callout->priority(), 'H'); 470 471 auto& fru = callout->fruIdentity(); 472 473 EXPECT_EQ(fru->getPN(), ""); 474 EXPECT_EQ(fru->getCCIN(), ""); 475 EXPECT_EQ(fru->getSN(), ""); 476 EXPECT_FALSE(fru->getMaintProc()); 477 478 // flatten and unflatten 479 std::vector<uint8_t> data; 480 Stream stream{data}; 481 src.flatten(stream); 482 483 stream.offset(0); 484 SRC newSRC{stream}; 485 EXPECT_TRUE(newSRC.valid()); 486 ASSERT_TRUE(src.callouts()); 487 EXPECT_EQ(src.callouts()->callouts().size(), 1); 488 } 489 490 TEST_F(SRCTest, RegistryCalloutTest) 491 { 492 message::Entry entry; 493 entry.src.type = 0xBD; 494 entry.src.reasonCode = 0xABCD; 495 entry.src.deconfigFlag = true; 496 entry.subsystem = 0x42; 497 498 entry.callouts = R"( 499 [ 500 { 501 "System": "systemA", 502 "CalloutList": 503 [ 504 { 505 "Priority": "high", 506 "SymbolicFRU": "service_docs" 507 }, 508 { 509 "Priority": "medium", 510 "Procedure": "bmc_code" 511 } 512 ] 513 }, 514 { 515 "System": "systemB", 516 "CalloutList": 517 [ 518 { 519 "Priority": "high", 520 "LocCode": "P0-C8", 521 "SymbolicFRUTrusted": "service_docs" 522 }, 523 { 524 "Priority": "medium", 525 "SymbolicFRUTrusted": "service_docs" 526 } 527 ] 528 }, 529 { 530 "System": "systemC", 531 "CalloutList": 532 [ 533 { 534 "Priority": "high", 535 "LocCode": "P0-C8" 536 }, 537 { 538 "Priority": "medium", 539 "LocCode": "P0-C9" 540 } 541 ] 542 } 543 ])"_json; 544 545 { 546 // Call out a symbolic FRU and a procedure 547 AdditionalData ad; 548 NiceMock<MockDataInterface> dataIface; 549 std::vector<std::string> names{"systemA"}; 550 551 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 552 553 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 554 "system/entry"}; 555 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 556 .WillOnce(Return(std::vector<bool>{false, false, false})); 557 558 SRC src{entry, ad, dataIface}; 559 560 const auto& hexwords = src.hexwordData(); 561 auto mask = static_cast<uint32_t>(SRC::ErrorStatusFlags::deconfigured); 562 EXPECT_EQ(hexwords[5 - 2] & mask, mask); 563 564 auto& callouts = src.callouts()->callouts(); 565 ASSERT_EQ(callouts.size(), 2); 566 567 EXPECT_EQ(callouts[0]->locationCodeSize(), 0); 568 EXPECT_EQ(callouts[0]->priority(), 'H'); 569 570 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 571 EXPECT_EQ(callouts[1]->priority(), 'M'); 572 573 auto& fru1 = callouts[0]->fruIdentity(); 574 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 575 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 576 EXPECT_FALSE(fru1->getMaintProc()); 577 EXPECT_FALSE(fru1->getSN()); 578 EXPECT_FALSE(fru1->getCCIN()); 579 580 auto& fru2 = callouts[1]->fruIdentity(); 581 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001"); 582 EXPECT_EQ(fru2->failingComponentType(), 583 src::FRUIdentity::maintenanceProc); 584 EXPECT_FALSE(fru2->getPN()); 585 EXPECT_FALSE(fru2->getSN()); 586 EXPECT_FALSE(fru2->getCCIN()); 587 } 588 589 { 590 // Call out a trusted symbolic FRU with a location code, and 591 // another one without. 592 AdditionalData ad; 593 NiceMock<MockDataInterface> dataIface; 594 std::vector<std::string> names{"systemB"}; 595 596 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8")); 597 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 598 599 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 600 "system/entry"}; 601 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 602 .WillOnce(Return(std::vector<bool>{false, false, false})); 603 604 SRC src{entry, ad, dataIface}; 605 606 auto& callouts = src.callouts()->callouts(); 607 EXPECT_EQ(callouts.size(), 2); 608 609 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8"); 610 EXPECT_EQ(callouts[0]->priority(), 'H'); 611 612 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 613 EXPECT_EQ(callouts[1]->priority(), 'M'); 614 615 auto& fru1 = callouts[0]->fruIdentity(); 616 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 617 EXPECT_EQ(fru1->failingComponentType(), 618 src::FRUIdentity::symbolicFRUTrustedLocCode); 619 EXPECT_FALSE(fru1->getMaintProc()); 620 EXPECT_FALSE(fru1->getSN()); 621 EXPECT_FALSE(fru1->getCCIN()); 622 623 // It asked for a trusted symbolic FRU, but no location code 624 // was provided so it is switched back to a normal one 625 auto& fru2 = callouts[1]->fruIdentity(); 626 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS"); 627 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU); 628 EXPECT_FALSE(fru2->getMaintProc()); 629 EXPECT_FALSE(fru2->getSN()); 630 EXPECT_FALSE(fru2->getCCIN()); 631 } 632 633 { 634 // Two hardware callouts 635 AdditionalData ad; 636 NiceMock<MockDataInterface> dataIface; 637 std::vector<std::string> names{"systemC"}; 638 639 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 640 "system/entry"}; 641 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 642 .WillOnce(Return(std::vector<bool>{false, false, false})); 643 644 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 645 646 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 647 .WillOnce(Return("UXXX-P0-C8")); 648 649 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0)) 650 .WillOnce(Return("UXXX-P0-C9")); 651 652 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false)) 653 .WillOnce(Return(std::vector<std::string>{ 654 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"})); 655 656 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false)) 657 .WillOnce(Return(std::vector<std::string>{ 658 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"})); 659 660 EXPECT_CALL( 661 dataIface, 662 getHWCalloutFields( 663 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, 664 _)) 665 .Times(1) 666 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 667 SetArgReferee<2>("CCCC"), 668 SetArgReferee<3>("123456789ABC"))); 669 670 EXPECT_CALL( 671 dataIface, 672 getHWCalloutFields( 673 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _, 674 _)) 675 .Times(1) 676 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 677 SetArgReferee<2>("DDDD"), 678 SetArgReferee<3>("23456789ABCD"))); 679 680 SRC src{entry, ad, dataIface}; 681 682 auto& callouts = src.callouts()->callouts(); 683 EXPECT_EQ(callouts.size(), 2); 684 685 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8"); 686 EXPECT_EQ(callouts[0]->priority(), 'H'); 687 688 auto& fru1 = callouts[0]->fruIdentity(); 689 EXPECT_EQ(fru1->getPN().value(), "1234567"); 690 EXPECT_EQ(fru1->getCCIN().value(), "CCCC"); 691 EXPECT_EQ(fru1->getSN().value(), "123456789ABC"); 692 693 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9"); 694 EXPECT_EQ(callouts[1]->priority(), 'M'); 695 696 auto& fru2 = callouts[1]->fruIdentity(); 697 EXPECT_EQ(fru2->getPN().value(), "2345678"); 698 EXPECT_EQ(fru2->getCCIN().value(), "DDDD"); 699 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD"); 700 } 701 } 702 703 // Test that a symbolic FRU with a trusted location code callout 704 // from the registry can get its location from the 705 // CALLOUT_INVENTORY_PATH AdditionalData entry. 706 TEST_F(SRCTest, SymbolicFRUWithInvPathTest) 707 { 708 message::Entry entry; 709 entry.src.type = 0xBD; 710 entry.src.reasonCode = 0xABCD; 711 entry.subsystem = 0x42; 712 713 entry.callouts = R"( 714 [{ 715 "CalloutList": 716 [ 717 { 718 "Priority": "high", 719 "SymbolicFRUTrusted": "service_docs", 720 "UseInventoryLocCode": true 721 }, 722 { 723 "Priority": "medium", 724 "LocCode": "P0-C8", 725 "SymbolicFRUTrusted": "pwrsply" 726 } 727 ] 728 }])"_json; 729 730 { 731 // The location code for the first symbolic FRU callout will 732 // come from this inventory path since UseInventoryLocCode is set. 733 // In this case there will be no normal FRU callout for the motherboard. 734 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 735 AdditionalData ad{adData}; 736 NiceMock<MockDataInterface> dataIface; 737 std::vector<std::string> names{"systemA"}; 738 739 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 740 "system/entry"}; 741 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 742 .WillOnce(Return(std::vector<bool>{false, false, false})); 743 744 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 745 746 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 747 .Times(1) 748 .WillOnce(Return("Ufcs-P10")); 749 750 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 751 .WillOnce(Return("Ufcs-P0-C8")); 752 753 SRC src{entry, ad, dataIface}; 754 755 auto& callouts = src.callouts()->callouts(); 756 EXPECT_EQ(callouts.size(), 2); 757 758 // The location code for the first symbolic FRU callout with a 759 // trusted location code comes from the motherboard. 760 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10"); 761 EXPECT_EQ(callouts[0]->priority(), 'H'); 762 auto& fru1 = callouts[0]->fruIdentity(); 763 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 764 EXPECT_EQ(fru1->failingComponentType(), 765 src::FRUIdentity::symbolicFRUTrustedLocCode); 766 767 // The second trusted symbolic FRU callouts uses the location 768 // code in the registry as usual. 769 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8"); 770 EXPECT_EQ(callouts[1]->priority(), 'M'); 771 auto& fru2 = callouts[1]->fruIdentity(); 772 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY"); 773 EXPECT_EQ(fru2->failingComponentType(), 774 src::FRUIdentity::symbolicFRUTrustedLocCode); 775 } 776 777 { 778 // This time say we want to use the location code from 779 // the inventory, but don't pass it in and the callout should 780 // end up a regular symbolic FRU 781 entry.callouts = R"( 782 [{ 783 "CalloutList": 784 [ 785 { 786 "Priority": "high", 787 "SymbolicFRUTrusted": "service_docs", 788 "UseInventoryLocCode": true 789 } 790 ] 791 }])"_json; 792 793 AdditionalData ad; 794 NiceMock<MockDataInterface> dataIface; 795 std::vector<std::string> names{"systemA"}; 796 797 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 798 "system/entry"}; 799 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 800 .WillOnce(Return(std::vector<bool>{false, false, false})); 801 802 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 803 804 SRC src{entry, ad, dataIface}; 805 806 auto& callouts = src.callouts()->callouts(); 807 EXPECT_EQ(callouts.size(), 1); 808 809 EXPECT_EQ(callouts[0]->locationCode(), ""); 810 EXPECT_EQ(callouts[0]->priority(), 'H'); 811 auto& fru1 = callouts[0]->fruIdentity(); 812 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 813 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 814 } 815 } 816 817 // Test looking up device path fails in the callout jSON. 818 TEST_F(SRCTest, DevicePathCalloutTest) 819 { 820 message::Entry entry; 821 entry.src.type = 0xBD; 822 entry.src.reasonCode = 0xABCD; 823 entry.subsystem = 0x42; 824 825 const auto calloutJSON = R"( 826 { 827 "I2C": 828 { 829 "14": 830 { 831 "114": 832 { 833 "Callouts":[ 834 { 835 "Name": "/chassis/motherboard/cpu0", 836 "LocationCode": "P1-C40", 837 "Priority": "H" 838 }, 839 { 840 "Name": "/chassis/motherboard", 841 "LocationCode": "P1", 842 "Priority": "M" 843 }, 844 { 845 "Name": "/chassis/motherboard/bmc", 846 "LocationCode": "P1-C15", 847 "Priority": "L" 848 } 849 ], 850 "Dest": "proc 0 target" 851 } 852 } 853 } 854 })"; 855 856 auto dataPath = getPELReadOnlyDataPath(); 857 std::ofstream file{dataPath / "systemA_dev_callouts.json"}; 858 file << calloutJSON; 859 file.close(); 860 861 NiceMock<MockDataInterface> dataIface; 862 std::vector<std::string> names{"systemA"}; 863 864 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 865 "system/entry"}; 866 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 867 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 868 869 EXPECT_CALL(dataIface, getSystemNames) 870 .Times(5) 871 .WillRepeatedly(Return(names)); 872 873 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false)) 874 .Times(3) 875 .WillRepeatedly(Return(std::vector<std::string>{ 876 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"})); 877 878 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)) 879 .Times(3) 880 .WillRepeatedly(Return(std::vector<std::string>{ 881 "/xyz/openbmc_project/inventory/chassis/motherboard"})); 882 883 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false)) 884 .Times(3) 885 .WillRepeatedly(Return(std::vector<std::string>{ 886 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"})); 887 888 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0)) 889 .Times(3) 890 .WillRepeatedly(Return("Ufcs-P1-C40")); 891 892 EXPECT_CALL(dataIface, expandLocationCode("P1", 0)) 893 .Times(3) 894 .WillRepeatedly(Return("Ufcs-P1")); 895 896 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0)) 897 .Times(3) 898 .WillRepeatedly(Return("Ufcs-P1-C15")); 899 900 EXPECT_CALL( 901 dataIface, 902 getHWCalloutFields( 903 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _)) 904 .Times(3) 905 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"), 906 SetArgReferee<2>("CCCC"), 907 SetArgReferee<3>("123456789ABC"))); 908 EXPECT_CALL( 909 dataIface, 910 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard", 911 _, _, _)) 912 .Times(3) 913 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"), 914 SetArgReferee<2>("MMMM"), 915 SetArgReferee<3>("CBA987654321"))); 916 EXPECT_CALL( 917 dataIface, 918 getHWCalloutFields( 919 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _)) 920 .Times(3) 921 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"), 922 SetArgReferee<2>("BBBB"), 923 SetArgReferee<3>("C123456789AB"))); 924 925 // Call this below with different AdditionalData values that 926 // result in the same callouts. 927 auto checkCallouts = [&entry, &dataIface](const auto& items) { 928 AdditionalData ad{items}; 929 SRC src{entry, ad, dataIface}; 930 931 ASSERT_TRUE(src.callouts()); 932 auto& callouts = src.callouts()->callouts(); 933 934 ASSERT_EQ(callouts.size(), 3); 935 936 { 937 EXPECT_EQ(callouts[0]->priority(), 'H'); 938 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40"); 939 940 auto& fru = callouts[0]->fruIdentity(); 941 EXPECT_EQ(fru->getPN().value(), "1234567"); 942 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 943 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 944 } 945 { 946 EXPECT_EQ(callouts[1]->priority(), 'M'); 947 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1"); 948 949 auto& fru = callouts[1]->fruIdentity(); 950 EXPECT_EQ(fru->getPN().value(), "7654321"); 951 EXPECT_EQ(fru->getCCIN().value(), "MMMM"); 952 EXPECT_EQ(fru->getSN().value(), "CBA987654321"); 953 } 954 { 955 EXPECT_EQ(callouts[2]->priority(), 'L'); 956 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15"); 957 958 auto& fru = callouts[2]->fruIdentity(); 959 EXPECT_EQ(fru->getPN().value(), "7123456"); 960 EXPECT_EQ(fru->getCCIN().value(), "BBBB"); 961 EXPECT_EQ(fru->getSN().value(), "C123456789AB"); 962 } 963 }; 964 965 { 966 // Callouts based on the device path 967 std::vector<std::string> items{ 968 "CALLOUT_ERRNO=5", 969 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 970 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}; 971 972 checkCallouts(items); 973 } 974 975 { 976 // Callouts based on the I2C bus and address 977 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 978 "CALLOUT_IIC_ADDR=0x72"}; 979 checkCallouts(items); 980 } 981 982 { 983 // Also based on I2C bus and address, but with bus = /dev/i2c-14 984 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 985 "CALLOUT_IIC_ADDR=0x72"}; 986 checkCallouts(items); 987 } 988 989 { 990 // Callout not found 991 std::vector<std::string> items{ 992 "CALLOUT_ERRNO=5", 993 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 994 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}; 995 996 AdditionalData ad{items}; 997 SRC src{entry, ad, dataIface}; 998 999 EXPECT_FALSE(src.callouts()); 1000 ASSERT_EQ(src.getDebugData().size(), 1); 1001 EXPECT_EQ(src.getDebugData()[0], 1002 "Problem looking up I2C callouts on 24 18: " 1003 "[json.exception.out_of_range.403] key '24' not found"); 1004 } 1005 1006 { 1007 // Callout not found 1008 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22", 1009 "CALLOUT_IIC_ADDR=0x99"}; 1010 AdditionalData ad{items}; 1011 SRC src{entry, ad, dataIface}; 1012 1013 EXPECT_FALSE(src.callouts()); 1014 ASSERT_EQ(src.getDebugData().size(), 1); 1015 EXPECT_EQ(src.getDebugData()[0], 1016 "Problem looking up I2C callouts on 22 153: " 1017 "[json.exception.out_of_range.403] key '22' not found"); 1018 } 1019 1020 fs::remove_all(dataPath); 1021 } 1022 1023 // Test when callouts are passed in via JSON 1024 TEST_F(SRCTest, JsonCalloutsTest) 1025 { 1026 const auto jsonCallouts = R"( 1027 [ 1028 { 1029 "LocationCode": "P0-C1", 1030 "Priority": "H", 1031 "MRUs": [ 1032 { 1033 "ID": 42, 1034 "Priority": "H" 1035 }, 1036 { 1037 "ID": 43, 1038 "Priority": "M" 1039 } 1040 ] 1041 }, 1042 { 1043 "InventoryPath": "/inv/system/chassis/motherboard/cpu0", 1044 "Priority": "M", 1045 "Guarded": true, 1046 "Deconfigured": true 1047 }, 1048 { 1049 "Procedure": "PROCEDU", 1050 "Priority": "A" 1051 }, 1052 { 1053 "SymbolicFRU": "TRUSTED", 1054 "Priority": "B", 1055 "TrustedLocationCode": true, 1056 "LocationCode": "P1-C23" 1057 }, 1058 { 1059 "SymbolicFRU": "FRUTST1", 1060 "Priority": "C", 1061 "LocationCode": "P1-C24" 1062 }, 1063 { 1064 "SymbolicFRU": "FRUTST2LONG", 1065 "Priority": "L" 1066 }, 1067 { 1068 "Procedure": "fsi_path", 1069 "Priority": "L" 1070 }, 1071 { 1072 "SymbolicFRU": "ambient_temp", 1073 "Priority": "L" 1074 } 1075 ] 1076 )"_json; 1077 1078 message::Entry entry; 1079 entry.src.type = 0xBD; 1080 entry.src.reasonCode = 0xABCD; 1081 entry.subsystem = 0x42; 1082 1083 AdditionalData ad; 1084 NiceMock<MockDataInterface> dataIface; 1085 1086 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1087 "system/entry"}; 1088 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1089 .WillOnce(Return(std::vector<bool>{false, false, false})); 1090 1091 // Callout 0 mock calls 1092 { 1093 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1094 .Times(1) 1095 .WillOnce(Return("UXXX-P0-C1")); 1096 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1097 .Times(1) 1098 .WillOnce(Return(std::vector<std::string>{ 1099 "/inv/system/chassis/motherboard/bmc"})); 1100 EXPECT_CALL( 1101 dataIface, 1102 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1103 .Times(1) 1104 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1105 SetArgReferee<2>("CCCC"), 1106 SetArgReferee<3>("123456789ABC"))); 1107 } 1108 // Callout 1 mock calls 1109 { 1110 EXPECT_CALL(dataIface, 1111 getLocationCode("/inv/system/chassis/motherboard/cpu0")) 1112 .WillOnce(Return("UYYY-P5")); 1113 EXPECT_CALL( 1114 dataIface, 1115 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _)) 1116 .Times(1) 1117 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 1118 SetArgReferee<2>("DDDD"), 1119 SetArgReferee<3>("23456789ABCD"))); 1120 } 1121 // Callout 3 mock calls 1122 { 1123 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0)) 1124 .Times(1) 1125 .WillOnce(Return("UXXX-P1-C23")); 1126 } 1127 // Callout 4 mock calls 1128 { 1129 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0)) 1130 .Times(1) 1131 .WillOnce(Return("UXXX-P1-C24")); 1132 } 1133 1134 SRC src{entry, ad, jsonCallouts, dataIface}; 1135 ASSERT_TRUE(src.callouts()); 1136 1137 // Check the guarded and deconfigured flags 1138 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000); 1139 1140 const auto& callouts = src.callouts()->callouts(); 1141 ASSERT_EQ(callouts.size(), 8); 1142 1143 // Check callout 0 1144 { 1145 EXPECT_EQ(callouts[0]->priority(), 'H'); 1146 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); 1147 1148 auto& fru = callouts[0]->fruIdentity(); 1149 EXPECT_EQ(fru->getPN().value(), "1234567"); 1150 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1151 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1152 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1153 1154 auto& mruCallouts = callouts[0]->mru(); 1155 ASSERT_TRUE(mruCallouts); 1156 auto& mrus = mruCallouts->mrus(); 1157 ASSERT_EQ(mrus.size(), 2); 1158 EXPECT_EQ(mrus[0].id, 42); 1159 EXPECT_EQ(mrus[0].priority, 'H'); 1160 EXPECT_EQ(mrus[1].id, 43); 1161 EXPECT_EQ(mrus[1].priority, 'M'); 1162 } 1163 1164 // Check callout 1 1165 { 1166 EXPECT_EQ(callouts[1]->priority(), 'M'); 1167 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5"); 1168 1169 auto& fru = callouts[1]->fruIdentity(); 1170 EXPECT_EQ(fru->getPN().value(), "2345678"); 1171 EXPECT_EQ(fru->getCCIN().value(), "DDDD"); 1172 EXPECT_EQ(fru->getSN().value(), "23456789ABCD"); 1173 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1174 } 1175 1176 // Check callout 2 1177 { 1178 EXPECT_EQ(callouts[2]->priority(), 'A'); 1179 EXPECT_EQ(callouts[2]->locationCode(), ""); 1180 1181 auto& fru = callouts[2]->fruIdentity(); 1182 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); 1183 EXPECT_EQ(fru->failingComponentType(), 1184 src::FRUIdentity::maintenanceProc); 1185 } 1186 1187 // Check callout 3 1188 { 1189 EXPECT_EQ(callouts[3]->priority(), 'B'); 1190 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23"); 1191 1192 auto& fru = callouts[3]->fruIdentity(); 1193 EXPECT_EQ(fru->getPN().value(), "TRUSTED"); 1194 EXPECT_EQ(fru->failingComponentType(), 1195 src::FRUIdentity::symbolicFRUTrustedLocCode); 1196 } 1197 1198 // Check callout 4 1199 { 1200 EXPECT_EQ(callouts[4]->priority(), 'C'); 1201 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24"); 1202 1203 auto& fru = callouts[4]->fruIdentity(); 1204 EXPECT_EQ(fru->getPN().value(), "FRUTST1"); 1205 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1206 } 1207 1208 // Check callout 5 1209 { 1210 EXPECT_EQ(callouts[5]->priority(), 'L'); 1211 EXPECT_EQ(callouts[5]->locationCode(), ""); 1212 1213 auto& fru = callouts[5]->fruIdentity(); 1214 EXPECT_EQ(fru->getPN().value(), "FRUTST2"); 1215 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1216 } 1217 1218 // Check callout 6 1219 { 1220 EXPECT_EQ(callouts[6]->priority(), 'L'); 1221 EXPECT_EQ(callouts[6]->locationCode(), ""); 1222 1223 auto& fru = callouts[6]->fruIdentity(); 1224 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004"); 1225 EXPECT_EQ(fru->failingComponentType(), 1226 src::FRUIdentity::maintenanceProc); 1227 } 1228 1229 // Check callout 7 1230 { 1231 EXPECT_EQ(callouts[7]->priority(), 'L'); 1232 EXPECT_EQ(callouts[7]->locationCode(), ""); 1233 1234 auto& fru = callouts[7]->fruIdentity(); 1235 EXPECT_EQ(fru->getPN().value(), "AMBTEMP"); 1236 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1237 } 1238 1239 // Check that it didn't find any errors 1240 const auto& data = src.getDebugData(); 1241 EXPECT_TRUE(data.empty()); 1242 } 1243 1244 TEST_F(SRCTest, JsonBadCalloutsTest) 1245 { 1246 // The first call will have a Throw in a mock call. 1247 // The second will have a different Throw in a mock call. 1248 // The others have issues with the Priority field. 1249 const auto jsonCallouts = R"( 1250 [ 1251 { 1252 "LocationCode": "P0-C1", 1253 "Priority": "H" 1254 }, 1255 { 1256 "LocationCode": "P0-C2", 1257 "Priority": "H" 1258 }, 1259 { 1260 "LocationCode": "P0-C3" 1261 }, 1262 { 1263 "LocationCode": "P0-C4", 1264 "Priority": "X" 1265 } 1266 ] 1267 )"_json; 1268 1269 message::Entry entry; 1270 entry.src.type = 0xBD; 1271 entry.src.reasonCode = 0xABCD; 1272 entry.subsystem = 0x42; 1273 1274 AdditionalData ad; 1275 NiceMock<MockDataInterface> dataIface; 1276 1277 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1278 "system/entry"}; 1279 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1280 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 1281 1282 // Callout 0 mock calls 1283 // Expand location code will fail, so the unexpanded location 1284 // code should show up in the callout instead. 1285 { 1286 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1287 .WillOnce(Throw(std::runtime_error("Fail"))); 1288 1289 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1290 .Times(1) 1291 .WillOnce(Return(std::vector<std::string>{ 1292 "/inv/system/chassis/motherboard/bmc"})); 1293 EXPECT_CALL( 1294 dataIface, 1295 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1296 .Times(1) 1297 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1298 SetArgReferee<2>("CCCC"), 1299 SetArgReferee<3>("123456789ABC"))); 1300 } 1301 1302 // Callout 1 mock calls 1303 // getInventoryFromLocCode will fail 1304 { 1305 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0)) 1306 .Times(1) 1307 .WillOnce(Return("UXXX-P0-C2")); 1308 1309 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false)) 1310 .Times(1) 1311 .WillOnce(Throw(std::runtime_error("Fail"))); 1312 } 1313 1314 SRC src{entry, ad, jsonCallouts, dataIface}; 1315 1316 ASSERT_TRUE(src.callouts()); 1317 1318 const auto& callouts = src.callouts()->callouts(); 1319 1320 // Only the first callout was successful 1321 ASSERT_EQ(callouts.size(), 1); 1322 1323 { 1324 EXPECT_EQ(callouts[0]->priority(), 'H'); 1325 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1"); 1326 1327 auto& fru = callouts[0]->fruIdentity(); 1328 EXPECT_EQ(fru->getPN().value(), "1234567"); 1329 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1330 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1331 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1332 } 1333 1334 const auto& data = src.getDebugData(); 1335 ASSERT_EQ(data.size(), 4); 1336 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail"); 1337 EXPECT_STREQ(data[1].c_str(), 1338 "Failed extracting callout data from JSON: Unable to " 1339 "get inventory path from location code: P0-C2: Fail"); 1340 EXPECT_STREQ(data[2].c_str(), 1341 "Failed extracting callout data from JSON: " 1342 "[json.exception.out_of_range.403] key 'Priority' not found"); 1343 EXPECT_STREQ(data[3].c_str(), 1344 "Failed extracting callout data from JSON: Invalid " 1345 "priority 'X' found in JSON callout"); 1346 } 1347 1348 // Test that an inventory path callout can have 1349 // a different priority than H. 1350 TEST_F(SRCTest, InventoryCalloutTestPriority) 1351 { 1352 message::Entry entry; 1353 entry.src.type = 0xBD; 1354 entry.src.reasonCode = 0xABCD; 1355 entry.subsystem = 0x42; 1356 1357 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard", 1358 "CALLOUT_PRIORITY=M"}; 1359 AdditionalData ad{adData}; 1360 NiceMock<MockDataInterface> dataIface; 1361 1362 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1363 "system/entry"}; 1364 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1365 .WillOnce(Return(std::vector<bool>{false, false, false})); 1366 1367 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 1368 .WillOnce(Return("UTMS-P1")); 1369 1370 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 1371 .Times(1) 1372 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 1373 SetArgReferee<3>("123456789ABC"))); 1374 1375 SRC src{entry, ad, dataIface}; 1376 EXPECT_TRUE(src.valid()); 1377 1378 ASSERT_TRUE(src.callouts()); 1379 1380 EXPECT_EQ(src.callouts()->callouts().size(), 1); 1381 1382 auto& callout = src.callouts()->callouts().front(); 1383 1384 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 1385 EXPECT_EQ(callout->priority(), 'M'); 1386 } 1387 1388 // Test for bmc & platform dump status bits 1389 TEST_F(SRCTest, DumpStatusBitsCheck) 1390 { 1391 message::Entry entry; 1392 entry.src.type = 0xBD; 1393 entry.src.reasonCode = 0xABCD; 1394 entry.subsystem = 0x42; 1395 1396 AdditionalData ad; 1397 NiceMock<MockDataInterface> dataIface; 1398 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1399 "system/entry"}; 1400 1401 { 1402 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1403 .WillOnce(Return(std::vector<bool>{true, false, false})); 1404 1405 SRC src{entry, ad, dataIface}; 1406 EXPECT_TRUE(src.valid()); 1407 1408 const auto& hexwords = src.hexwordData(); 1409 EXPECT_EQ(0x00080055, hexwords[0]); 1410 } 1411 1412 { 1413 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1414 .WillOnce(Return(std::vector<bool>{false, true, false})); 1415 1416 SRC src{entry, ad, dataIface}; 1417 EXPECT_TRUE(src.valid()); 1418 1419 const auto& hexwords = src.hexwordData(); 1420 EXPECT_EQ(0x00000255, hexwords[0]); 1421 } 1422 1423 { 1424 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1425 .WillOnce(Return(std::vector<bool>{false, false, true})); 1426 1427 SRC src{entry, ad, dataIface}; 1428 EXPECT_TRUE(src.valid()); 1429 1430 const auto& hexwords = src.hexwordData(); 1431 EXPECT_EQ(0x00000455, hexwords[0]); 1432 } 1433 1434 { 1435 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1436 .WillOnce(Return(std::vector<bool>{true, true, true})); 1437 1438 SRC src{entry, ad, dataIface}; 1439 EXPECT_TRUE(src.valid()); 1440 1441 const auto& hexwords = src.hexwordData(); 1442 EXPECT_EQ(0x00080655, hexwords[0]); 1443 } 1444 } 1445 1446 // Test SRC with additional data - PEL_SUBSYSTEM 1447 TEST_F(SRCTest, TestPELSubsystem) 1448 { 1449 message::Entry entry; 1450 entry.src.type = 0xBD; 1451 entry.src.reasonCode = 0xABCD; 1452 entry.subsystem = 0x42; 1453 1454 // Values for the SRC words pointed to above 1455 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"}; 1456 AdditionalData ad{adData}; 1457 NiceMock<MockDataInterface> dataIface; 1458 1459 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD")); 1460 1461 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1462 "system/entry"}; 1463 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1464 .WillOnce(Return(std::vector<bool>{false, false, false})); 1465 1466 SRC src{entry, ad, dataIface}; 1467 1468 EXPECT_TRUE(src.valid()); 1469 1470 EXPECT_EQ(src.asciiString(), "BD20ABCD "); 1471 } 1472 1473 void setAsciiString(std::vector<uint8_t>& src, const std::string& value) 1474 { 1475 assert(40 + value.size() <= src.size()); 1476 1477 for (size_t i = 0; i < value.size(); i++) 1478 { 1479 src[40 + i] = value[i]; 1480 } 1481 } 1482 1483 TEST_F(SRCTest, TestGetProgressCode) 1484 { 1485 { 1486 // A real SRC with CC009184 1487 std::vector<uint8_t> src{ 1488 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0, 1489 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1490 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57, 1491 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1492 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 1493 1494 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184); 1495 } 1496 1497 { 1498 // A real SRC with STANDBY 1499 std::vector<uint8_t> src{ 1500 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 1501 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1502 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68, 1503 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1504 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 1505 1506 EXPECT_EQ(SRC::getProgressCode(src), 0); 1507 } 1508 1509 { 1510 // A real SRC with CC009184, but 1 byte too short 1511 std::vector<uint8_t> src{ 1512 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0, 1513 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1514 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57, 1515 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1516 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 1517 src.resize(71); 1518 EXPECT_EQ(SRC::getProgressCode(src), 0); 1519 } 1520 1521 { 1522 // A few different ones 1523 const std::map<std::string, uint32_t> progressCodes{ 1524 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00}, 1525 {"abcdef00", 0xABCDEF00}, {"X1234567", 0}, 1526 {"1234567X", 0}, {"1 ", 0}}; 1527 1528 std::vector<uint8_t> src(72, 0x0); 1529 1530 for (const auto& [code, expected] : progressCodes) 1531 { 1532 setAsciiString(src, code); 1533 EXPECT_EQ(SRC::getProgressCode(src), expected); 1534 } 1535 1536 // empty 1537 src.clear(); 1538 EXPECT_EQ(SRC::getProgressCode(src), 0); 1539 } 1540 } 1541 1542 // Test progress is in right SRC hex data field 1543 TEST_F(SRCTest, TestProgressCodeField) 1544 { 1545 message::Entry entry; 1546 entry.src.type = 0xBD; 1547 entry.src.reasonCode = 0xABCD; 1548 entry.subsystem = 0x42; 1549 1550 AdditionalData ad; 1551 NiceMock<MockDataInterface> dataIface; 1552 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1553 "system/entry"}; 1554 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1555 .WillOnce(Return(std::vector<bool>{false, false, false})); 1556 EXPECT_CALL(dataIface, getRawProgressSRC()) 1557 .WillOnce(Return(std::vector<uint8_t>{ 1558 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0, 1559 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1560 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57, 1561 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1562 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32})); 1563 1564 SRC src{entry, ad, dataIface}; 1565 EXPECT_TRUE(src.valid()); 1566 1567 // Verify that the hex vlue is set at the right hexword 1568 EXPECT_EQ(src.hexwordData()[2], 0xCC009184); 1569 } 1570