1 #pragma once 2 3 #include "config.h" 4 5 #include "constants.hpp" 6 #include "event_logger.hpp" 7 #include "exceptions.hpp" 8 #include "logger.hpp" 9 #include "types.hpp" 10 11 #include <nlohmann/json.hpp> 12 #include <utility/common_utility.hpp> 13 #include <utility/dbus_utility.hpp> 14 15 #include <filesystem> 16 #include <fstream> 17 #include <regex> 18 #include <typeindex> 19 20 namespace vpd 21 { 22 namespace vpdSpecificUtility 23 { 24 /** 25 * @brief API to generate file name for bad VPD. 26 * 27 * For i2c eeproms - the pattern of the vpd-name will be 28 * i2c-<bus-number>-<eeprom-address>. 29 * For spi eeproms - the pattern of the vpd-name will be spi-<spi-number>. 30 * 31 * @param[in] i_vpdFilePath - file path of the vpd. 32 * 33 * @return On success, returns generated file name, otherwise returns empty 34 * string. 35 */ 36 inline std::string generateBadVPDFileName( 37 const std::string& i_vpdFilePath) noexcept 38 { 39 std::string l_badVpdFileName{constants::badVpdDir}; 40 try 41 { 42 if (i_vpdFilePath.find("i2c") != std::string::npos) 43 { 44 l_badVpdFileName += "i2c-"; 45 std::regex l_i2cPattern("(at24/)([0-9]+-[0-9]+)\\/"); 46 std::smatch l_match; 47 if (std::regex_search(i_vpdFilePath, l_match, l_i2cPattern)) 48 { 49 l_badVpdFileName += l_match.str(2); 50 } 51 } 52 else if (i_vpdFilePath.find("spi") != std::string::npos) 53 { 54 std::regex l_spiPattern("((spi)[0-9]+)(.0)"); 55 std::smatch l_match; 56 if (std::regex_search(i_vpdFilePath, l_match, l_spiPattern)) 57 { 58 l_badVpdFileName += l_match.str(1); 59 } 60 } 61 } 62 catch (const std::exception& l_ex) 63 { 64 l_badVpdFileName.clear(); 65 logging::logMessage("Failed to generate bad VPD file name for [" + 66 i_vpdFilePath + "]. Error: " + l_ex.what()); 67 } 68 return l_badVpdFileName; 69 } 70 71 /** 72 * @brief API which dumps the broken/bad vpd in a directory. 73 * When the vpd is bad, this API places the bad vpd file inside 74 * "/var/lib/vpd/dumps" in BMC, in order to collect bad VPD data as a part of 75 * user initiated BMC dump. 76 * 77 * 78 * @param[in] i_vpdFilePath - vpd file path 79 * @param[in] i_vpdVector - vpd vector 80 * 81 * @return On success returns 0, otherwise returns -1. 82 */ 83 inline int dumpBadVpd(const std::string& i_vpdFilePath, 84 const types::BinaryVector& i_vpdVector) noexcept 85 { 86 int l_rc{constants::FAILURE}; 87 try 88 { 89 std::filesystem::create_directory(constants::badVpdDir); 90 auto l_badVpdPath = generateBadVPDFileName(i_vpdFilePath); 91 92 if (l_badVpdPath.empty()) 93 { 94 throw std::runtime_error("Failed to generate bad VPD file name"); 95 } 96 97 if (std::filesystem::exists(l_badVpdPath)) 98 { 99 std::error_code l_ec; 100 std::filesystem::remove(l_badVpdPath, l_ec); 101 if (l_ec) // error code 102 { 103 const std::string l_errorMsg{ 104 "Error removing the existing broken vpd in " + 105 l_badVpdPath + 106 ". Error code : " + std::to_string(l_ec.value()) + 107 ". Error message : " + l_ec.message()}; 108 109 throw std::runtime_error(l_errorMsg); 110 } 111 } 112 113 std::ofstream l_badVpdFileStream(l_badVpdPath, std::ofstream::binary); 114 if (!l_badVpdFileStream.is_open()) 115 { 116 const std::string l_errorMsg{ 117 "Failed to open bad vpd file path [" + l_badVpdPath + 118 "]. Unable to dump the broken/bad vpd file."}; 119 120 throw std::runtime_error(l_errorMsg); 121 } 122 123 l_badVpdFileStream.write( 124 reinterpret_cast<const char*>(i_vpdVector.data()), 125 i_vpdVector.size()); 126 127 l_rc = constants::SUCCESS; 128 } 129 catch (const std::exception& l_ex) 130 { 131 logging::logMessage("Failed to dump bad VPD for [" + i_vpdFilePath + 132 "]. Error: " + l_ex.what()); 133 } 134 return l_rc; 135 } 136 137 /** 138 * @brief An API to read value of a keyword. 139 * 140 * 141 * @param[in] i_kwdValueMap - A map having Kwd value pair. 142 * @param[in] i_kwd - keyword name. 143 * 144 * @return On success returns value of the keyword read from map, otherwise 145 * returns empty string. 146 */ 147 inline std::string getKwVal(const types::IPZKwdValueMap& i_kwdValueMap, 148 const std::string& i_kwd) noexcept 149 { 150 std::string l_kwdValue; 151 try 152 { 153 if (i_kwd.empty()) 154 { 155 throw std::runtime_error("Invalid parameters"); 156 } 157 158 auto l_itrToKwd = i_kwdValueMap.find(i_kwd); 159 if (l_itrToKwd != i_kwdValueMap.end()) 160 { 161 l_kwdValue = l_itrToKwd->second; 162 } 163 else 164 { 165 throw std::runtime_error("Keyword not found"); 166 } 167 } 168 catch (const std::exception& l_ex) 169 { 170 logging::logMessage("Failed to get value for keyword [" + i_kwd + 171 "]. Error : " + l_ex.what()); 172 } 173 return l_kwdValue; 174 } 175 176 /** 177 * @brief An API to process encoding of a keyword. 178 * 179 * @param[in] i_keyword - Keyword to be processed. 180 * @param[in] i_encoding - Type of encoding. 181 * 182 * @return Value after being processed for encoded type. 183 */ 184 inline std::string encodeKeyword(const std::string& i_keyword, 185 const std::string& i_encoding) noexcept 186 { 187 // Default value is keyword value 188 std::string l_result(i_keyword.begin(), i_keyword.end()); 189 try 190 { 191 if (i_encoding == "MAC") 192 { 193 l_result.clear(); 194 size_t l_firstByte = i_keyword[0]; 195 l_result += commonUtility::toHex(l_firstByte >> 4); 196 l_result += commonUtility::toHex(l_firstByte & 0x0f); 197 for (size_t i = 1; i < i_keyword.size(); ++i) 198 { 199 l_result += ":"; 200 l_result += commonUtility::toHex(i_keyword[i] >> 4); 201 l_result += commonUtility::toHex(i_keyword[i] & 0x0f); 202 } 203 } 204 else if (i_encoding == "DATE") 205 { 206 // Date, represent as 207 // <year>-<month>-<day> <hour>:<min> 208 l_result.clear(); 209 static constexpr uint8_t skipPrefix = 3; 210 211 auto strItr = i_keyword.begin(); 212 advance(strItr, skipPrefix); 213 for_each(strItr, i_keyword.end(), 214 [&l_result](size_t c) { l_result += c; }); 215 216 l_result.insert(constants::BD_YEAR_END, 1, '-'); 217 l_result.insert(constants::BD_MONTH_END, 1, '-'); 218 l_result.insert(constants::BD_DAY_END, 1, ' '); 219 l_result.insert(constants::BD_HOUR_END, 1, ':'); 220 } 221 } 222 catch (const std::exception& l_ex) 223 { 224 l_result.clear(); 225 logging::logMessage("Failed to encode keyword [" + i_keyword + 226 "]. Error: " + l_ex.what()); 227 } 228 229 return l_result; 230 } 231 232 /** 233 * @brief Helper function to insert or merge in map. 234 * 235 * This method checks in an interface if the given interface exists. If the 236 * interface key already exists, property map is inserted corresponding to it. 237 * If the key does'nt exist then given interface and property map pair is newly 238 * created. If the property present in propertymap already exist in the 239 * InterfaceMap, then the new property value is ignored. 240 * 241 * @param[in,out] io_map - Interface map. 242 * @param[in] i_interface - Interface to be processed. 243 * @param[in] i_propertyMap - new property map that needs to be emplaced. 244 * 245 * @return On success returns 0, otherwise returns -1. 246 */ 247 inline int insertOrMerge(types::InterfaceMap& io_map, 248 const std::string& i_interface, 249 types::PropertyMap&& i_propertyMap) noexcept 250 { 251 int l_rc{constants::FAILURE}; 252 try 253 { 254 if (io_map.find(i_interface) != io_map.end()) 255 { 256 auto& l_prop = io_map.at(i_interface); 257 std::for_each(i_propertyMap.begin(), i_propertyMap.end(), 258 [&l_prop](auto l_keyValue) { 259 l_prop[l_keyValue.first] = l_keyValue.second; 260 }); 261 } 262 else 263 { 264 io_map.emplace(i_interface, i_propertyMap); 265 } 266 267 l_rc = constants::SUCCESS; 268 } 269 catch (const std::exception& l_ex) 270 { 271 // ToDo:: Log PEL 272 logging::logMessage( 273 "Inserting properties into interface[" + i_interface + 274 "] map failed, reason: " + std::string(l_ex.what())); 275 } 276 return l_rc; 277 } 278 279 /** 280 * @brief API to expand unpanded location code. 281 * 282 * Note: The API handles all the exception internally, in case of any error 283 * unexpanded location code will be returned as it is. 284 * 285 * @param[in] unexpandedLocationCode - Unexpanded location code. 286 * @param[in] parsedVpdMap - Parsed VPD map. 287 * @return Expanded location code. In case of any error, unexpanded is returned 288 * as it is. 289 */ 290 inline std::string getExpandedLocationCode( 291 const std::string& unexpandedLocationCode, 292 const types::VPDMapVariant& parsedVpdMap) 293 { 294 auto expanded{unexpandedLocationCode}; 295 296 try 297 { 298 // Expanded location code is formed by combining two keywords 299 // depending on type in unexpanded one. Second one is always "SE". 300 std::string kwd1, kwd2{constants::kwdSE}; 301 302 // interface to search for required keywords; 303 std::string kwdInterface; 304 305 // record which holds the required keywords. 306 std::string recordName; 307 308 auto pos = unexpandedLocationCode.find("fcs"); 309 if (pos != std::string::npos) 310 { 311 kwd1 = constants::kwdFC; 312 kwdInterface = constants::vcenInf; 313 recordName = constants::recVCEN; 314 } 315 else 316 { 317 pos = unexpandedLocationCode.find("mts"); 318 if (pos != std::string::npos) 319 { 320 kwd1 = constants::kwdTM; 321 kwdInterface = constants::vsysInf; 322 recordName = constants::recVSYS; 323 } 324 else 325 { 326 throw std::runtime_error( 327 "Error detecting type of unexpanded location code."); 328 } 329 } 330 331 std::string firstKwdValue, secondKwdValue; 332 333 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap); 334 ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end()) 335 { 336 auto itrToVCEN = (*ipzVpdMap).find(recordName); 337 firstKwdValue = getKwVal(itrToVCEN->second, kwd1); 338 if (firstKwdValue.empty()) 339 { 340 throw std::runtime_error( 341 "Failed to get value for keyword [" + kwd1 + "]"); 342 } 343 344 secondKwdValue = getKwVal(itrToVCEN->second, kwd2); 345 if (secondKwdValue.empty()) 346 { 347 throw std::runtime_error( 348 "Failed to get value for keyword [" + kwd2 + "]"); 349 } 350 } 351 else 352 { 353 std::array<const char*, 1> interfaceList = {kwdInterface.c_str()}; 354 355 types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap( 356 std::string(constants::systemVpdInvPath), interfaceList); 357 358 if (mapperRetValue.empty()) 359 { 360 throw std::runtime_error("Mapper failed to get service"); 361 } 362 363 const std::string& serviceName = std::get<0>(mapperRetValue.at(0)); 364 365 auto retVal = dbusUtility::readDbusProperty( 366 serviceName, std::string(constants::systemVpdInvPath), 367 kwdInterface, kwd1); 368 369 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal)) 370 { 371 firstKwdValue.assign( 372 reinterpret_cast<const char*>(kwdVal->data()), 373 kwdVal->size()); 374 } 375 else 376 { 377 throw std::runtime_error( 378 "Failed to read value of " + kwd1 + " from Bus"); 379 } 380 381 retVal = dbusUtility::readDbusProperty( 382 serviceName, std::string(constants::systemVpdInvPath), 383 kwdInterface, kwd2); 384 385 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal)) 386 { 387 secondKwdValue.assign( 388 reinterpret_cast<const char*>(kwdVal->data()), 389 kwdVal->size()); 390 } 391 else 392 { 393 throw std::runtime_error( 394 "Failed to read value of " + kwd2 + " from Bus"); 395 } 396 } 397 398 if (unexpandedLocationCode.find("fcs") != std::string::npos) 399 { 400 // TODO: See if ND0 can be placed in the JSON 401 expanded.replace( 402 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue); 403 } 404 else 405 { 406 replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.'); 407 expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue); 408 } 409 } 410 catch (const std::exception& ex) 411 { 412 logging::logMessage("Failed to expand location code with exception: " + 413 std::string(ex.what())); 414 } 415 416 return expanded; 417 } 418 419 /** 420 * @brief An API to get VPD in a vector. 421 * 422 * The vector is required by the respective parser to fill the VPD map. 423 * Note: API throws exception in case of failure. Caller needs to handle. 424 * 425 * @param[in] vpdFilePath - EEPROM path of the FRU. 426 * @param[out] vpdVector - VPD in vector form. 427 * @param[in] vpdStartOffset - Offset of VPD data in EEPROM. 428 */ 429 inline void getVpdDataInVector(const std::string& vpdFilePath, 430 types::BinaryVector& vpdVector, 431 size_t& vpdStartOffset) 432 { 433 try 434 { 435 std::fstream vpdFileStream; 436 vpdFileStream.exceptions( 437 std::ifstream::badbit | std::ifstream::failbit); 438 vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary); 439 auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath), 440 static_cast<uintmax_t>(65504)); 441 vpdVector.resize(vpdSizeToRead); 442 443 vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg); 444 vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]), 445 vpdSizeToRead); 446 447 vpdVector.resize(vpdFileStream.gcount()); 448 vpdFileStream.clear(std::ios_base::eofbit); 449 } 450 catch (const std::ifstream::failure& fail) 451 { 452 std::cerr << "Exception in file handling [" << vpdFilePath 453 << "] error : " << fail.what(); 454 throw; 455 } 456 } 457 458 /** 459 * @brief An API to get D-bus representation of given VPD keyword. 460 * 461 * @param[in] i_keywordName - VPD keyword name. 462 * 463 * @return D-bus representation of given keyword. 464 */ 465 inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName) 466 { 467 // Check for "#" prefixed VPD keyword. 468 if ((i_keywordName.size() == vpd::constants::TWO_BYTES) && 469 (i_keywordName.at(0) == constants::POUND_KW)) 470 { 471 // D-bus doesn't support "#". Replace "#" with "PD_" for those "#" 472 // prefixed keywords. 473 return (std::string(constants::POUND_KW_PREFIX) + 474 i_keywordName.substr(1)); 475 } 476 477 // Return the keyword name back, if D-bus representation is same as the VPD 478 // keyword name. 479 return i_keywordName; 480 } 481 482 /** 483 * @brief API to find CCIN in parsed VPD map. 484 * 485 * Few FRUs need some special handling. To identify those FRUs CCIN are used. 486 * The API will check from parsed VPD map if the FRU is the one with desired 487 * CCIN. 488 * 489 * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match. 490 * @param[in] i_parsedVpdMap - Parsed VPD map. 491 * 492 * @return True if found, false otherwise. 493 */ 494 inline bool findCcinInVpd(const nlohmann::json& i_JsonObject, 495 const types::VPDMapVariant& i_parsedVpdMap) noexcept 496 { 497 bool l_rc{false}; 498 try 499 { 500 if (i_JsonObject.empty()) 501 { 502 throw std::runtime_error("Json object is empty. Can't find CCIN"); 503 } 504 505 if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap)) 506 { 507 auto l_itrToRec = (*l_ipzVPDMap).find("VINI"); 508 if (l_itrToRec == (*l_ipzVPDMap).end()) 509 { 510 throw DataException( 511 "VINI record not found in parsed VPD. Can't find CCIN"); 512 } 513 514 std::string l_ccinFromVpd{ 515 vpdSpecificUtility::getKwVal(l_itrToRec->second, "CC")}; 516 if (l_ccinFromVpd.empty()) 517 { 518 throw DataException( 519 "Empty CCIN value in VPD map. Can't find CCIN"); 520 } 521 522 transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(), 523 l_ccinFromVpd.begin(), ::toupper); 524 525 for (std::string l_ccinValue : i_JsonObject["ccin"]) 526 { 527 transform(l_ccinValue.begin(), l_ccinValue.end(), 528 l_ccinValue.begin(), ::toupper); 529 530 if (l_ccinValue.compare(l_ccinFromVpd) == 531 constants::STR_CMP_SUCCESS) 532 { 533 // CCIN found 534 l_rc = true; 535 } 536 } 537 538 if (!l_rc) 539 { 540 logging::logMessage("No match found for CCIN"); 541 } 542 } 543 else 544 { 545 logging::logMessage("VPD type not supported. Can't find CCIN"); 546 } 547 } 548 catch (const std::exception& l_ex) 549 { 550 const std::string l_errMsg{ 551 "Failed to find CCIN in VPD. Error : " + std::string(l_ex.what())}; 552 553 if (typeid(l_ex) == std::type_index(typeid(DataException))) 554 { 555 EventLogger::createSyncPel( 556 types::ErrorType::InvalidVpdMessage, 557 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, 558 l_errMsg, std::nullopt, std::nullopt, std::nullopt, 559 std::nullopt); 560 } 561 562 logging::logMessage(l_errMsg); 563 } 564 return l_rc; 565 } 566 567 /** 568 * @brief API to reset data of a FRU populated under PIM. 569 * 570 * This API resets the data for particular interfaces of a FRU under PIM. 571 * 572 * @param[in] i_objectPath - DBus object path of the FRU. 573 * @param[in] io_interfaceMap - Interface and its properties map. 574 */ 575 inline void resetDataUnderPIM(const std::string& i_objectPath, 576 types::InterfaceMap& io_interfaceMap) 577 { 578 try 579 { 580 std::array<const char*, 0> l_interfaces; 581 const types::MapperGetObject& l_getObjectMap = 582 dbusUtility::getObjectMap(i_objectPath, l_interfaces); 583 584 const std::vector<std::string>& l_vpdRelatedInterfaces{ 585 constants::operationalStatusInf, constants::inventoryItemInf, 586 constants::assetInf, constants::vpdCollectionInterface}; 587 588 for (const auto& [l_service, l_interfaceList] : l_getObjectMap) 589 { 590 if (l_service.compare(constants::pimServiceName) != 591 constants::STR_CMP_SUCCESS) 592 { 593 continue; 594 } 595 596 for (const auto& l_interface : l_interfaceList) 597 { 598 if ((l_interface.find(constants::ipzVpdInf) != 599 std::string::npos) || 600 ((std::find(l_vpdRelatedInterfaces.begin(), 601 l_vpdRelatedInterfaces.end(), l_interface)) != 602 l_vpdRelatedInterfaces.end())) 603 { 604 const types::PropertyMap& l_propertyValueMap = 605 dbusUtility::getPropertyMap(l_service, i_objectPath, 606 l_interface); 607 608 types::PropertyMap l_propertyMap; 609 610 for (const auto& l_aProperty : l_propertyValueMap) 611 { 612 const std::string& l_propertyName = l_aProperty.first; 613 const auto& l_propertyValue = l_aProperty.second; 614 615 if (std::holds_alternative<types::BinaryVector>( 616 l_propertyValue)) 617 { 618 l_propertyMap.emplace(l_propertyName, 619 types::BinaryVector{}); 620 } 621 else if (std::holds_alternative<std::string>( 622 l_propertyValue)) 623 { 624 if (l_propertyName.compare("CollectionStatus") == 625 constants::STR_CMP_SUCCESS) 626 { 627 l_propertyMap.emplace( 628 l_propertyName, 629 constants::vpdCollectionNotStarted); 630 } 631 else 632 { 633 l_propertyMap.emplace(l_propertyName, 634 std::string{}); 635 } 636 } 637 else if (std::holds_alternative<bool>(l_propertyValue)) 638 { 639 if (l_propertyName.compare("Present") == 640 constants::STR_CMP_SUCCESS) 641 { 642 l_propertyMap.emplace(l_propertyName, false); 643 } 644 else if (l_propertyName.compare("Functional") == 645 constants::STR_CMP_SUCCESS) 646 { 647 // Since FRU is not present functional property 648 // is considered as true. 649 l_propertyMap.emplace(l_propertyName, true); 650 } 651 } 652 } 653 io_interfaceMap.emplace(l_interface, 654 std::move(l_propertyMap)); 655 } 656 } 657 } 658 } 659 catch (const std::exception& l_ex) 660 { 661 logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath + 662 " with error: " + std::string(l_ex.what())); 663 } 664 } 665 666 /** 667 * @brief API to detect pass1 planar type. 668 * 669 * Based on HW version and IM keyword, This API detects is it is a pass1 planar 670 * or not. 671 * 672 * @return True if pass 1 planar, false otherwise. 673 */ 674 inline bool isPass1Planar() noexcept 675 { 676 bool l_rc{false}; 677 try 678 { 679 auto l_retVal = dbusUtility::readDbusProperty( 680 constants::pimServiceName, constants::systemVpdInvPath, 681 constants::viniInf, constants::kwdHW); 682 683 auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal); 684 685 l_retVal = dbusUtility::readDbusProperty( 686 constants::pimServiceName, constants::systemInvPath, 687 constants::vsbpInf, constants::kwdIM); 688 689 auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal); 690 691 if (l_hwVer && l_imValue) 692 { 693 if (l_hwVer->size() != constants::VALUE_2) 694 { 695 throw std::runtime_error("Invalid HW keyword length."); 696 } 697 698 if (l_imValue->size() != constants::VALUE_4) 699 { 700 throw std::runtime_error("Invalid IM keyword length."); 701 } 702 703 const types::BinaryVector l_everest{80, 00, 48, 00}; 704 const types::BinaryVector l_fuji{96, 00, 32, 00}; 705 706 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji)) 707 { 708 if ((*l_hwVer).at(1) < constants::VALUE_21) 709 { 710 l_rc = true; 711 } 712 } 713 else if ((*l_hwVer).at(1) < constants::VALUE_2) 714 { 715 l_rc = true; 716 } 717 } 718 } 719 catch (const std::exception& l_ex) 720 { 721 logging::logMessage("Failed to check for pass 1 planar. Error: " + 722 std::string(l_ex.what())); 723 } 724 725 return l_rc; 726 } 727 728 /** 729 * @brief API to detect if system configuration is that of PowerVS system. 730 * 731 * @param[in] i_imValue - IM value of the system. 732 * @return true if it is PowerVS configuration, false otherwise. 733 */ 734 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue) 735 { 736 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4) 737 { 738 return false; 739 } 740 741 // Should be a 0x5000XX series system. 742 if (i_imValue.at(0) == constants::HEX_VALUE_50 && 743 i_imValue.at(1) == constants::HEX_VALUE_00) 744 { 745 std::string l_imagePrefix = dbusUtility::getImagePrefix(); 746 747 // Check image for 0x500030XX series. 748 if ((i_imValue.at(2) == constants::HEX_VALUE_30) && 749 ((l_imagePrefix == constants::powerVsImagePrefix_MY) || 750 (l_imagePrefix == constants::powerVsImagePrefix_NY))) 751 { 752 logging::logMessage("PowerVS configuration"); 753 return true; 754 } 755 756 // Check image for 0X500010XX series. 757 if ((i_imValue.at(2) == constants::HEX_VALUE_10) && 758 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) || 759 (l_imagePrefix == constants::powerVsImagePrefix_NZ))) 760 { 761 logging::logMessage("PowerVS configuration"); 762 return true; 763 } 764 } 765 return false; 766 } 767 768 /** 769 * @brief API to get CCIN for a given FRU from DBus. 770 * 771 * The API reads the CCIN for a FRU based on its inventory path. 772 * 773 * @param[in] i_invObjPath - Inventory path of the FRU. 774 * @return CCIN of the FRU on success, empty string otherwise. 775 */ 776 inline std::string getCcinFromDbus(const std::string& i_invObjPath) 777 { 778 try 779 { 780 if (i_invObjPath.empty()) 781 { 782 throw std::runtime_error("Empty EEPROM path, can't read CCIN"); 783 } 784 785 const auto& l_retValue = dbusUtility::readDbusProperty( 786 constants::pimServiceName, i_invObjPath, constants::viniInf, 787 constants::kwdCCIN); 788 789 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue); 790 if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4) 791 { 792 throw DbusException("Invalid CCIN read from Dbus"); 793 } 794 795 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end()); 796 } 797 catch (const std::exception& l_ex) 798 { 799 logging::logMessage(l_ex.what()); 800 return std::string{}; 801 } 802 } 803 804 /** 805 * @brief API to check if the current running image is a powerVS image. 806 * 807 * @return true if it is PowerVS image, false otherwise. 808 */ 809 inline bool isPowerVsImage() 810 { 811 std::string l_imagePrefix = dbusUtility::getImagePrefix(); 812 813 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) || 814 (l_imagePrefix == constants::powerVsImagePrefix_NY) || 815 (l_imagePrefix == constants::powerVsImagePrefix_MZ) || 816 (l_imagePrefix == constants::powerVsImagePrefix_NZ)) 817 { 818 return true; 819 } 820 return false; 821 } 822 823 /** 824 * @brief API to sync keyword update to inherited FRUs. 825 * 826 * For a given keyword update on a EEPROM path, this API syncs the keyword 827 * update to all inherited FRUs' respective interface, property on PIM. 828 * 829 * @param[in] i_fruPath - EEPROM path of FRU. 830 * @param[in] i_paramsToWriteData - Input details. 831 * @param[in] i_sysCfgJsonObj - System config JSON. 832 * 833 */ 834 inline void updateKwdOnInheritedFrus( 835 const std::string& i_fruPath, 836 const types::WriteVpdParams& i_paramsToWriteData, 837 const nlohmann::json& i_sysCfgJsonObj) noexcept 838 { 839 try 840 { 841 if (!i_sysCfgJsonObj.contains("frus")) 842 { 843 throw std::runtime_error("Mandatory tag(s) missing from JSON"); 844 } 845 846 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath)) 847 { 848 throw std::runtime_error( 849 "VPD path [" + i_fruPath + "] not found in system config JSON"); 850 } 851 852 const types::IpzData* l_ipzData = 853 std::get_if<types::IpzData>(&i_paramsToWriteData); 854 855 if (!l_ipzData) 856 { 857 throw std::runtime_error("Unsupported VPD type"); 858 } 859 // iterate through all inventory paths for given EEPROM path, 860 // except the base FRU. 861 // if for an inventory path, "inherit" tag is true, 862 // update the inventory path's com.ibm.ipzvpd.<record>,keyword 863 // property 864 865 types::ObjectMap l_objectInterfaceMap; 866 867 auto l_populateInterfaceMap = 868 [&l_objectInterfaceMap, 869 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) { 870 // update inherited FRUs only 871 if (l_Fru.value("inherit", true)) 872 { 873 l_objectInterfaceMap.emplace( 874 sdbusplus::message::object_path{l_Fru["inventoryPath"]}, 875 types::InterfaceMap{ 876 {std::string{constants::ipzVpdInf + 877 std::get<0>(*l_ipzData)}, 878 types::PropertyMap{{std::get<1>(*l_ipzData), 879 std::get<2>(*l_ipzData)}}}}); 880 } 881 }; 882 883 // iterate through all FRUs except the base FRU 884 std::for_each( 885 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1, 886 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap); 887 888 if (!l_objectInterfaceMap.empty()) 889 { 890 // notify PIM 891 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 892 { 893 throw std::runtime_error( 894 "Call to PIM failed for VPD file " + i_fruPath); 895 } 896 } 897 } 898 catch (const std::exception& l_ex) 899 { 900 logging::logMessage( 901 "Failed to sync keyword update to inherited FRUs of FRU [" + 902 i_fruPath + "]. Error: " + std::string(l_ex.what())); 903 } 904 } 905 906 /** 907 * @brief API to get common interface(s) properties corresponding to given 908 * record and keyword. 909 * 910 * For a given record and keyword, this API finds the corresponding common 911 * interfaces(s) properties from the system config JSON and populates an 912 * interface map with the respective properties and values. 913 * 914 * @param[in] i_paramsToWriteData - Input details. 915 * @param[in] i_commonInterfaceJson - Common interface JSON object. 916 * 917 * @return Returns a map of common interface(s) and properties corresponding to 918 * the record and keyword. An empty map is returned if no such common 919 * interface(s) and properties are found. 920 */ 921 inline types::InterfaceMap getCommonInterfaceProperties( 922 const types::WriteVpdParams& i_paramsToWriteData, 923 const nlohmann::json& i_commonInterfaceJson) noexcept 924 { 925 types::InterfaceMap l_interfaceMap; 926 try 927 { 928 const types::IpzData* l_ipzData = 929 std::get_if<types::IpzData>(&i_paramsToWriteData); 930 931 if (!l_ipzData) 932 { 933 throw std::runtime_error("Invalid VPD type"); 934 } 935 936 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData), 937 &l_interfaceMap]( 938 const auto& l_interfacesPropPair) { 939 // find matching property value pair 940 const auto l_matchPropValuePairIt = std::find_if( 941 l_interfacesPropPair.value().items().begin(), 942 l_interfacesPropPair.value().items().end(), 943 [&l_ipzData](const auto& l_propValuePair) { 944 return (l_propValuePair.value().value("recordName", "") == 945 std::get<0>(*l_ipzData) && 946 l_propValuePair.value().value("keywordName", "") == 947 std::get<1>(*l_ipzData)); 948 }); 949 950 if (l_matchPropValuePairIt != 951 l_interfacesPropPair.value().items().end()) 952 { 953 // add property map to interface map 954 l_interfaceMap.emplace( 955 l_interfacesPropPair.key(), 956 types::PropertyMap{ 957 {l_matchPropValuePairIt.key(), 958 vpdSpecificUtility::encodeKeyword( 959 std::string(std::get<2>(*l_ipzData).begin(), 960 std::get<2>(*l_ipzData).end()), 961 l_matchPropValuePairIt.value().value("encoding", 962 ""))}}); 963 } 964 }; 965 966 // iterate through all common interfaces and populate interface map 967 std::for_each(i_commonInterfaceJson.items().begin(), 968 i_commonInterfaceJson.items().end(), 969 l_populateInterfaceMap); 970 } 971 catch (const std::exception& l_ex) 972 { 973 logging::logMessage( 974 "Failed to find common interface properties. Error: " + 975 std::string(l_ex.what())); 976 } 977 return l_interfaceMap; 978 } 979 980 /** 981 * @brief API to update common interface(s) properties when keyword is updated. 982 * 983 * For a given keyword update on a EEPROM path, this API syncs the keyword 984 * update to respective common interface(s) properties of the base FRU and all 985 * inherited FRUs. 986 * 987 * @param[in] i_fruPath - EEPROM path of FRU. 988 * @param[in] i_paramsToWriteData - Input details. 989 * @param[in] i_sysCfgJsonObj - System config JSON. 990 * 991 */ 992 inline void updateCiPropertyOfInheritedFrus( 993 const std::string& i_fruPath, 994 const types::WriteVpdParams& i_paramsToWriteData, 995 const nlohmann::json& i_sysCfgJsonObj) noexcept 996 { 997 try 998 { 999 if (!i_sysCfgJsonObj.contains("commonInterfaces")) 1000 { 1001 // no common interfaces in JSON, nothing to do 1002 return; 1003 } 1004 1005 if (!i_sysCfgJsonObj.contains("frus")) 1006 { 1007 throw std::runtime_error("Mandatory tag(s) missing from JSON"); 1008 } 1009 1010 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath)) 1011 { 1012 throw std::runtime_error( 1013 "VPD path [" + i_fruPath + "] not found in system config JSON"); 1014 } 1015 1016 if (!std::get_if<types::IpzData>(&i_paramsToWriteData)) 1017 { 1018 throw std::runtime_error("Unsupported VPD type"); 1019 } 1020 1021 // iterate through all inventory paths for given EEPROM path, 1022 // if for an inventory path, "inherit" tag is true, 1023 // update the inventory path's com.ibm.ipzvpd.<record>,keyword 1024 // property 1025 1026 types::ObjectMap l_objectInterfaceMap; 1027 1028 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties( 1029 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"]); 1030 1031 if (l_interfaceMap.empty()) 1032 { 1033 // nothing to do 1034 return; 1035 } 1036 1037 auto l_populateObjectInterfaceMap = 1038 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const( 1039 l_interfaceMap)](const auto& l_Fru) { 1040 if (l_Fru.value("inherit", true) && 1041 l_Fru.contains("inventoryPath")) 1042 { 1043 l_objectInterfaceMap.emplace( 1044 sdbusplus::message::object_path{l_Fru["inventoryPath"]}, 1045 l_interfaceMap); 1046 } 1047 }; 1048 1049 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(), 1050 i_sysCfgJsonObj["frus"][i_fruPath].end(), 1051 l_populateObjectInterfaceMap); 1052 1053 if (!l_objectInterfaceMap.empty()) 1054 { 1055 // notify PIM 1056 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1057 { 1058 throw std::runtime_error( 1059 "Call to PIM failed for VPD file " + i_fruPath); 1060 } 1061 } 1062 } 1063 catch (const std::exception& l_ex) 1064 { 1065 logging::logMessage( 1066 "Failed to update common interface properties of FRU [" + 1067 i_fruPath + "]. Error: " + std::string(l_ex.what())); 1068 } 1069 } 1070 } // namespace vpdSpecificUtility 1071 } // namespace vpd 1072