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