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