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( 648 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 649 650 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false)) 651 .WillOnce(Return( 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( 870 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 871 872 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)) 873 .Times(3) 874 .WillRepeatedly( 875 Return("/xyz/openbmc_project/inventory/chassis/motherboard")); 876 877 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false)) 878 .Times(3) 879 .WillRepeatedly( 880 Return("/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("/inv/system/chassis/motherboard/bmc")); 1093 EXPECT_CALL( 1094 dataIface, 1095 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1096 .Times(1) 1097 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1098 SetArgReferee<2>("CCCC"), 1099 SetArgReferee<3>("123456789ABC"))); 1100 } 1101 // Callout 1 mock calls 1102 { 1103 EXPECT_CALL(dataIface, 1104 getLocationCode("/inv/system/chassis/motherboard/cpu0")) 1105 .WillOnce(Return("UYYY-P5")); 1106 EXPECT_CALL( 1107 dataIface, 1108 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _)) 1109 .Times(1) 1110 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 1111 SetArgReferee<2>("DDDD"), 1112 SetArgReferee<3>("23456789ABCD"))); 1113 } 1114 // Callout 3 mock calls 1115 { 1116 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0)) 1117 .Times(1) 1118 .WillOnce(Return("UXXX-P1-C23")); 1119 } 1120 // Callout 4 mock calls 1121 { 1122 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0)) 1123 .Times(1) 1124 .WillOnce(Return("UXXX-P1-C24")); 1125 } 1126 1127 SRC src{entry, ad, jsonCallouts, dataIface}; 1128 ASSERT_TRUE(src.callouts()); 1129 1130 // Check the guarded and deconfigured flags 1131 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000); 1132 1133 const auto& callouts = src.callouts()->callouts(); 1134 ASSERT_EQ(callouts.size(), 8); 1135 1136 // Check callout 0 1137 { 1138 EXPECT_EQ(callouts[0]->priority(), 'H'); 1139 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); 1140 1141 auto& fru = callouts[0]->fruIdentity(); 1142 EXPECT_EQ(fru->getPN().value(), "1234567"); 1143 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1144 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1145 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1146 1147 auto& mruCallouts = callouts[0]->mru(); 1148 ASSERT_TRUE(mruCallouts); 1149 auto& mrus = mruCallouts->mrus(); 1150 ASSERT_EQ(mrus.size(), 2); 1151 EXPECT_EQ(mrus[0].id, 42); 1152 EXPECT_EQ(mrus[0].priority, 'H'); 1153 EXPECT_EQ(mrus[1].id, 43); 1154 EXPECT_EQ(mrus[1].priority, 'M'); 1155 } 1156 1157 // Check callout 1 1158 { 1159 EXPECT_EQ(callouts[1]->priority(), 'M'); 1160 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5"); 1161 1162 auto& fru = callouts[1]->fruIdentity(); 1163 EXPECT_EQ(fru->getPN().value(), "2345678"); 1164 EXPECT_EQ(fru->getCCIN().value(), "DDDD"); 1165 EXPECT_EQ(fru->getSN().value(), "23456789ABCD"); 1166 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1167 } 1168 1169 // Check callout 2 1170 { 1171 EXPECT_EQ(callouts[2]->priority(), 'A'); 1172 EXPECT_EQ(callouts[2]->locationCode(), ""); 1173 1174 auto& fru = callouts[2]->fruIdentity(); 1175 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); 1176 EXPECT_EQ(fru->failingComponentType(), 1177 src::FRUIdentity::maintenanceProc); 1178 } 1179 1180 // Check callout 3 1181 { 1182 EXPECT_EQ(callouts[3]->priority(), 'B'); 1183 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23"); 1184 1185 auto& fru = callouts[3]->fruIdentity(); 1186 EXPECT_EQ(fru->getPN().value(), "TRUSTED"); 1187 EXPECT_EQ(fru->failingComponentType(), 1188 src::FRUIdentity::symbolicFRUTrustedLocCode); 1189 } 1190 1191 // Check callout 4 1192 { 1193 EXPECT_EQ(callouts[4]->priority(), 'C'); 1194 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24"); 1195 1196 auto& fru = callouts[4]->fruIdentity(); 1197 EXPECT_EQ(fru->getPN().value(), "FRUTST1"); 1198 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1199 } 1200 1201 // Check callout 5 1202 { 1203 EXPECT_EQ(callouts[5]->priority(), 'L'); 1204 EXPECT_EQ(callouts[5]->locationCode(), ""); 1205 1206 auto& fru = callouts[5]->fruIdentity(); 1207 EXPECT_EQ(fru->getPN().value(), "FRUTST2"); 1208 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1209 } 1210 1211 // Check callout 6 1212 { 1213 EXPECT_EQ(callouts[6]->priority(), 'L'); 1214 EXPECT_EQ(callouts[6]->locationCode(), ""); 1215 1216 auto& fru = callouts[6]->fruIdentity(); 1217 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004"); 1218 EXPECT_EQ(fru->failingComponentType(), 1219 src::FRUIdentity::maintenanceProc); 1220 } 1221 1222 // Check callout 7 1223 { 1224 EXPECT_EQ(callouts[7]->priority(), 'L'); 1225 EXPECT_EQ(callouts[7]->locationCode(), ""); 1226 1227 auto& fru = callouts[7]->fruIdentity(); 1228 EXPECT_EQ(fru->getPN().value(), "AMBTEMP"); 1229 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1230 } 1231 1232 // Check that it didn't find any errors 1233 const auto& data = src.getDebugData(); 1234 EXPECT_TRUE(data.empty()); 1235 } 1236 1237 TEST_F(SRCTest, JsonBadCalloutsTest) 1238 { 1239 // The first call will have a Throw in a mock call. 1240 // The second will have a different Throw in a mock call. 1241 // The others have issues with the Priority field. 1242 const auto jsonCallouts = R"( 1243 [ 1244 { 1245 "LocationCode": "P0-C1", 1246 "Priority": "H" 1247 }, 1248 { 1249 "LocationCode": "P0-C2", 1250 "Priority": "H" 1251 }, 1252 { 1253 "LocationCode": "P0-C3" 1254 }, 1255 { 1256 "LocationCode": "P0-C4", 1257 "Priority": "X" 1258 } 1259 ] 1260 )"_json; 1261 1262 message::Entry entry; 1263 entry.src.type = 0xBD; 1264 entry.src.reasonCode = 0xABCD; 1265 entry.subsystem = 0x42; 1266 1267 AdditionalData ad; 1268 NiceMock<MockDataInterface> dataIface; 1269 1270 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1271 "system/entry"}; 1272 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1273 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 1274 1275 // Callout 0 mock calls 1276 // Expand location code will fail, so the unexpanded location 1277 // code should show up in the callout instead. 1278 { 1279 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1280 .WillOnce(Throw(std::runtime_error("Fail"))); 1281 1282 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1283 .Times(1) 1284 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1285 EXPECT_CALL( 1286 dataIface, 1287 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1288 .Times(1) 1289 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1290 SetArgReferee<2>("CCCC"), 1291 SetArgReferee<3>("123456789ABC"))); 1292 } 1293 1294 // Callout 1 mock calls 1295 // getInventoryFromLocCode will fail 1296 { 1297 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0)) 1298 .Times(1) 1299 .WillOnce(Return("UXXX-P0-C2")); 1300 1301 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false)) 1302 .Times(1) 1303 .WillOnce(Throw(std::runtime_error("Fail"))); 1304 } 1305 1306 SRC src{entry, ad, jsonCallouts, dataIface}; 1307 1308 ASSERT_TRUE(src.callouts()); 1309 1310 const auto& callouts = src.callouts()->callouts(); 1311 1312 // Only the first callout was successful 1313 ASSERT_EQ(callouts.size(), 1); 1314 1315 { 1316 EXPECT_EQ(callouts[0]->priority(), 'H'); 1317 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1"); 1318 1319 auto& fru = callouts[0]->fruIdentity(); 1320 EXPECT_EQ(fru->getPN().value(), "1234567"); 1321 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1322 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1323 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1324 } 1325 1326 const auto& data = src.getDebugData(); 1327 ASSERT_EQ(data.size(), 4); 1328 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail"); 1329 EXPECT_STREQ(data[1].c_str(), 1330 "Failed extracting callout data from JSON: Unable to " 1331 "get inventory path from location code: P0-C2: Fail"); 1332 EXPECT_STREQ(data[2].c_str(), 1333 "Failed extracting callout data from JSON: " 1334 "[json.exception.out_of_range.403] key 'Priority' not found"); 1335 EXPECT_STREQ(data[3].c_str(), 1336 "Failed extracting callout data from JSON: Invalid " 1337 "priority 'X' found in JSON callout"); 1338 } 1339 1340 // Test that an inventory path callout can have 1341 // a different priority than H. 1342 TEST_F(SRCTest, InventoryCalloutTestPriority) 1343 { 1344 message::Entry entry; 1345 entry.src.type = 0xBD; 1346 entry.src.reasonCode = 0xABCD; 1347 entry.subsystem = 0x42; 1348 1349 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard", 1350 "CALLOUT_PRIORITY=M"}; 1351 AdditionalData ad{adData}; 1352 NiceMock<MockDataInterface> dataIface; 1353 1354 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1355 "system/entry"}; 1356 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1357 .WillOnce(Return(std::vector<bool>{false, false, false})); 1358 1359 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 1360 .WillOnce(Return("UTMS-P1")); 1361 1362 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 1363 .Times(1) 1364 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 1365 SetArgReferee<3>("123456789ABC"))); 1366 1367 SRC src{entry, ad, dataIface}; 1368 EXPECT_TRUE(src.valid()); 1369 1370 ASSERT_TRUE(src.callouts()); 1371 1372 EXPECT_EQ(src.callouts()->callouts().size(), 1); 1373 1374 auto& callout = src.callouts()->callouts().front(); 1375 1376 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 1377 EXPECT_EQ(callout->priority(), 'M'); 1378 } 1379 1380 // Test for bmc & platform dump status bits 1381 TEST_F(SRCTest, DumpStatusBitsCheck) 1382 { 1383 message::Entry entry; 1384 entry.src.type = 0xBD; 1385 entry.src.reasonCode = 0xABCD; 1386 entry.subsystem = 0x42; 1387 1388 AdditionalData ad; 1389 NiceMock<MockDataInterface> dataIface; 1390 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1391 "system/entry"}; 1392 1393 { 1394 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1395 .WillOnce(Return(std::vector<bool>{true, false, false})); 1396 1397 SRC src{entry, ad, dataIface}; 1398 EXPECT_TRUE(src.valid()); 1399 1400 const auto& hexwords = src.hexwordData(); 1401 EXPECT_EQ(0x00080055, hexwords[0]); 1402 } 1403 1404 { 1405 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1406 .WillOnce(Return(std::vector<bool>{false, true, false})); 1407 1408 SRC src{entry, ad, dataIface}; 1409 EXPECT_TRUE(src.valid()); 1410 1411 const auto& hexwords = src.hexwordData(); 1412 EXPECT_EQ(0x00000255, hexwords[0]); 1413 } 1414 1415 { 1416 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1417 .WillOnce(Return(std::vector<bool>{false, false, true})); 1418 1419 SRC src{entry, ad, dataIface}; 1420 EXPECT_TRUE(src.valid()); 1421 1422 const auto& hexwords = src.hexwordData(); 1423 EXPECT_EQ(0x00000455, hexwords[0]); 1424 } 1425 1426 { 1427 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1428 .WillOnce(Return(std::vector<bool>{true, true, true})); 1429 1430 SRC src{entry, ad, dataIface}; 1431 EXPECT_TRUE(src.valid()); 1432 1433 const auto& hexwords = src.hexwordData(); 1434 EXPECT_EQ(0x00080655, hexwords[0]); 1435 } 1436 } 1437 1438 // Test SRC with additional data - PEL_SUBSYSTEM 1439 TEST_F(SRCTest, TestPELSubsystem) 1440 { 1441 message::Entry entry; 1442 entry.src.type = 0xBD; 1443 entry.src.reasonCode = 0xABCD; 1444 entry.subsystem = 0x42; 1445 1446 // Values for the SRC words pointed to above 1447 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"}; 1448 AdditionalData ad{adData}; 1449 NiceMock<MockDataInterface> dataIface; 1450 1451 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD")); 1452 1453 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1454 "system/entry"}; 1455 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1456 .WillOnce(Return(std::vector<bool>{false, false, false})); 1457 1458 SRC src{entry, ad, dataIface}; 1459 1460 EXPECT_TRUE(src.valid()); 1461 1462 EXPECT_EQ(src.asciiString(), "BD20ABCD "); 1463 } 1464 1465 void setAsciiString(std::vector<uint8_t>& src, const std::string& value) 1466 { 1467 assert(40 + value.size() <= src.size()); 1468 1469 for (size_t i = 0; i < value.size(); i++) 1470 { 1471 src[40 + i] = value[i]; 1472 } 1473 } 1474 1475 TEST_F(SRCTest, TestGetProgressCode) 1476 { 1477 { 1478 // A real SRC with CC009184 1479 std::vector<uint8_t> src{ 1480 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0, 1481 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1482 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57, 1483 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1484 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 1485 1486 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184); 1487 } 1488 1489 { 1490 // A real SRC with STANDBY 1491 std::vector<uint8_t> src{ 1492 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 1493 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1494 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68, 1495 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1496 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 1497 1498 EXPECT_EQ(SRC::getProgressCode(src), 0); 1499 } 1500 1501 { 1502 // A real SRC with CC009184, but 1 byte too short 1503 std::vector<uint8_t> src{ 1504 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0, 1505 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57, 1507 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1508 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 1509 src.resize(71); 1510 EXPECT_EQ(SRC::getProgressCode(src), 0); 1511 } 1512 1513 { 1514 // A few different ones 1515 const std::map<std::string, uint32_t> progressCodes{ 1516 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00}, 1517 {"abcdef00", 0xABCDEF00}, {"X1234567", 0}, 1518 {"1234567X", 0}, {"1 ", 0}}; 1519 1520 std::vector<uint8_t> src(72, 0x0); 1521 1522 for (const auto& [code, expected] : progressCodes) 1523 { 1524 setAsciiString(src, code); 1525 EXPECT_EQ(SRC::getProgressCode(src), expected); 1526 } 1527 1528 // empty 1529 src.clear(); 1530 EXPECT_EQ(SRC::getProgressCode(src), 0); 1531 } 1532 } 1533 1534 // Test progress is in right SRC hex data field 1535 TEST_F(SRCTest, TestProgressCodeField) 1536 { 1537 message::Entry entry; 1538 entry.src.type = 0xBD; 1539 entry.src.reasonCode = 0xABCD; 1540 entry.subsystem = 0x42; 1541 1542 AdditionalData ad; 1543 NiceMock<MockDataInterface> dataIface; 1544 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1545 "system/entry"}; 1546 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1547 .WillOnce(Return(std::vector<bool>{false, false, false})); 1548 EXPECT_CALL(dataIface, getRawProgressSRC()) 1549 .WillOnce(Return(std::vector<uint8_t>{ 1550 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0, 1551 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1552 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57, 1553 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1554 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32})); 1555 1556 SRC src{entry, ad, dataIface}; 1557 EXPECT_TRUE(src.valid()); 1558 1559 // Verify that the hex vlue is set at the right hexword 1560 EXPECT_EQ(src.hexwordData()[2], 0xCC009184); 1561 } 1562