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