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