xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/vpd_specific_utility.hpp (revision d3662226ea3d25bae316acd4387c3ee26fcee3b9)
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  */
generateBadVPDFileName(const std::string & i_vpdFilePath,uint16_t & o_errCode)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  */
dumpBadVpd(const std::string & i_vpdFilePath,const types::BinaryVector & i_vpdVector,uint16_t & o_errCode)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  */
getKwVal(const types::IPZKwdValueMap & i_kwdValueMap,const std::string & i_kwd,uint16_t & o_errCode)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  */
encodeKeyword(const std::string & i_keyword,const std::string & i_encoding,uint16_t & o_errCode)193 inline std::string encodeKeyword(const std::string& i_keyword,
194                                  const std::string& i_encoding,
195                                  uint16_t& o_errCode) noexcept
196 {
197     if (i_keyword.empty())
198     {
199         o_errCode = error_code::INVALID_INPUT_PARAMETER;
200         return std::string{};
201     }
202 
203     // Default value is keyword value
204     std::string l_result(i_keyword.begin(), i_keyword.end());
205 
206     if (i_encoding.empty())
207     {
208         return l_result;
209     }
210 
211     try
212     {
213         if (i_encoding == "MAC")
214         {
215             l_result.clear();
216             size_t l_firstByte = i_keyword[0];
217 
218             auto l_hexValue = commonUtility::toHex(l_firstByte >> 4);
219 
220             if (!l_hexValue)
221             {
222                 o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
223                 return std::string{};
224             }
225 
226             l_result += l_hexValue;
227 
228             l_hexValue = commonUtility::toHex(l_firstByte & 0x0f);
229 
230             if (!l_hexValue)
231             {
232                 o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
233                 return std::string{};
234             }
235 
236             l_result += l_hexValue;
237 
238             for (size_t i = 1; i < i_keyword.size(); ++i)
239             {
240                 l_result += ":";
241 
242                 l_hexValue = commonUtility::toHex(i_keyword[i] >> 4);
243 
244                 if (!l_hexValue)
245                 {
246                     o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
247                     return std::string{};
248                 }
249 
250                 l_result += l_hexValue;
251 
252                 l_hexValue = commonUtility::toHex(i_keyword[i] & 0x0f);
253 
254                 if (!l_hexValue)
255                 {
256                     o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
257                     return std::string{};
258                 }
259 
260                 l_result += l_hexValue;
261             }
262         }
263         else if (i_encoding == "DATE")
264         {
265             // Date, represent as
266             // <year>-<month>-<day> <hour>:<min>
267             l_result.clear();
268             static constexpr uint8_t skipPrefix = 3;
269 
270             auto strItr = i_keyword.begin();
271             advance(strItr, skipPrefix);
272             for_each(strItr, i_keyword.end(),
273                      [&l_result](size_t c) { l_result += c; });
274 
275             l_result.insert(constants::BD_YEAR_END, 1, '-');
276             l_result.insert(constants::BD_MONTH_END, 1, '-');
277             l_result.insert(constants::BD_DAY_END, 1, ' ');
278             l_result.insert(constants::BD_HOUR_END, 1, ':');
279         }
280     }
281     catch (const std::exception& l_ex)
282     {
283         l_result.clear();
284         o_errCode = error_code::STANDARD_EXCEPTION;
285     }
286 
287     return l_result;
288 }
289 
290 /**
291  * @brief Helper function to insert or merge in map.
292  *
293  * This method checks in an interface if the given interface exists. If the
294  * interface key already exists, property map is inserted corresponding to it.
295  * If the key does'nt exist then given interface and property map pair is newly
296  * created. If the property present in propertymap already exist in the
297  * InterfaceMap, then the new property value is ignored.
298  *
299  * @param[in,out] io_map - Interface map.
300  * @param[in] i_interface - Interface to be processed.
301  * @param[in] i_propertyMap - new property map that needs to be emplaced.
302  * @param[out] o_errCode - To set error code in case of error.
303  *
304  * @return On success returns 0, otherwise returns -1.
305  */
insertOrMerge(types::InterfaceMap & io_map,const std::string & i_interface,types::PropertyMap && i_propertyMap,uint16_t & o_errCode)306 inline int insertOrMerge(types::InterfaceMap& io_map,
307                          const std::string& i_interface,
308                          types::PropertyMap&& i_propertyMap,
309                          uint16_t& o_errCode) noexcept
310 {
311     int l_rc{constants::FAILURE};
312 
313     try
314     {
315         if (io_map.find(i_interface) != io_map.end())
316         {
317             auto& l_prop = io_map.at(i_interface);
318             std::for_each(i_propertyMap.begin(), i_propertyMap.end(),
319                           [&l_prop](auto l_keyValue) {
320                               l_prop[l_keyValue.first] = l_keyValue.second;
321                           });
322         }
323         else
324         {
325             io_map.emplace(i_interface, i_propertyMap);
326         }
327 
328         l_rc = constants::SUCCESS;
329     }
330     catch (const std::exception& l_ex)
331     {
332         o_errCode = error_code::STANDARD_EXCEPTION;
333     }
334     return l_rc;
335 }
336 
337 /**
338  * @brief API to expand unpanded location code.
339  *
340  * Note: The API handles all the exception internally, in case of any error
341  * unexpanded location code will be returned as it is.
342  *
343  * @param[in] unexpandedLocationCode - Unexpanded location code.
344  * @param[in] parsedVpdMap - Parsed VPD map.
345  * @return Expanded location code. In case of any error, unexpanded is returned
346  * as it is.
347  */
getExpandedLocationCode(const std::string & unexpandedLocationCode,const types::VPDMapVariant & parsedVpdMap)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  */
getVpdDataInVector(const std::string & vpdFilePath,types::BinaryVector & vpdVector,size_t & vpdStartOffset)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  */
getDbusPropNameForGivenKw(const std::string & i_keywordName)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  */
findCcinInVpd(const nlohmann::json & i_JsonObject,const types::VPDMapVariant & i_parsedVpdMap)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  */
resetDataUnderPIM(const std::string & i_objectPath,types::InterfaceMap & io_interfaceMap,uint16_t & o_errCode)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  */
isPass1Planar()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  */
isPowerVsConfiguration(const types::BinaryVector & i_imValue,uint16_t & o_errCode)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  */
getCcinFromDbus(const std::string & i_invObjPath)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  */
isPowerVsImage()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  */
updateKwdOnInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj)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  */
getCommonInterfaceProperties(const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_commonInterfaceJson)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  */
updateCiPropertyOfInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj)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  */
convertWriteVpdParamsToString(const types::WriteVpdParams & i_paramsToWriteData,uint16_t & o_errCode)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