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 if (l_propertyName.compare("PrettyName") == 723 constants::STR_CMP_SUCCESS) 724 { 725 // The FRU name is constant and independent of 726 // its presence state. So, it should not get 727 // reset. 728 continue; 729 } 730 else 731 { 732 l_propertyMap.emplace(l_propertyName, 733 std::string{}); 734 } 735 } 736 else if (std::holds_alternative<bool>(l_propertyValue)) 737 { 738 if (l_propertyName.compare("Present") == 739 constants::STR_CMP_SUCCESS) 740 { 741 l_propertyMap.emplace(l_propertyName, false); 742 } 743 else if (l_propertyName.compare("Functional") == 744 constants::STR_CMP_SUCCESS) 745 { 746 // Since FRU is not present functional property 747 // is considered as true. 748 l_propertyMap.emplace(l_propertyName, true); 749 } 750 } 751 } 752 io_interfaceMap.emplace(l_interface, 753 std::move(l_propertyMap)); 754 } 755 } 756 } 757 } 758 catch (const std::exception& l_ex) 759 { 760 o_errCode = error_code::STANDARD_EXCEPTION; 761 } 762 } 763 764 /** 765 * @brief API to detect pass1 planar type. 766 * 767 * Based on HW version and IM keyword, This API detects is it is a pass1 planar 768 * or not. 769 * @param[out] o_errCode - To set error code in case of error. 770 * 771 * @return True if pass 1 planar, false otherwise. 772 */ 773 inline bool isPass1Planar(uint16_t& o_errCode) noexcept 774 { 775 o_errCode = 0; 776 bool l_rc{false}; 777 auto l_retVal = dbusUtility::readDbusProperty( 778 constants::pimServiceName, constants::systemVpdInvPath, 779 constants::viniInf, constants::kwdHW); 780 781 auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal); 782 783 l_retVal = dbusUtility::readDbusProperty( 784 constants::pimServiceName, constants::systemInvPath, constants::vsbpInf, 785 constants::kwdIM); 786 787 auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal); 788 789 if (l_hwVer && l_imValue) 790 { 791 if (l_hwVer->size() != constants::VALUE_2) 792 { 793 o_errCode = error_code::INVALID_KEYWORD_LENGTH; 794 return l_rc; 795 } 796 797 if (l_imValue->size() != constants::VALUE_4) 798 { 799 o_errCode = error_code::INVALID_KEYWORD_LENGTH; 800 return l_rc; 801 } 802 803 const types::BinaryVector l_everest{80, 00, 48, 00}; 804 const types::BinaryVector l_fuji{96, 00, 32, 00}; 805 806 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji)) 807 { 808 if ((*l_hwVer).at(1) < constants::VALUE_21) 809 { 810 l_rc = true; 811 } 812 } 813 else if ((*l_hwVer).at(1) < constants::VALUE_2) 814 { 815 l_rc = true; 816 } 817 } 818 819 return l_rc; 820 } 821 822 /** 823 * @brief API to detect if system configuration is that of PowerVS system. 824 * 825 * @param[in] i_imValue - IM value of the system. 826 * @param[out] o_errCode - To set error code in case of error. 827 * @return true if it is PowerVS configuration, false otherwise. 828 */ 829 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue, 830 uint16_t& o_errCode) 831 { 832 o_errCode = 0; 833 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4) 834 { 835 o_errCode = error_code::INVALID_INPUT_PARAMETER; 836 return false; 837 } 838 839 // Should be a 0x5000XX series system. 840 if (i_imValue.at(0) == constants::HEX_VALUE_50 && 841 i_imValue.at(1) == constants::HEX_VALUE_00) 842 { 843 std::string l_imagePrefix = dbusUtility::getImagePrefix(); 844 845 // Check image for 0x500030XX series. 846 if ((i_imValue.at(2) == constants::HEX_VALUE_30) && 847 ((l_imagePrefix == constants::powerVsImagePrefix_MY) || 848 (l_imagePrefix == constants::powerVsImagePrefix_NY))) 849 { 850 logging::logMessage("PowerVS configuration"); 851 return true; 852 } 853 854 // Check image for 0X500010XX series. 855 if ((i_imValue.at(2) == constants::HEX_VALUE_10) && 856 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) || 857 (l_imagePrefix == constants::powerVsImagePrefix_NZ))) 858 { 859 logging::logMessage("PowerVS configuration"); 860 return true; 861 } 862 } 863 return false; 864 } 865 866 /** 867 * @brief API to get CCIN for a given FRU from DBus. 868 * 869 * The API reads the CCIN for a FRU based on its inventory path. 870 * 871 * @param[in] i_invObjPath - Inventory path of the FRU. 872 * @param[out] o_errCode - To set error code in case of error. 873 * 874 * @return CCIN of the FRU on success, empty string otherwise. 875 */ 876 inline std::string getCcinFromDbus(const std::string& i_invObjPath, 877 uint16_t& o_errCode) 878 { 879 o_errCode = 0; 880 try 881 { 882 if (i_invObjPath.empty()) 883 { 884 o_errCode = error_code::INVALID_INPUT_PARAMETER; 885 return std::string{}; 886 } 887 888 const auto& l_retValue = dbusUtility::readDbusProperty( 889 constants::pimServiceName, i_invObjPath, constants::viniInf, 890 constants::kwdCCIN); 891 892 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue); 893 894 if (!l_ptrCcin) 895 { 896 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS; 897 return std::string{}; 898 } 899 900 if ((*l_ptrCcin).size() != constants::VALUE_4) 901 { 902 o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS; 903 return std::string{}; 904 } 905 906 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end()); 907 } 908 catch (const std::exception& l_ex) 909 { 910 o_errCode = error_code::STANDARD_EXCEPTION; 911 return std::string{}; 912 } 913 } 914 915 /** 916 * @brief API to check if the current running image is a powerVS image. 917 * 918 * @return true if it is PowerVS image, false otherwise. 919 */ 920 inline bool isPowerVsImage() 921 { 922 std::string l_imagePrefix = dbusUtility::getImagePrefix(); 923 924 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) || 925 (l_imagePrefix == constants::powerVsImagePrefix_NY) || 926 (l_imagePrefix == constants::powerVsImagePrefix_MZ) || 927 (l_imagePrefix == constants::powerVsImagePrefix_NZ)) 928 { 929 return true; 930 } 931 return false; 932 } 933 934 /** 935 * @brief API to sync keyword update to inherited FRUs. 936 * 937 * For a given keyword update on a EEPROM path, this API syncs the keyword 938 * update to all inherited FRUs' respective interface, property on PIM. 939 * 940 * @param[in] i_fruPath - EEPROM path of FRU. 941 * @param[in] i_paramsToWriteData - Input details. 942 * @param[in] i_sysCfgJsonObj - System config JSON. 943 * @param[out] o_errCode - To set error code in case of error. 944 * 945 */ 946 inline void updateKwdOnInheritedFrus( 947 const std::string& i_fruPath, 948 const types::WriteVpdParams& i_paramsToWriteData, 949 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept 950 { 951 o_errCode = 0; 952 try 953 { 954 if (i_fruPath.empty() || i_sysCfgJsonObj.empty()) 955 { 956 o_errCode = error_code::INVALID_INPUT_PARAMETER; 957 return; 958 } 959 960 if (!i_sysCfgJsonObj.contains("frus")) 961 { 962 o_errCode = error_code::INVALID_JSON; 963 return; 964 } 965 966 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath)) 967 { 968 o_errCode = error_code::FRU_PATH_NOT_FOUND; 969 return; 970 } 971 972 const types::IpzData* l_ipzData = 973 std::get_if<types::IpzData>(&i_paramsToWriteData); 974 975 if (!l_ipzData) 976 { 977 o_errCode = error_code::UNSUPPORTED_VPD_TYPE; 978 return; 979 } 980 // iterate through all inventory paths for given EEPROM path, 981 // except the base FRU. 982 // if for an inventory path, "inherit" tag is true, 983 // update the inventory path's com.ibm.ipzvpd.<record>,keyword 984 // property 985 986 types::ObjectMap l_objectInterfaceMap; 987 988 auto l_populateInterfaceMap = 989 [&l_objectInterfaceMap, 990 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) { 991 // update inherited FRUs only 992 if (l_Fru.value("inherit", true)) 993 { 994 l_objectInterfaceMap.emplace( 995 sdbusplus::message::object_path{l_Fru["inventoryPath"]}, 996 types::InterfaceMap{ 997 {std::string{constants::ipzVpdInf + 998 std::get<0>(*l_ipzData)}, 999 types::PropertyMap{{std::get<1>(*l_ipzData), 1000 std::get<2>(*l_ipzData)}}}}); 1001 } 1002 }; 1003 1004 // iterate through all FRUs except the base FRU 1005 std::for_each( 1006 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1, 1007 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap); 1008 1009 if (!l_objectInterfaceMap.empty()) 1010 { 1011 // notify PIM 1012 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1013 { 1014 o_errCode = error_code::DBUS_FAILURE; 1015 return; 1016 } 1017 } 1018 } 1019 catch (const std::exception& l_ex) 1020 { 1021 o_errCode = error_code::STANDARD_EXCEPTION; 1022 return; 1023 } 1024 } 1025 1026 /** 1027 * @brief API to get common interface(s) properties corresponding to given 1028 * record and keyword. 1029 * 1030 * For a given record and keyword, this API finds the corresponding common 1031 * interfaces(s) properties from the system config JSON and populates an 1032 * interface map with the respective properties and values. 1033 * 1034 * @param[in] i_paramsToWriteData - Input details. 1035 * @param[in] i_commonInterfaceJson - Common interface JSON object. 1036 * @param[out] o_errCode - To set error code in case of error. 1037 * 1038 * @return Returns a map of common interface(s) and properties corresponding to 1039 * the record and keyword. An empty map is returned if no such common 1040 * interface(s) and properties are found. 1041 */ 1042 inline types::InterfaceMap getCommonInterfaceProperties( 1043 const types::WriteVpdParams& i_paramsToWriteData, 1044 const nlohmann::json& i_commonInterfaceJson, uint16_t& o_errCode) noexcept 1045 { 1046 types::InterfaceMap l_interfaceMap; 1047 o_errCode = 0; 1048 try 1049 { 1050 if (i_commonInterfaceJson.empty()) 1051 { 1052 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1053 return l_interfaceMap; 1054 } 1055 1056 const types::IpzData* l_ipzData = 1057 std::get_if<types::IpzData>(&i_paramsToWriteData); 1058 1059 if (!l_ipzData) 1060 { 1061 o_errCode = error_code::UNSUPPORTED_VPD_TYPE; 1062 return l_interfaceMap; 1063 } 1064 1065 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData), 1066 &l_interfaceMap, &o_errCode]( 1067 const auto& l_interfacesPropPair) { 1068 if (l_interfacesPropPair.value().empty()) 1069 { 1070 return; 1071 } 1072 1073 // find matching property value pair 1074 const auto l_matchPropValuePairIt = std::find_if( 1075 l_interfacesPropPair.value().items().begin(), 1076 l_interfacesPropPair.value().items().end(), 1077 [&l_ipzData](const auto& l_propValuePair) { 1078 return (l_propValuePair.value().value("recordName", "") == 1079 std::get<0>(*l_ipzData) && 1080 l_propValuePair.value().value("keywordName", "") == 1081 std::get<1>(*l_ipzData)); 1082 }); 1083 1084 if (l_matchPropValuePairIt != 1085 l_interfacesPropPair.value().items().end()) 1086 { 1087 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(), 1088 std::get<2>(*l_ipzData).end()); 1089 1090 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword( 1091 l_kwd, l_matchPropValuePairIt.value().value("encoding", ""), 1092 o_errCode); 1093 1094 if (l_encodedValue.empty() && o_errCode) 1095 { 1096 logging::logMessage( 1097 "Failed to get encoded value for keyword : " + l_kwd + 1098 ", error : " + commonUtility::getErrCodeMsg(o_errCode)); 1099 } 1100 1101 // add property map to interface map 1102 l_interfaceMap.emplace( 1103 l_interfacesPropPair.key(), 1104 types::PropertyMap{ 1105 {l_matchPropValuePairIt.key(), l_encodedValue}}); 1106 } 1107 }; 1108 1109 if (!i_commonInterfaceJson.empty()) 1110 { 1111 // iterate through all common interfaces and populate interface map 1112 std::for_each(i_commonInterfaceJson.items().begin(), 1113 i_commonInterfaceJson.items().end(), 1114 l_populateInterfaceMap); 1115 } 1116 } 1117 catch (const std::exception& l_ex) 1118 { 1119 o_errCode = error_code::STANDARD_EXCEPTION; 1120 } 1121 return l_interfaceMap; 1122 } 1123 1124 /** 1125 * @brief API to update common interface(s) properties when keyword is updated. 1126 * 1127 * For a given keyword update on a EEPROM path, this API syncs the keyword 1128 * update to respective common interface(s) properties of the base FRU and all 1129 * inherited FRUs. 1130 * 1131 * @param[in] i_fruPath - EEPROM path of FRU. 1132 * @param[in] i_paramsToWriteData - Input details. 1133 * @param[in] i_sysCfgJsonObj - System config JSON. 1134 * @param[out] o_errCode - To set error code in case of error. 1135 */ 1136 inline void updateCiPropertyOfInheritedFrus( 1137 const std::string& i_fruPath, 1138 const types::WriteVpdParams& i_paramsToWriteData, 1139 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept 1140 { 1141 o_errCode = 0; 1142 try 1143 { 1144 if (i_fruPath.empty() || i_sysCfgJsonObj.empty()) 1145 { 1146 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1147 return; 1148 } 1149 1150 if (!i_sysCfgJsonObj.contains("commonInterfaces")) 1151 { 1152 // no common interfaces in JSON, nothing to do 1153 return; 1154 } 1155 1156 if (!i_sysCfgJsonObj.contains("frus")) 1157 { 1158 o_errCode = error_code::INVALID_JSON; 1159 return; 1160 } 1161 1162 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath)) 1163 { 1164 o_errCode = error_code::FRU_PATH_NOT_FOUND; 1165 return; 1166 } 1167 1168 if (!std::get_if<types::IpzData>(&i_paramsToWriteData)) 1169 { 1170 o_errCode = error_code::UNSUPPORTED_VPD_TYPE; 1171 return; 1172 } 1173 1174 // iterate through all inventory paths for given EEPROM path, 1175 // if for an inventory path, "inherit" tag is true, 1176 // update the inventory path's com.ibm.ipzvpd.<record>,keyword 1177 // property 1178 1179 types::ObjectMap l_objectInterfaceMap; 1180 1181 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties( 1182 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"], 1183 o_errCode); 1184 1185 if (l_interfaceMap.empty()) 1186 { 1187 if (o_errCode) 1188 { 1189 logging::logMessage( 1190 "Failed to get common interface property list, error : " + 1191 commonUtility::getErrCodeMsg(o_errCode)); 1192 } 1193 // nothing to do 1194 return; 1195 } 1196 1197 auto l_populateObjectInterfaceMap = 1198 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const( 1199 l_interfaceMap)](const auto& l_Fru) { 1200 if (l_Fru.value("inherit", true) && 1201 l_Fru.contains("inventoryPath")) 1202 { 1203 l_objectInterfaceMap.emplace( 1204 sdbusplus::message::object_path{l_Fru["inventoryPath"]}, 1205 l_interfaceMap); 1206 } 1207 }; 1208 1209 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(), 1210 i_sysCfgJsonObj["frus"][i_fruPath].end(), 1211 l_populateObjectInterfaceMap); 1212 1213 if (!l_objectInterfaceMap.empty()) 1214 { 1215 // notify PIM 1216 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1217 { 1218 o_errCode = error_code::DBUS_FAILURE; 1219 return; 1220 } 1221 } 1222 } 1223 catch (const std::exception& l_ex) 1224 { 1225 o_errCode = error_code::STANDARD_EXCEPTION; 1226 } 1227 } 1228 1229 /** 1230 * @brief API to convert write VPD parameters to a string. 1231 * 1232 * @param[in] i_paramsToWriteData - write VPD parameters. 1233 * @param[out] o_errCode - To set error code in case of error. 1234 * 1235 * @return On success returns string representation of write VPD parameters, 1236 * otherwise returns an empty string. 1237 */ 1238 inline const std::string convertWriteVpdParamsToString( 1239 const types::WriteVpdParams& i_paramsToWriteData, 1240 uint16_t& o_errCode) noexcept 1241 { 1242 o_errCode = 0; 1243 try 1244 { 1245 if (const types::IpzData* l_ipzDataPtr = 1246 std::get_if<types::IpzData>(&i_paramsToWriteData)) 1247 { 1248 return std::string{ 1249 "Record: " + std::get<0>(*l_ipzDataPtr) + 1250 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " + 1251 commonUtility::convertByteVectorToHex( 1252 std::get<2>(*l_ipzDataPtr))}; 1253 } 1254 else if (const types::KwData* l_kwDataPtr = 1255 std::get_if<types::KwData>(&i_paramsToWriteData)) 1256 { 1257 return std::string{ 1258 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " + 1259 commonUtility::convertByteVectorToHex( 1260 std::get<1>(*l_kwDataPtr))}; 1261 } 1262 else 1263 { 1264 o_errCode = error_code::UNSUPPORTED_VPD_TYPE; 1265 } 1266 } 1267 catch (const std::exception& l_ex) 1268 { 1269 o_errCode = error_code::STANDARD_EXCEPTION; 1270 } 1271 return std::string{}; 1272 } 1273 1274 /** 1275 * @brief An API to read IM value from VPD. 1276 * 1277 * @param[in] i_parsedVpd - Parsed VPD. 1278 * @param[out] o_errCode - To set error code in case of error. 1279 */ 1280 inline std::string getIMValue(const types::IPZVpdMap& i_parsedVpd, 1281 uint16_t& o_errCode) noexcept 1282 { 1283 o_errCode = 0; 1284 std::ostringstream l_imData; 1285 try 1286 { 1287 if (i_parsedVpd.empty()) 1288 { 1289 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1290 return {}; 1291 } 1292 1293 const auto& l_itrToVSBP = i_parsedVpd.find("VSBP"); 1294 if (l_itrToVSBP == i_parsedVpd.end()) 1295 { 1296 o_errCode = error_code::RECORD_NOT_FOUND; 1297 return {}; 1298 } 1299 1300 const auto& l_itrToIM = (l_itrToVSBP->second).find("IM"); 1301 if (l_itrToIM == (l_itrToVSBP->second).end()) 1302 { 1303 o_errCode = error_code::KEYWORD_NOT_FOUND; 1304 return {}; 1305 } 1306 1307 types::BinaryVector l_imVal; 1308 std::copy(l_itrToIM->second.begin(), l_itrToIM->second.end(), 1309 back_inserter(l_imVal)); 1310 1311 for (auto& l_aByte : l_imVal) 1312 { 1313 l_imData << std::setw(2) << std::setfill('0') << std::hex 1314 << static_cast<int>(l_aByte); 1315 } 1316 } 1317 catch (const std::exception& l_ex) 1318 { 1319 logging::logMessage("Failed to get IM value with exception:" + 1320 std::string(l_ex.what())); 1321 o_errCode = error_code::STANDARD_EXCEPTION; 1322 } 1323 1324 return l_imData.str(); 1325 } 1326 1327 /** 1328 * @brief An API to read HW version from VPD. 1329 * 1330 * @param[in] i_parsedVpd - Parsed VPD. 1331 * @param[out] o_errCode - To set error code in case of error. 1332 */ 1333 inline std::string getHWVersion(const types::IPZVpdMap& i_parsedVpd, 1334 uint16_t& o_errCode) noexcept 1335 { 1336 o_errCode = 0; 1337 std::ostringstream l_hwString; 1338 try 1339 { 1340 if (i_parsedVpd.empty()) 1341 { 1342 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1343 return {}; 1344 } 1345 1346 const auto& l_itrToVINI = i_parsedVpd.find("VINI"); 1347 if (l_itrToVINI == i_parsedVpd.end()) 1348 { 1349 o_errCode = error_code::RECORD_NOT_FOUND; 1350 return {}; 1351 } 1352 1353 const auto& l_itrToHW = (l_itrToVINI->second).find("HW"); 1354 if (l_itrToHW == (l_itrToVINI->second).end()) 1355 { 1356 o_errCode = error_code::KEYWORD_NOT_FOUND; 1357 return {}; 1358 } 1359 1360 types::BinaryVector l_hwVal; 1361 std::copy(l_itrToHW->second.begin(), l_itrToHW->second.end(), 1362 back_inserter(l_hwVal)); 1363 1364 // The planar pass only comes from the LSB of the HW keyword, 1365 // where as the MSB is used for other purposes such as signifying clock 1366 // termination. 1367 l_hwVal[0] = 0x00; 1368 1369 for (auto& l_aByte : l_hwVal) 1370 { 1371 l_hwString << std::setw(2) << std::setfill('0') << std::hex 1372 << static_cast<int>(l_aByte); 1373 } 1374 } 1375 catch (const std::exception& l_ex) 1376 { 1377 logging::logMessage("Failed to get HW version with exception:" + 1378 std::string(l_ex.what())); 1379 o_errCode = error_code::STANDARD_EXCEPTION; 1380 } 1381 1382 return l_hwString.str(); 1383 } 1384 1385 } // namespace vpdSpecificUtility 1386 } // namespace vpd 1387