xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/vpd_specific_utility.hpp (revision c0c007de496f738e9c23e12c5246335c7a1baf49)
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                             }
632                             else
633                             {
634                                 l_propertyMap.emplace(l_propertyName,
635                                                       std::string{});
636                             }
637                         }
638                         else if (std::holds_alternative<bool>(l_propertyValue))
639                         {
640                             if (l_propertyName.compare("Present") ==
641                                 constants::STR_CMP_SUCCESS)
642                             {
643                                 l_propertyMap.emplace(l_propertyName, false);
644                             }
645                             else if (l_propertyName.compare("Functional") ==
646                                      constants::STR_CMP_SUCCESS)
647                             {
648                                 // Since FRU is not present functional property
649                                 // is considered as true.
650                                 l_propertyMap.emplace(l_propertyName, true);
651                             }
652                         }
653                     }
654                     io_interfaceMap.emplace(l_interface,
655                                             std::move(l_propertyMap));
656                 }
657             }
658         }
659     }
660     catch (const std::exception& l_ex)
661     {
662         logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath +
663                             " with error: " + std::string(l_ex.what()));
664     }
665 }
666 
667 /**
668  * @brief API to detect pass1 planar type.
669  *
670  * Based on HW version and IM keyword, This API detects is it is a pass1 planar
671  * or not.
672  *
673  * @return True if pass 1 planar, false otherwise.
674  */
675 inline bool isPass1Planar() noexcept
676 {
677     bool l_rc{false};
678     try
679     {
680         auto l_retVal = dbusUtility::readDbusProperty(
681             constants::pimServiceName, constants::systemVpdInvPath,
682             constants::viniInf, constants::kwdHW);
683 
684         auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
685 
686         l_retVal = dbusUtility::readDbusProperty(
687             constants::pimServiceName, constants::systemInvPath,
688             constants::vsbpInf, constants::kwdIM);
689 
690         auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
691 
692         if (l_hwVer && l_imValue)
693         {
694             if (l_hwVer->size() != constants::VALUE_2)
695             {
696                 throw std::runtime_error("Invalid HW keyword length.");
697             }
698 
699             if (l_imValue->size() != constants::VALUE_4)
700             {
701                 throw std::runtime_error("Invalid IM keyword length.");
702             }
703 
704             const types::BinaryVector l_everest{80, 00, 48, 00};
705             const types::BinaryVector l_fuji{96, 00, 32, 00};
706 
707             if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
708             {
709                 if ((*l_hwVer).at(1) < constants::VALUE_21)
710                 {
711                     l_rc = true;
712                 }
713             }
714             else if ((*l_hwVer).at(1) < constants::VALUE_2)
715             {
716                 l_rc = true;
717             }
718         }
719     }
720     catch (const std::exception& l_ex)
721     {
722         logging::logMessage("Failed to check for pass 1 planar. Error: " +
723                             std::string(l_ex.what()));
724     }
725 
726     return l_rc;
727 }
728 
729 /**
730  * @brief API to detect if system configuration is that of PowerVS system.
731  *
732  * @param[in] i_imValue - IM value of the system.
733  * @return true if it is PowerVS configuration, false otherwise.
734  */
735 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue)
736 {
737     if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
738     {
739         return false;
740     }
741 
742     // Should be a 0x5000XX series system.
743     if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
744         i_imValue.at(1) == constants::HEX_VALUE_00)
745     {
746         std::string l_imagePrefix = dbusUtility::getImagePrefix();
747 
748         // Check image for 0x500030XX series.
749         if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
750             ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
751              (l_imagePrefix == constants::powerVsImagePrefix_NY)))
752         {
753             logging::logMessage("PowerVS configuration");
754             return true;
755         }
756 
757         // Check image for 0X500010XX series.
758         if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
759             ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
760              (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
761         {
762             logging::logMessage("PowerVS configuration");
763             return true;
764         }
765     }
766     return false;
767 }
768 
769 /**
770  * @brief API to get CCIN for a given FRU from DBus.
771  *
772  * The API reads the CCIN for a FRU based on its inventory path.
773  *
774  * @param[in] i_invObjPath - Inventory path of the FRU.
775  * @return CCIN of the FRU on success, empty string otherwise.
776  */
777 inline std::string getCcinFromDbus(const std::string& i_invObjPath)
778 {
779     try
780     {
781         if (i_invObjPath.empty())
782         {
783             throw std::runtime_error("Empty EEPROM path, can't read CCIN");
784         }
785 
786         const auto& l_retValue = dbusUtility::readDbusProperty(
787             constants::pimServiceName, i_invObjPath, constants::viniInf,
788             constants::kwdCCIN);
789 
790         auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
791         if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4)
792         {
793             throw DbusException("Invalid CCIN read from Dbus");
794         }
795 
796         return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
797     }
798     catch (const std::exception& l_ex)
799     {
800         logging::logMessage(l_ex.what());
801         return std::string{};
802     }
803 }
804 
805 /**
806  * @brief API to check if the current running image is a powerVS image.
807  *
808  * @return true if it is PowerVS image, false otherwise.
809  */
810 inline bool isPowerVsImage()
811 {
812     std::string l_imagePrefix = dbusUtility::getImagePrefix();
813 
814     if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
815         (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
816         (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
817         (l_imagePrefix == constants::powerVsImagePrefix_NZ))
818     {
819         return true;
820     }
821     return false;
822 }
823 
824 /**
825  * @brief API to sync keyword update to inherited FRUs.
826  *
827  * For a given keyword update on a EEPROM path, this API syncs the keyword
828  * update to all inherited FRUs' respective interface, property on PIM.
829  *
830  * @param[in] i_fruPath - EEPROM path of FRU.
831  * @param[in] i_paramsToWriteData - Input details.
832  * @param[in] i_sysCfgJsonObj - System config JSON.
833  *
834  */
835 inline void updateKwdOnInheritedFrus(
836     const std::string& i_fruPath,
837     const types::WriteVpdParams& i_paramsToWriteData,
838     const nlohmann::json& i_sysCfgJsonObj) noexcept
839 {
840     try
841     {
842         if (!i_sysCfgJsonObj.contains("frus"))
843         {
844             throw std::runtime_error("Mandatory tag(s) missing from JSON");
845         }
846 
847         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
848         {
849             throw std::runtime_error(
850                 "VPD path [" + i_fruPath + "] not found in system config JSON");
851         }
852 
853         const types::IpzData* l_ipzData =
854             std::get_if<types::IpzData>(&i_paramsToWriteData);
855 
856         if (!l_ipzData)
857         {
858             throw std::runtime_error("Unsupported VPD type");
859         }
860         //  iterate through all inventory paths for given EEPROM path,
861         //  except the base FRU.
862         //  if for an inventory path, "inherit" tag is true,
863         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
864         //  property
865 
866         types::ObjectMap l_objectInterfaceMap;
867 
868         auto l_populateInterfaceMap =
869             [&l_objectInterfaceMap,
870              &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
871                 // update inherited FRUs only
872                 if (l_Fru.value("inherit", true))
873                 {
874                     l_objectInterfaceMap.emplace(
875                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
876                         types::InterfaceMap{
877                             {std::string{constants::ipzVpdInf +
878                                          std::get<0>(*l_ipzData)},
879                              types::PropertyMap{{std::get<1>(*l_ipzData),
880                                                  std::get<2>(*l_ipzData)}}}});
881                 }
882             };
883 
884         // iterate through all FRUs except the base FRU
885         std::for_each(
886             i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
887             i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
888 
889         if (!l_objectInterfaceMap.empty())
890         {
891             // notify PIM
892             if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
893             {
894                 throw std::runtime_error(
895                     "Call to PIM failed for VPD file " + i_fruPath);
896             }
897         }
898     }
899     catch (const std::exception& l_ex)
900     {
901         logging::logMessage(
902             "Failed to sync keyword update to inherited FRUs of FRU [" +
903             i_fruPath + "]. Error: " + std::string(l_ex.what()));
904     }
905 }
906 
907 /**
908  * @brief API to get common interface(s) properties corresponding to given
909  * record and keyword.
910  *
911  * For a given record and keyword, this API finds the corresponding common
912  * interfaces(s) properties from the system config JSON and populates an
913  * interface map with the respective properties and values.
914  *
915  * @param[in] i_paramsToWriteData - Input details.
916  * @param[in] i_commonInterfaceJson - Common interface JSON object.
917  *
918  * @return Returns a map of common interface(s) and properties corresponding to
919  * the record and keyword. An empty map is returned if no such common
920  * interface(s) and properties are found.
921  */
922 inline types::InterfaceMap getCommonInterfaceProperties(
923     const types::WriteVpdParams& i_paramsToWriteData,
924     const nlohmann::json& i_commonInterfaceJson) noexcept
925 {
926     types::InterfaceMap l_interfaceMap;
927     try
928     {
929         const types::IpzData* l_ipzData =
930             std::get_if<types::IpzData>(&i_paramsToWriteData);
931 
932         if (!l_ipzData)
933         {
934             throw std::runtime_error("Invalid VPD type");
935         }
936 
937         auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
938                                        &l_interfaceMap](
939                                           const auto& l_interfacesPropPair) {
940             // find matching property value pair
941             const auto l_matchPropValuePairIt = std::find_if(
942                 l_interfacesPropPair.value().items().begin(),
943                 l_interfacesPropPair.value().items().end(),
944                 [&l_ipzData](const auto& l_propValuePair) {
945                     return (l_propValuePair.value().value("recordName", "") ==
946                                 std::get<0>(*l_ipzData) &&
947                             l_propValuePair.value().value("keywordName", "") ==
948                                 std::get<1>(*l_ipzData));
949                 });
950 
951             if (l_matchPropValuePairIt !=
952                 l_interfacesPropPair.value().items().end())
953             {
954                 // add property map to interface map
955                 l_interfaceMap.emplace(
956                     l_interfacesPropPair.key(),
957                     types::PropertyMap{
958                         {l_matchPropValuePairIt.key(),
959                          vpdSpecificUtility::encodeKeyword(
960                              std::string(std::get<2>(*l_ipzData).begin(),
961                                          std::get<2>(*l_ipzData).end()),
962                              l_matchPropValuePairIt.value().value("encoding",
963                                                                   ""))}});
964             }
965         };
966 
967         if (!i_commonInterfaceJson.empty())
968         {
969             // iterate through all common interfaces and populate interface map
970             std::for_each(i_commonInterfaceJson.items().begin(),
971                           i_commonInterfaceJson.items().end(),
972                           l_populateInterfaceMap);
973         }
974     }
975     catch (const std::exception& l_ex)
976     {
977         logging::logMessage(
978             "Failed to find common interface properties. Error: " +
979             std::string(l_ex.what()));
980     }
981     return l_interfaceMap;
982 }
983 
984 /**
985  * @brief API to update common interface(s) properties when keyword is updated.
986  *
987  * For a given keyword update on a EEPROM path, this API syncs the keyword
988  * update to respective common interface(s) properties of the base FRU and all
989  * inherited FRUs.
990  *
991  * @param[in] i_fruPath - EEPROM path of FRU.
992  * @param[in] i_paramsToWriteData - Input details.
993  * @param[in] i_sysCfgJsonObj - System config JSON.
994  *
995  */
996 inline void updateCiPropertyOfInheritedFrus(
997     const std::string& i_fruPath,
998     const types::WriteVpdParams& i_paramsToWriteData,
999     const nlohmann::json& i_sysCfgJsonObj) noexcept
1000 {
1001     try
1002     {
1003         if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1004         {
1005             // no common interfaces in JSON, nothing to do
1006             return;
1007         }
1008 
1009         if (!i_sysCfgJsonObj.contains("frus"))
1010         {
1011             throw std::runtime_error("Mandatory tag(s) missing from JSON");
1012         }
1013 
1014         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1015         {
1016             throw std::runtime_error(
1017                 "VPD path [" + i_fruPath + "] not found in system config JSON");
1018         }
1019 
1020         if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1021         {
1022             throw std::runtime_error("Unsupported VPD type");
1023         }
1024 
1025         //  iterate through all inventory paths for given EEPROM path,
1026         //  if for an inventory path, "inherit" tag is true,
1027         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
1028         //  property
1029 
1030         types::ObjectMap l_objectInterfaceMap;
1031 
1032         const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1033             i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"]);
1034 
1035         if (l_interfaceMap.empty())
1036         {
1037             // nothing to do
1038             return;
1039         }
1040 
1041         auto l_populateObjectInterfaceMap =
1042             [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1043                                         l_interfaceMap)](const auto& l_Fru) {
1044                 if (l_Fru.value("inherit", true) &&
1045                     l_Fru.contains("inventoryPath"))
1046                 {
1047                     l_objectInterfaceMap.emplace(
1048                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1049                         l_interfaceMap);
1050                 }
1051             };
1052 
1053         std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1054                       i_sysCfgJsonObj["frus"][i_fruPath].end(),
1055                       l_populateObjectInterfaceMap);
1056 
1057         if (!l_objectInterfaceMap.empty())
1058         {
1059             // notify PIM
1060             if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1061             {
1062                 throw std::runtime_error(
1063                     "Call to PIM failed for VPD file " + i_fruPath);
1064             }
1065         }
1066     }
1067     catch (const std::exception& l_ex)
1068     {
1069         logging::logMessage(
1070             "Failed to update common interface properties of FRU [" +
1071             i_fruPath + "]. Error: " + std::string(l_ex.what()));
1072     }
1073 }
1074 
1075 /**
1076  * @brief API to save current time stamp in PIM.
1077  *
1078  * This API will capture current time stamp and save it in progress interface
1079  * for the given inventory path.
1080  *
1081  * @param[in] i_inventoryPath - Inventory path of FRU.
1082  * @param[in] i_property - Property to save the time.
1083  */
1084 inline void saveTimeStampInPim(const std::string& i_inventoryPath,
1085                                const std::string& i_property) noexcept
1086 {
1087     if (i_inventoryPath.empty() || i_property.empty())
1088     {
1089         logging::logMessage("Invalid input parameter. Can't save time in PIM.");
1090         return;
1091     }
1092 
1093     try
1094     {
1095         types::ObjectMap l_ObjMap = {std::make_pair(
1096             i_inventoryPath,
1097             types::InterfaceMap{std::make_pair(
1098                 constants::vpdCollectionInterface,
1099                 types::PropertyMap{std::make_pair(
1100                     i_property,
1101                     types::DbusVariantType{
1102                         commonUtility::getCurrentTimeSinceEpoch()})})})};
1103 
1104         if (!dbusUtility::callPIM(move(l_ObjMap)))
1105         {
1106             logging::logMessage(
1107                 "Call to PIM failed while saving time for path " +
1108                 i_inventoryPath);
1109         }
1110     }
1111     catch (const std::exception& l_ex)
1112     {
1113         logging::logMessage("Failed to save time stamp under PIM for reason: " +
1114                             std::string(l_ex.what()));
1115     }
1116 }
1117 
1118 /**
1119  * @brief API to get error code message.
1120  *
1121  * @param[in] i_errCode - error code.
1122  *
1123  * @return Error message set for that error code. Otherwise empty
1124  * string.
1125  */
1126 inline std::string getErrCodeMsg(const uint16_t& i_errCode)
1127 {
1128     if (error_code::errorCodeMap.find(i_errCode) !=
1129         error_code::errorCodeMap.end())
1130     {
1131         return error_code::errorCodeMap.at(i_errCode);
1132     }
1133 
1134     return std::string{};
1135 }
1136 } // namespace vpdSpecificUtility
1137 } // namespace vpd
1138