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