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