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