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