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