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 * @param[out] o_errCode - To set error code in case of error. 637 */ 638 inline void resetDataUnderPIM(const std::string& i_objectPath, 639 types::InterfaceMap& io_interfaceMap, 640 uint16_t& o_errCode) 641 { 642 if (i_objectPath.empty()) 643 { 644 o_errCode = error_code::INVALID_INPUT_PARAMETER; 645 return; 646 } 647 648 try 649 { 650 std::vector<std::string> l_interfaces; 651 const types::MapperGetObject& l_getObjectMap = 652 dbusUtility::getObjectMap(i_objectPath, l_interfaces); 653 654 const std::vector<std::string>& l_vpdRelatedInterfaces{ 655 constants::operationalStatusInf, constants::inventoryItemInf, 656 constants::assetInf, constants::vpdCollectionInterface}; 657 658 for (const auto& [l_service, l_interfaceList] : l_getObjectMap) 659 { 660 if (l_service.compare(constants::pimServiceName) != 661 constants::STR_CMP_SUCCESS) 662 { 663 continue; 664 } 665 666 for (const auto& l_interface : l_interfaceList) 667 { 668 if ((l_interface.find(constants::ipzVpdInf) != 669 std::string::npos) || 670 ((std::find(l_vpdRelatedInterfaces.begin(), 671 l_vpdRelatedInterfaces.end(), l_interface)) != 672 l_vpdRelatedInterfaces.end())) 673 { 674 const types::PropertyMap& l_propertyValueMap = 675 dbusUtility::getPropertyMap(l_service, i_objectPath, 676 l_interface); 677 678 types::PropertyMap l_propertyMap; 679 680 for (const auto& l_aProperty : l_propertyValueMap) 681 { 682 const std::string& l_propertyName = l_aProperty.first; 683 const auto& l_propertyValue = l_aProperty.second; 684 685 if (std::holds_alternative<types::BinaryVector>( 686 l_propertyValue)) 687 { 688 l_propertyMap.emplace(l_propertyName, 689 types::BinaryVector{}); 690 } 691 else if (std::holds_alternative<std::string>( 692 l_propertyValue)) 693 { 694 if (l_propertyName.compare("Status") == 695 constants::STR_CMP_SUCCESS) 696 { 697 l_propertyMap.emplace( 698 l_propertyName, 699 constants::vpdCollectionNotStarted); 700 l_propertyMap.emplace("StartTime", 0); 701 l_propertyMap.emplace("CompletedTime", 0); 702 } 703 else 704 { 705 l_propertyMap.emplace(l_propertyName, 706 std::string{}); 707 } 708 } 709 else if (std::holds_alternative<bool>(l_propertyValue)) 710 { 711 if (l_propertyName.compare("Present") == 712 constants::STR_CMP_SUCCESS) 713 { 714 l_propertyMap.emplace(l_propertyName, false); 715 } 716 else if (l_propertyName.compare("Functional") == 717 constants::STR_CMP_SUCCESS) 718 { 719 // Since FRU is not present functional property 720 // is considered as true. 721 l_propertyMap.emplace(l_propertyName, true); 722 } 723 } 724 } 725 io_interfaceMap.emplace(l_interface, 726 std::move(l_propertyMap)); 727 } 728 } 729 } 730 } 731 catch (const std::exception& l_ex) 732 { 733 o_errCode = error_code::STANDARD_EXCEPTION; 734 } 735 } 736 737 /** 738 * @brief API to detect pass1 planar type. 739 * 740 * Based on HW version and IM keyword, This API detects is it is a pass1 planar 741 * or not. 742 * 743 * @return True if pass 1 planar, false otherwise. 744 */ 745 inline bool isPass1Planar() noexcept 746 { 747 bool l_rc{false}; 748 try 749 { 750 auto l_retVal = dbusUtility::readDbusProperty( 751 constants::pimServiceName, constants::systemVpdInvPath, 752 constants::viniInf, constants::kwdHW); 753 754 auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal); 755 756 l_retVal = dbusUtility::readDbusProperty( 757 constants::pimServiceName, constants::systemInvPath, 758 constants::vsbpInf, constants::kwdIM); 759 760 auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal); 761 762 if (l_hwVer && l_imValue) 763 { 764 if (l_hwVer->size() != constants::VALUE_2) 765 { 766 throw std::runtime_error("Invalid HW keyword length."); 767 } 768 769 if (l_imValue->size() != constants::VALUE_4) 770 { 771 throw std::runtime_error("Invalid IM keyword length."); 772 } 773 774 const types::BinaryVector l_everest{80, 00, 48, 00}; 775 const types::BinaryVector l_fuji{96, 00, 32, 00}; 776 777 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji)) 778 { 779 if ((*l_hwVer).at(1) < constants::VALUE_21) 780 { 781 l_rc = true; 782 } 783 } 784 else if ((*l_hwVer).at(1) < constants::VALUE_2) 785 { 786 l_rc = true; 787 } 788 } 789 } 790 catch (const std::exception& l_ex) 791 { 792 logging::logMessage("Failed to check for pass 1 planar. Error: " + 793 std::string(l_ex.what())); 794 } 795 796 return l_rc; 797 } 798 799 /** 800 * @brief API to detect if system configuration is that of PowerVS system. 801 * 802 * @param[in] i_imValue - IM value of the system. 803 * @param[out] o_errCode - To set error code in case of error. 804 * @return true if it is PowerVS configuration, false otherwise. 805 */ 806 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue, 807 uint16_t& o_errCode) 808 { 809 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4) 810 { 811 o_errCode = error_code::INVALID_INPUT_PARAMETER; 812 return false; 813 } 814 815 // Should be a 0x5000XX series system. 816 if (i_imValue.at(0) == constants::HEX_VALUE_50 && 817 i_imValue.at(1) == constants::HEX_VALUE_00) 818 { 819 std::string l_imagePrefix = dbusUtility::getImagePrefix(); 820 821 // Check image for 0x500030XX series. 822 if ((i_imValue.at(2) == constants::HEX_VALUE_30) && 823 ((l_imagePrefix == constants::powerVsImagePrefix_MY) || 824 (l_imagePrefix == constants::powerVsImagePrefix_NY))) 825 { 826 logging::logMessage("PowerVS configuration"); 827 return true; 828 } 829 830 // Check image for 0X500010XX series. 831 if ((i_imValue.at(2) == constants::HEX_VALUE_10) && 832 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) || 833 (l_imagePrefix == constants::powerVsImagePrefix_NZ))) 834 { 835 logging::logMessage("PowerVS configuration"); 836 return true; 837 } 838 } 839 return false; 840 } 841 842 /** 843 * @brief API to get CCIN for a given FRU from DBus. 844 * 845 * The API reads the CCIN for a FRU based on its inventory path. 846 * 847 * @param[in] i_invObjPath - Inventory path of the FRU. 848 * @return CCIN of the FRU on success, empty string otherwise. 849 */ 850 inline std::string getCcinFromDbus(const std::string& i_invObjPath) 851 { 852 try 853 { 854 if (i_invObjPath.empty()) 855 { 856 throw std::runtime_error("Empty EEPROM path, can't read CCIN"); 857 } 858 859 const auto& l_retValue = dbusUtility::readDbusProperty( 860 constants::pimServiceName, i_invObjPath, constants::viniInf, 861 constants::kwdCCIN); 862 863 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue); 864 if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4) 865 { 866 throw DbusException("Invalid CCIN read from Dbus"); 867 } 868 869 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end()); 870 } 871 catch (const std::exception& l_ex) 872 { 873 logging::logMessage(l_ex.what()); 874 return std::string{}; 875 } 876 } 877 878 /** 879 * @brief API to check if the current running image is a powerVS image. 880 * 881 * @return true if it is PowerVS image, false otherwise. 882 */ 883 inline bool isPowerVsImage() 884 { 885 std::string l_imagePrefix = dbusUtility::getImagePrefix(); 886 887 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) || 888 (l_imagePrefix == constants::powerVsImagePrefix_NY) || 889 (l_imagePrefix == constants::powerVsImagePrefix_MZ) || 890 (l_imagePrefix == constants::powerVsImagePrefix_NZ)) 891 { 892 return true; 893 } 894 return false; 895 } 896 897 /** 898 * @brief API to sync keyword update to inherited FRUs. 899 * 900 * For a given keyword update on a EEPROM path, this API syncs the keyword 901 * update to all inherited FRUs' respective interface, property on PIM. 902 * 903 * @param[in] i_fruPath - EEPROM path of FRU. 904 * @param[in] i_paramsToWriteData - Input details. 905 * @param[in] i_sysCfgJsonObj - System config JSON. 906 * 907 */ 908 inline void updateKwdOnInheritedFrus( 909 const std::string& i_fruPath, 910 const types::WriteVpdParams& i_paramsToWriteData, 911 const nlohmann::json& i_sysCfgJsonObj) noexcept 912 { 913 try 914 { 915 if (!i_sysCfgJsonObj.contains("frus")) 916 { 917 throw std::runtime_error("Mandatory tag(s) missing from JSON"); 918 } 919 920 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath)) 921 { 922 throw std::runtime_error( 923 "VPD path [" + i_fruPath + "] not found in system config JSON"); 924 } 925 926 const types::IpzData* l_ipzData = 927 std::get_if<types::IpzData>(&i_paramsToWriteData); 928 929 if (!l_ipzData) 930 { 931 throw std::runtime_error("Unsupported VPD type"); 932 } 933 // iterate through all inventory paths for given EEPROM path, 934 // except the base FRU. 935 // if for an inventory path, "inherit" tag is true, 936 // update the inventory path's com.ibm.ipzvpd.<record>,keyword 937 // property 938 939 types::ObjectMap l_objectInterfaceMap; 940 941 auto l_populateInterfaceMap = 942 [&l_objectInterfaceMap, 943 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) { 944 // update inherited FRUs only 945 if (l_Fru.value("inherit", true)) 946 { 947 l_objectInterfaceMap.emplace( 948 sdbusplus::message::object_path{l_Fru["inventoryPath"]}, 949 types::InterfaceMap{ 950 {std::string{constants::ipzVpdInf + 951 std::get<0>(*l_ipzData)}, 952 types::PropertyMap{{std::get<1>(*l_ipzData), 953 std::get<2>(*l_ipzData)}}}}); 954 } 955 }; 956 957 // iterate through all FRUs except the base FRU 958 std::for_each( 959 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1, 960 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap); 961 962 if (!l_objectInterfaceMap.empty()) 963 { 964 // notify PIM 965 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 966 { 967 throw std::runtime_error( 968 "Call to PIM failed for VPD file " + i_fruPath); 969 } 970 } 971 } 972 catch (const std::exception& l_ex) 973 { 974 logging::logMessage( 975 "Failed to sync keyword update to inherited FRUs of FRU [" + 976 i_fruPath + "]. Error: " + std::string(l_ex.what())); 977 } 978 } 979 980 /** 981 * @brief API to get common interface(s) properties corresponding to given 982 * record and keyword. 983 * 984 * For a given record and keyword, this API finds the corresponding common 985 * interfaces(s) properties from the system config JSON and populates an 986 * interface map with the respective properties and values. 987 * 988 * @param[in] i_paramsToWriteData - Input details. 989 * @param[in] i_commonInterfaceJson - Common interface JSON object. 990 * 991 * @return Returns a map of common interface(s) and properties corresponding to 992 * the record and keyword. An empty map is returned if no such common 993 * interface(s) and properties are found. 994 */ 995 inline types::InterfaceMap getCommonInterfaceProperties( 996 const types::WriteVpdParams& i_paramsToWriteData, 997 const nlohmann::json& i_commonInterfaceJson) noexcept 998 { 999 types::InterfaceMap l_interfaceMap; 1000 try 1001 { 1002 const types::IpzData* l_ipzData = 1003 std::get_if<types::IpzData>(&i_paramsToWriteData); 1004 1005 if (!l_ipzData) 1006 { 1007 throw std::runtime_error("Invalid VPD type"); 1008 } 1009 1010 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData), 1011 &l_interfaceMap]( 1012 const auto& l_interfacesPropPair) { 1013 if (l_interfacesPropPair.value().empty()) 1014 { 1015 return; 1016 } 1017 1018 // find matching property value pair 1019 const auto l_matchPropValuePairIt = std::find_if( 1020 l_interfacesPropPair.value().items().begin(), 1021 l_interfacesPropPair.value().items().end(), 1022 [&l_ipzData](const auto& l_propValuePair) { 1023 return (l_propValuePair.value().value("recordName", "") == 1024 std::get<0>(*l_ipzData) && 1025 l_propValuePair.value().value("keywordName", "") == 1026 std::get<1>(*l_ipzData)); 1027 }); 1028 1029 uint16_t l_errCode = 0; 1030 1031 if (l_matchPropValuePairIt != 1032 l_interfacesPropPair.value().items().end()) 1033 { 1034 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(), 1035 std::get<2>(*l_ipzData).end()); 1036 1037 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword( 1038 l_kwd, l_matchPropValuePairIt.value().value("encoding", ""), 1039 l_errCode); 1040 1041 if (l_errCode) 1042 { 1043 logging::logMessage( 1044 "Failed to get encoded value for key : " + l_kwd + 1045 " ,error : " + commonUtility::getErrCodeMsg(l_errCode)); 1046 } 1047 1048 // add property map to interface map 1049 l_interfaceMap.emplace( 1050 l_interfacesPropPair.key(), 1051 types::PropertyMap{ 1052 {l_matchPropValuePairIt.key(), l_encodedValue}}); 1053 } 1054 }; 1055 1056 if (!i_commonInterfaceJson.empty()) 1057 { 1058 // iterate through all common interfaces and populate interface map 1059 std::for_each(i_commonInterfaceJson.items().begin(), 1060 i_commonInterfaceJson.items().end(), 1061 l_populateInterfaceMap); 1062 } 1063 } 1064 catch (const std::exception& l_ex) 1065 { 1066 logging::logMessage( 1067 "Failed to find common interface properties. Error: " + 1068 std::string(l_ex.what())); 1069 } 1070 return l_interfaceMap; 1071 } 1072 1073 /** 1074 * @brief API to update common interface(s) properties when keyword is updated. 1075 * 1076 * For a given keyword update on a EEPROM path, this API syncs the keyword 1077 * update to respective common interface(s) properties of the base FRU and all 1078 * inherited FRUs. 1079 * 1080 * @param[in] i_fruPath - EEPROM path of FRU. 1081 * @param[in] i_paramsToWriteData - Input details. 1082 * @param[in] i_sysCfgJsonObj - System config JSON. 1083 * 1084 */ 1085 inline void updateCiPropertyOfInheritedFrus( 1086 const std::string& i_fruPath, 1087 const types::WriteVpdParams& i_paramsToWriteData, 1088 const nlohmann::json& i_sysCfgJsonObj) noexcept 1089 { 1090 try 1091 { 1092 if (!i_sysCfgJsonObj.contains("commonInterfaces")) 1093 { 1094 // no common interfaces in JSON, nothing to do 1095 return; 1096 } 1097 1098 if (!i_sysCfgJsonObj.contains("frus")) 1099 { 1100 throw std::runtime_error("Mandatory tag(s) missing from JSON"); 1101 } 1102 1103 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath)) 1104 { 1105 throw std::runtime_error( 1106 "VPD path [" + i_fruPath + "] not found in system config JSON"); 1107 } 1108 1109 if (!std::get_if<types::IpzData>(&i_paramsToWriteData)) 1110 { 1111 throw std::runtime_error("Unsupported VPD type"); 1112 } 1113 1114 // iterate through all inventory paths for given EEPROM path, 1115 // if for an inventory path, "inherit" tag is true, 1116 // update the inventory path's com.ibm.ipzvpd.<record>,keyword 1117 // property 1118 1119 types::ObjectMap l_objectInterfaceMap; 1120 1121 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties( 1122 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"]); 1123 1124 if (l_interfaceMap.empty()) 1125 { 1126 // nothing to do 1127 return; 1128 } 1129 1130 auto l_populateObjectInterfaceMap = 1131 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const( 1132 l_interfaceMap)](const auto& l_Fru) { 1133 if (l_Fru.value("inherit", true) && 1134 l_Fru.contains("inventoryPath")) 1135 { 1136 l_objectInterfaceMap.emplace( 1137 sdbusplus::message::object_path{l_Fru["inventoryPath"]}, 1138 l_interfaceMap); 1139 } 1140 }; 1141 1142 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(), 1143 i_sysCfgJsonObj["frus"][i_fruPath].end(), 1144 l_populateObjectInterfaceMap); 1145 1146 if (!l_objectInterfaceMap.empty()) 1147 { 1148 // notify PIM 1149 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1150 { 1151 throw std::runtime_error( 1152 "Call to PIM failed for VPD file " + i_fruPath); 1153 } 1154 } 1155 } 1156 catch (const std::exception& l_ex) 1157 { 1158 logging::logMessage( 1159 "Failed to update common interface properties of FRU [" + 1160 i_fruPath + "]. Error: " + std::string(l_ex.what())); 1161 } 1162 } 1163 1164 /** 1165 * @brief API to convert write VPD parameters to a string. 1166 * 1167 * @param[in] i_paramsToWriteData - write VPD parameters. 1168 * @param[out] o_errCode - To set error code in case of error. 1169 * 1170 * @return On success returns string representation of write VPD parameters, 1171 * otherwise returns an empty string. 1172 */ 1173 inline const std::string convertWriteVpdParamsToString( 1174 const types::WriteVpdParams& i_paramsToWriteData, 1175 uint16_t& o_errCode) noexcept 1176 { 1177 try 1178 { 1179 if (const types::IpzData* l_ipzDataPtr = 1180 std::get_if<types::IpzData>(&i_paramsToWriteData)) 1181 { 1182 return std::string{ 1183 "Record: " + std::get<0>(*l_ipzDataPtr) + 1184 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " + 1185 commonUtility::convertByteVectorToHex( 1186 std::get<2>(*l_ipzDataPtr))}; 1187 } 1188 else if (const types::KwData* l_kwDataPtr = 1189 std::get_if<types::KwData>(&i_paramsToWriteData)) 1190 { 1191 return std::string{ 1192 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " + 1193 commonUtility::convertByteVectorToHex( 1194 std::get<1>(*l_kwDataPtr))}; 1195 } 1196 else 1197 { 1198 o_errCode = error_code::UNSUPPORTED_VPD_TYPE; 1199 } 1200 } 1201 catch (const std::exception& l_ex) 1202 { 1203 o_errCode = error_code::STANDARD_EXCEPTION; 1204 } 1205 return std::string{}; 1206 } 1207 1208 } // namespace vpdSpecificUtility 1209 } // namespace vpd 1210