xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/vpd_specific_utility.hpp (revision a55fcca1879010cad2279c429b24117ab220ef4f)
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  *
33  * @return On success, returns generated file name, otherwise returns empty
34  * string.
35  */
generateBadVPDFileName(const std::string & i_vpdFilePath)36 inline std::string generateBadVPDFileName(
37     const std::string& i_vpdFilePath) noexcept
38 {
39     std::string l_badVpdFileName{BAD_VPD_DIR};
40     try
41     {
42         if (i_vpdFilePath.find("i2c") != std::string::npos)
43         {
44             l_badVpdFileName += "i2c-";
45             std::regex l_i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
46             std::smatch l_match;
47             if (std::regex_search(i_vpdFilePath, l_match, l_i2cPattern))
48             {
49                 l_badVpdFileName += l_match.str(2);
50             }
51         }
52         else if (i_vpdFilePath.find("spi") != std::string::npos)
53         {
54             std::regex l_spiPattern("((spi)[0-9]+)(.0)");
55             std::smatch l_match;
56             if (std::regex_search(i_vpdFilePath, l_match, l_spiPattern))
57             {
58                 l_badVpdFileName += l_match.str(1);
59             }
60         }
61     }
62     catch (const std::exception& l_ex)
63     {
64         l_badVpdFileName.clear();
65         logging::logMessage("Failed to generate bad VPD file name for [" +
66                             i_vpdFilePath + "]. Error: " + l_ex.what());
67     }
68     return l_badVpdFileName;
69 }
70 
71 /**
72  * @brief API which dumps the broken/bad vpd in a directory.
73  * When the vpd is bad, this API places  the bad vpd file inside
74  * "/tmp/bad-vpd" in BMC, in order to collect bad VPD data as a part of user
75  * initiated BMC dump.
76  *
77  *
78  * @param[in] i_vpdFilePath - vpd file path
79  * @param[in] i_vpdVector - vpd vector
80  *
81  * @return On success returns 0, otherwise returns -1.
82  */
dumpBadVpd(const std::string & i_vpdFilePath,const types::BinaryVector & i_vpdVector)83 inline int dumpBadVpd(const std::string& i_vpdFilePath,
84                       const types::BinaryVector& i_vpdVector) noexcept
85 {
86     int l_rc{constants::FAILURE};
87     try
88     {
89         std::filesystem::create_directory(BAD_VPD_DIR);
90         auto l_badVpdPath = generateBadVPDFileName(i_vpdFilePath);
91 
92         if (l_badVpdPath.empty())
93         {
94             throw std::runtime_error("Failed to generate bad VPD file name");
95         }
96 
97         if (std::filesystem::exists(l_badVpdPath))
98         {
99             std::error_code l_ec;
100             std::filesystem::remove(l_badVpdPath, l_ec);
101             if (l_ec) // error code
102             {
103                 const std::string l_errorMsg{
104                     "Error removing the existing broken vpd in " +
105                     l_badVpdPath +
106                     ". Error code : " + std::to_string(l_ec.value()) +
107                     ". Error message : " + l_ec.message()};
108 
109                 throw std::runtime_error(l_errorMsg);
110             }
111         }
112 
113         std::ofstream l_badVpdFileStream(l_badVpdPath, std::ofstream::binary);
114         if (!l_badVpdFileStream.is_open())
115         {
116             throw std::runtime_error(
117                 "Failed to open bad vpd file path in /tmp/bad-vpd. "
118                 "Unable to dump the broken/bad vpd file.");
119         }
120 
121         l_badVpdFileStream.write(
122             reinterpret_cast<const char*>(i_vpdVector.data()),
123             i_vpdVector.size());
124 
125         l_rc = constants::SUCCESS;
126     }
127     catch (const std::exception& l_ex)
128     {
129         logging::logMessage("Failed to dump bad VPD for [" + i_vpdFilePath +
130                             "]. Error: " + l_ex.what());
131     }
132     return l_rc;
133 }
134 
135 /**
136  * @brief An API to read value of a keyword.
137  *
138  *
139  * @param[in] i_kwdValueMap - A map having Kwd value pair.
140  * @param[in] i_kwd - keyword name.
141  *
142  * @return On success returns value of the keyword read from map, otherwise
143  * returns empty string.
144  */
getKwVal(const types::IPZKwdValueMap & i_kwdValueMap,const std::string & i_kwd)145 inline std::string getKwVal(const types::IPZKwdValueMap& i_kwdValueMap,
146                             const std::string& i_kwd) noexcept
147 {
148     std::string l_kwdValue;
149     try
150     {
151         if (i_kwd.empty())
152         {
153             throw std::runtime_error("Invalid parameters");
154         }
155 
156         auto l_itrToKwd = i_kwdValueMap.find(i_kwd);
157         if (l_itrToKwd != i_kwdValueMap.end())
158         {
159             l_kwdValue = l_itrToKwd->second;
160         }
161         else
162         {
163             throw std::runtime_error("Keyword not found");
164         }
165     }
166     catch (const std::exception& l_ex)
167     {
168         logging::logMessage("Failed to get value for keyword [" + i_kwd +
169                             "]. Error : " + l_ex.what());
170     }
171     return l_kwdValue;
172 }
173 
174 /**
175  * @brief An API to process encoding of a keyword.
176  *
177  * @param[in] i_keyword - Keyword to be processed.
178  * @param[in] i_encoding - Type of encoding.
179  *
180  * @return Value after being processed for encoded type.
181  */
encodeKeyword(const std::string & i_keyword,const std::string & i_encoding)182 inline std::string encodeKeyword(const std::string& i_keyword,
183                                  const std::string& i_encoding) noexcept
184 {
185     // Default value is keyword value
186     std::string l_result(i_keyword.begin(), i_keyword.end());
187     try
188     {
189         if (i_encoding == "MAC")
190         {
191             l_result.clear();
192             size_t l_firstByte = i_keyword[0];
193             l_result += commonUtility::toHex(l_firstByte >> 4);
194             l_result += commonUtility::toHex(l_firstByte & 0x0f);
195             for (size_t i = 1; i < i_keyword.size(); ++i)
196             {
197                 l_result += ":";
198                 l_result += commonUtility::toHex(i_keyword[i] >> 4);
199                 l_result += commonUtility::toHex(i_keyword[i] & 0x0f);
200             }
201         }
202         else if (i_encoding == "DATE")
203         {
204             // Date, represent as
205             // <year>-<month>-<day> <hour>:<min>
206             l_result.clear();
207             static constexpr uint8_t skipPrefix = 3;
208 
209             auto strItr = i_keyword.begin();
210             advance(strItr, skipPrefix);
211             for_each(strItr, i_keyword.end(),
212                      [&l_result](size_t c) { l_result += c; });
213 
214             l_result.insert(constants::BD_YEAR_END, 1, '-');
215             l_result.insert(constants::BD_MONTH_END, 1, '-');
216             l_result.insert(constants::BD_DAY_END, 1, ' ');
217             l_result.insert(constants::BD_HOUR_END, 1, ':');
218         }
219     }
220     catch (const std::exception& l_ex)
221     {
222         l_result.clear();
223         logging::logMessage("Failed to encode keyword [" + i_keyword +
224                             "]. Error: " + l_ex.what());
225     }
226 
227     return l_result;
228 }
229 
230 /**
231  * @brief Helper function to insert or merge in map.
232  *
233  * This method checks in an interface if the given interface exists. If the
234  * interface key already exists, property map is inserted corresponding to it.
235  * If the key does'nt exist then given interface and property map pair is newly
236  * created. If the property present in propertymap already exist in the
237  * InterfaceMap, then the new property value is ignored.
238  *
239  * @param[in,out] io_map - Interface map.
240  * @param[in] i_interface - Interface to be processed.
241  * @param[in] i_propertyMap - new property map that needs to be emplaced.
242  *
243  * @return On success returns 0, otherwise returns -1.
244  */
insertOrMerge(types::InterfaceMap & io_map,const std::string & i_interface,types::PropertyMap && i_propertyMap)245 inline int insertOrMerge(types::InterfaceMap& io_map,
246                          const std::string& i_interface,
247                          types::PropertyMap&& i_propertyMap) noexcept
248 {
249     int l_rc{constants::FAILURE};
250     try
251     {
252         if (io_map.find(i_interface) != io_map.end())
253         {
254             auto& l_prop = io_map.at(i_interface);
255             std::for_each(i_propertyMap.begin(), i_propertyMap.end(),
256                           [&l_prop](auto l_keyValue) {
257                               l_prop[l_keyValue.first] = l_keyValue.second;
258                           });
259         }
260         else
261         {
262             io_map.emplace(i_interface, i_propertyMap);
263         }
264 
265         l_rc = constants::SUCCESS;
266     }
267     catch (const std::exception& l_ex)
268     {
269         // ToDo:: Log PEL
270         logging::logMessage(
271             "Inserting properties into interface[" + i_interface +
272             "] map failed, reason: " + std::string(l_ex.what()));
273     }
274     return l_rc;
275 }
276 
277 /**
278  * @brief API to expand unpanded location code.
279  *
280  * Note: The API handles all the exception internally, in case of any error
281  * unexpanded location code will be returned as it is.
282  *
283  * @param[in] unexpandedLocationCode - Unexpanded location code.
284  * @param[in] parsedVpdMap - Parsed VPD map.
285  * @return Expanded location code. In case of any error, unexpanded is returned
286  * as it is.
287  */
getExpandedLocationCode(const std::string & unexpandedLocationCode,const types::VPDMapVariant & parsedVpdMap)288 inline std::string getExpandedLocationCode(
289     const std::string& unexpandedLocationCode,
290     const types::VPDMapVariant& parsedVpdMap)
291 {
292     auto expanded{unexpandedLocationCode};
293 
294     try
295     {
296         // Expanded location code is formed by combining two keywords
297         // depending on type in unexpanded one. Second one is always "SE".
298         std::string kwd1, kwd2{constants::kwdSE};
299 
300         // interface to search for required keywords;
301         std::string kwdInterface;
302 
303         // record which holds the required keywords.
304         std::string recordName;
305 
306         auto pos = unexpandedLocationCode.find("fcs");
307         if (pos != std::string::npos)
308         {
309             kwd1 = constants::kwdFC;
310             kwdInterface = constants::vcenInf;
311             recordName = constants::recVCEN;
312         }
313         else
314         {
315             pos = unexpandedLocationCode.find("mts");
316             if (pos != std::string::npos)
317             {
318                 kwd1 = constants::kwdTM;
319                 kwdInterface = constants::vsysInf;
320                 recordName = constants::recVSYS;
321             }
322             else
323             {
324                 throw std::runtime_error(
325                     "Error detecting type of unexpanded location code.");
326             }
327         }
328 
329         std::string firstKwdValue, secondKwdValue;
330 
331         if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
332             ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
333         {
334             auto itrToVCEN = (*ipzVpdMap).find(recordName);
335             firstKwdValue = getKwVal(itrToVCEN->second, kwd1);
336             if (firstKwdValue.empty())
337             {
338                 throw std::runtime_error(
339                     "Failed to get value for keyword [" + kwd1 + "]");
340             }
341 
342             secondKwdValue = getKwVal(itrToVCEN->second, kwd2);
343             if (secondKwdValue.empty())
344             {
345                 throw std::runtime_error(
346                     "Failed to get value for keyword [" + kwd2 + "]");
347             }
348         }
349         else
350         {
351             std::array<const char*, 1> interfaceList = {kwdInterface.c_str()};
352 
353             types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
354                 std::string(constants::systemVpdInvPath), interfaceList);
355 
356             if (mapperRetValue.empty())
357             {
358                 throw std::runtime_error("Mapper failed to get service");
359             }
360 
361             const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
362 
363             auto retVal = dbusUtility::readDbusProperty(
364                 serviceName, std::string(constants::systemVpdInvPath),
365                 kwdInterface, kwd1);
366 
367             if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
368             {
369                 firstKwdValue.assign(
370                     reinterpret_cast<const char*>(kwdVal->data()),
371                     kwdVal->size());
372             }
373             else
374             {
375                 throw std::runtime_error(
376                     "Failed to read value of " + kwd1 + " from Bus");
377             }
378 
379             retVal = dbusUtility::readDbusProperty(
380                 serviceName, std::string(constants::systemVpdInvPath),
381                 kwdInterface, kwd2);
382 
383             if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
384             {
385                 secondKwdValue.assign(
386                     reinterpret_cast<const char*>(kwdVal->data()),
387                     kwdVal->size());
388             }
389             else
390             {
391                 throw std::runtime_error(
392                     "Failed to read value of " + kwd2 + " from Bus");
393             }
394         }
395 
396         if (unexpandedLocationCode.find("fcs") != std::string::npos)
397         {
398             // TODO: See if ND0 can be placed in the JSON
399             expanded.replace(
400                 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
401         }
402         else
403         {
404             replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
405             expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
406         }
407     }
408     catch (const std::exception& ex)
409     {
410         logging::logMessage("Failed to expand location code with exception: " +
411                             std::string(ex.what()));
412     }
413 
414     return expanded;
415 }
416 
417 /**
418  * @brief An API to get VPD in a vector.
419  *
420  * The vector is required by the respective parser to fill the VPD map.
421  * Note: API throws exception in case of failure. Caller needs to handle.
422  *
423  * @param[in] vpdFilePath - EEPROM path of the FRU.
424  * @param[out] vpdVector - VPD in vector form.
425  * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
426  */
getVpdDataInVector(const std::string & vpdFilePath,types::BinaryVector & vpdVector,size_t & vpdStartOffset)427 inline void getVpdDataInVector(const std::string& vpdFilePath,
428                                types::BinaryVector& vpdVector,
429                                size_t& vpdStartOffset)
430 {
431     try
432     {
433         std::fstream vpdFileStream;
434         vpdFileStream.exceptions(
435             std::ifstream::badbit | std::ifstream::failbit);
436         vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
437         auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
438                                       static_cast<uintmax_t>(65504));
439         vpdVector.resize(vpdSizeToRead);
440 
441         vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
442         vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
443                            vpdSizeToRead);
444 
445         vpdVector.resize(vpdFileStream.gcount());
446         vpdFileStream.clear(std::ios_base::eofbit);
447     }
448     catch (const std::ifstream::failure& fail)
449     {
450         std::cerr << "Exception in file handling [" << vpdFilePath
451                   << "] error : " << fail.what();
452         throw;
453     }
454 }
455 
456 /**
457  * @brief An API to get D-bus representation of given VPD keyword.
458  *
459  * @param[in] i_keywordName - VPD keyword name.
460  *
461  * @return D-bus representation of given keyword.
462  */
getDbusPropNameForGivenKw(const std::string & i_keywordName)463 inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName)
464 {
465     // Check for "#" prefixed VPD keyword.
466     if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
467         (i_keywordName.at(0) == constants::POUND_KW))
468     {
469         // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
470         // prefixed keywords.
471         return (std::string(constants::POUND_KW_PREFIX) +
472                 i_keywordName.substr(1));
473     }
474 
475     // Return the keyword name back, if D-bus representation is same as the VPD
476     // keyword name.
477     return i_keywordName;
478 }
479 
480 /**
481  * @brief API to find CCIN in parsed VPD map.
482  *
483  * Few FRUs need some special handling. To identify those FRUs CCIN are used.
484  * The API will check from parsed VPD map if the FRU is the one with desired
485  * CCIN.
486  *
487  * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
488  * @param[in] i_parsedVpdMap - Parsed VPD map.
489  *
490  * @return True if found, false otherwise.
491  */
findCcinInVpd(const nlohmann::json & i_JsonObject,const types::VPDMapVariant & i_parsedVpdMap)492 inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
493                           const types::VPDMapVariant& i_parsedVpdMap) noexcept
494 {
495     bool l_rc{false};
496     try
497     {
498         if (i_JsonObject.empty())
499         {
500             throw std::runtime_error("Json object is empty. Can't find CCIN");
501         }
502 
503         if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
504         {
505             auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
506             if (l_itrToRec == (*l_ipzVPDMap).end())
507             {
508                 throw DataException(
509                     "VINI record not found in parsed VPD. Can't find CCIN");
510             }
511 
512             std::string l_ccinFromVpd{
513                 vpdSpecificUtility::getKwVal(l_itrToRec->second, "CC")};
514             if (l_ccinFromVpd.empty())
515             {
516                 throw DataException(
517                     "Empty CCIN value in VPD map. Can't find CCIN");
518             }
519 
520             transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
521                       l_ccinFromVpd.begin(), ::toupper);
522 
523             for (std::string l_ccinValue : i_JsonObject["ccin"])
524             {
525                 transform(l_ccinValue.begin(), l_ccinValue.end(),
526                           l_ccinValue.begin(), ::toupper);
527 
528                 if (l_ccinValue.compare(l_ccinFromVpd) ==
529                     constants::STR_CMP_SUCCESS)
530                 {
531                     // CCIN found
532                     l_rc = true;
533                 }
534             }
535 
536             if (!l_rc)
537             {
538                 logging::logMessage("No match found for CCIN");
539             }
540         }
541         else
542         {
543             logging::logMessage("VPD type not supported. Can't find CCIN");
544         }
545     }
546     catch (const std::exception& l_ex)
547     {
548         const std::string l_errMsg{
549             "Failed to find CCIN in VPD. Error : " + std::string(l_ex.what())};
550 
551         if (typeid(l_ex) == std::type_index(typeid(DataException)))
552         {
553             EventLogger::createSyncPel(
554                 types::ErrorType::InvalidVpdMessage,
555                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
556                 l_errMsg, std::nullopt, std::nullopt, std::nullopt,
557                 std::nullopt);
558         }
559 
560         logging::logMessage(l_errMsg);
561     }
562     return l_rc;
563 }
564 
565 /**
566  * @brief API to reset data of a FRU populated under PIM.
567  *
568  * This API resets the data for particular interfaces of a FRU under PIM.
569  *
570  * @param[in] i_objectPath - DBus object path of the FRU.
571  * @param[in] io_interfaceMap - Interface and its properties map.
572  */
resetDataUnderPIM(const std::string & i_objectPath,types::InterfaceMap & io_interfaceMap)573 inline void resetDataUnderPIM(const std::string& i_objectPath,
574                               types::InterfaceMap& io_interfaceMap)
575 {
576     try
577     {
578         std::array<const char*, 0> l_interfaces;
579         const types::MapperGetObject& l_getObjectMap =
580             dbusUtility::getObjectMap(i_objectPath, l_interfaces);
581 
582         const std::vector<std::string>& l_vpdRelatedInterfaces{
583             constants::operationalStatusInf, constants::inventoryItemInf,
584             constants::assetInf};
585 
586         for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
587         {
588             if (l_service.compare(constants::pimServiceName) !=
589                 constants::STR_CMP_SUCCESS)
590             {
591                 continue;
592             }
593 
594             for (const auto& l_interface : l_interfaceList)
595             {
596                 if ((l_interface.find(constants::ipzVpdInf) !=
597                      std::string::npos) ||
598                     ((std::find(l_vpdRelatedInterfaces.begin(),
599                                 l_vpdRelatedInterfaces.end(), l_interface)) !=
600                      l_vpdRelatedInterfaces.end()))
601                 {
602                     const types::PropertyMap& l_propertyValueMap =
603                         dbusUtility::getPropertyMap(l_service, i_objectPath,
604                                                     l_interface);
605 
606                     types::PropertyMap l_propertyMap;
607 
608                     for (const auto& l_aProperty : l_propertyValueMap)
609                     {
610                         const std::string& l_propertyName = l_aProperty.first;
611                         const auto& l_propertyValue = l_aProperty.second;
612 
613                         if (std::holds_alternative<types::BinaryVector>(
614                                 l_propertyValue))
615                         {
616                             l_propertyMap.emplace(l_propertyName,
617                                                   types::BinaryVector{});
618                         }
619                         else if (std::holds_alternative<std::string>(
620                                      l_propertyValue))
621                         {
622                             l_propertyMap.emplace(l_propertyName,
623                                                   std::string{});
624                         }
625                         else if (std::holds_alternative<bool>(l_propertyValue))
626                         {
627                             // ToDo -- Update the functional status property
628                             // to true.
629                             if (l_propertyName.compare("Present") ==
630                                 constants::STR_CMP_SUCCESS)
631                             {
632                                 l_propertyMap.emplace(l_propertyName, false);
633                             }
634                         }
635                     }
636                     io_interfaceMap.emplace(l_interface,
637                                             std::move(l_propertyMap));
638                 }
639             }
640         }
641     }
642     catch (const std::exception& l_ex)
643     {
644         logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath +
645                             " with error: " + std::string(l_ex.what()));
646     }
647 }
648 
649 /**
650  * @brief API to detect pass1 planar type.
651  *
652  * Based on HW version and IM keyword, This API detects is it is a pass1 planar
653  * or not.
654  *
655  * @return True if pass 1 planar, false otherwise.
656  */
isPass1Planar()657 inline bool isPass1Planar() noexcept
658 {
659     bool l_rc{false};
660     try
661     {
662         auto l_retVal = dbusUtility::readDbusProperty(
663             constants::pimServiceName, constants::systemVpdInvPath,
664             constants::viniInf, constants::kwdHW);
665 
666         auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
667 
668         l_retVal = dbusUtility::readDbusProperty(
669             constants::pimServiceName, constants::systemInvPath,
670             constants::vsbpInf, constants::kwdIM);
671 
672         auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
673 
674         if (l_hwVer && l_imValue)
675         {
676             if (l_hwVer->size() != constants::VALUE_2)
677             {
678                 throw std::runtime_error("Invalid HW keyword length.");
679             }
680 
681             if (l_imValue->size() != constants::VALUE_4)
682             {
683                 throw std::runtime_error("Invalid IM keyword length.");
684             }
685 
686             const types::BinaryVector l_everest{80, 00, 48, 00};
687             const types::BinaryVector l_fuji{96, 00, 32, 00};
688 
689             if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
690             {
691                 if ((*l_hwVer).at(1) < constants::VALUE_21)
692                 {
693                     l_rc = true;
694                 }
695             }
696             else if ((*l_hwVer).at(1) < constants::VALUE_2)
697             {
698                 l_rc = true;
699             }
700         }
701     }
702     catch (const std::exception& l_ex)
703     {
704         logging::logMessage("Failed to check for pass 1 planar. Error: " +
705                             std::string(l_ex.what()));
706     }
707 
708     return l_rc;
709 }
710 
711 /**
712  * @brief API to detect if system configuration is that of PowerVS system.
713  *
714  * @param[in] i_imValue - IM value of the system.
715  * @return true if it is PowerVS configuration, false otherwise.
716  */
isPowerVsConfiguration(const types::BinaryVector & i_imValue)717 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue)
718 {
719     if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
720     {
721         return false;
722     }
723 
724     // Should be a 0x5000XX series system.
725     if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
726         i_imValue.at(1) == constants::HEX_VALUE_00)
727     {
728         std::string l_imagePrefix = dbusUtility::getImagePrefix();
729 
730         // Check image for 0x500030XX series.
731         if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
732             ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
733              (l_imagePrefix == constants::powerVsImagePrefix_NY)))
734         {
735             logging::logMessage("PowerVS configuration");
736             return true;
737         }
738 
739         // Check image for 0X500010XX series.
740         if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
741             ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
742              (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
743         {
744             logging::logMessage("PowerVS configuration");
745             return true;
746         }
747     }
748     return false;
749 }
750 
751 /**
752  * @brief API to get CCIN for a given FRU from DBus.
753  *
754  * The API reads the CCIN for a FRU based on its inventory path.
755  *
756  * @param[in] i_invObjPath - Inventory path of the FRU.
757  * @return CCIN of the FRU on success, empty string otherwise.
758  */
getCcinFromDbus(const std::string & i_invObjPath)759 inline std::string getCcinFromDbus(const std::string& i_invObjPath)
760 {
761     try
762     {
763         if (i_invObjPath.empty())
764         {
765             throw std::runtime_error("Empty EEPROM path, can't read CCIN");
766         }
767 
768         const auto& l_retValue = dbusUtility::readDbusProperty(
769             constants::pimServiceName, i_invObjPath, constants::viniInf,
770             constants::kwdCCIN);
771 
772         auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
773         if (!l_ptrCcin || (*l_ptrCcin).size() != constants::VALUE_4)
774         {
775             throw DbusException("Invalid CCIN read from Dbus");
776         }
777 
778         return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
779     }
780     catch (const std::exception& l_ex)
781     {
782         logging::logMessage(l_ex.what());
783         return std::string{};
784     }
785 }
786 } // namespace vpdSpecificUtility
787 } // namespace vpd
788