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