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