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