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