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