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