xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/vpd_specific_utility.hpp (revision 7b1f0354c732215d5102c0e77cfd296cce43f9d7)
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[in] i_clearPresence - Indicates whether to clear present property or
654  * not.
655  * @param[out] o_errCode - To set error code in case of error.
656  */
resetDataUnderPIM(const std::string & i_objectPath,types::InterfaceMap & io_interfaceMap,bool i_clearPresence,uint16_t & o_errCode)657 inline void resetDataUnderPIM(const std::string& i_objectPath,
658                               types::InterfaceMap& io_interfaceMap,
659                               bool i_clearPresence, uint16_t& o_errCode)
660 {
661     o_errCode = 0;
662     if (i_objectPath.empty())
663     {
664         o_errCode = error_code::INVALID_INPUT_PARAMETER;
665         return;
666     }
667 
668     try
669     {
670         std::vector<std::string> l_interfaces;
671         const types::MapperGetObject& l_getObjectMap =
672             dbusUtility::getObjectMap(i_objectPath, l_interfaces);
673 
674         const std::vector<std::string>& l_vpdRelatedInterfaces{
675             constants::operationalStatusInf, constants::inventoryItemInf,
676             constants::assetInf, constants::vpdCollectionInterface};
677 
678         for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
679         {
680             if (l_service.compare(constants::pimServiceName) !=
681                 constants::STR_CMP_SUCCESS)
682             {
683                 continue;
684             }
685 
686             for (const auto& l_interface : l_interfaceList)
687             {
688                 if ((l_interface.find(constants::ipzVpdInf) !=
689                          std::string::npos &&
690                      l_interface != constants::locationCodeInf) ||
691                     ((std::find(l_vpdRelatedInterfaces.begin(),
692                                 l_vpdRelatedInterfaces.end(), l_interface)) !=
693                      l_vpdRelatedInterfaces.end()))
694                 {
695                     const types::PropertyMap& l_propertyValueMap =
696                         dbusUtility::getPropertyMap(l_service, i_objectPath,
697                                                     l_interface);
698 
699                     types::PropertyMap l_propertyMap;
700 
701                     for (const auto& l_aProperty : l_propertyValueMap)
702                     {
703                         const std::string& l_propertyName = l_aProperty.first;
704                         const auto& l_propertyValue = l_aProperty.second;
705 
706                         if (std::holds_alternative<types::BinaryVector>(
707                                 l_propertyValue))
708                         {
709                             l_propertyMap.emplace(l_propertyName,
710                                                   types::BinaryVector{});
711                         }
712                         else if (std::holds_alternative<std::string>(
713                                      l_propertyValue))
714                         {
715                             if (l_propertyName.compare("Status") ==
716                                 constants::STR_CMP_SUCCESS)
717                             {
718                                 l_propertyMap.emplace(
719                                     l_propertyName,
720                                     constants::vpdCollectionNotStarted);
721                                 l_propertyMap.emplace("StartTime", 0);
722                                 l_propertyMap.emplace("CompletedTime", 0);
723                             }
724                             else if (l_propertyName.compare("PrettyName") ==
725                                      constants::STR_CMP_SUCCESS)
726                             {
727                                 // The FRU name is constant and independent of
728                                 // its presence state. So, it should not get
729                                 // reset.
730                                 continue;
731                             }
732                             else
733                             {
734                                 l_propertyMap.emplace(l_propertyName,
735                                                       std::string{});
736                             }
737                         }
738                         else if (std::holds_alternative<bool>(l_propertyValue))
739                         {
740                             if (l_propertyName.compare("Present") ==
741                                 constants::STR_CMP_SUCCESS)
742                             {
743                                 if (i_clearPresence)
744                                 {
745                                     l_propertyMap.emplace(l_propertyName,
746                                                           false);
747                                 }
748                             }
749                             else if (l_propertyName.compare("Functional") ==
750                                      constants::STR_CMP_SUCCESS)
751                             {
752                                 // Since FRU is not present functional property
753                                 // is considered as true.
754                                 l_propertyMap.emplace(l_propertyName, true);
755                             }
756                         }
757                     }
758                     io_interfaceMap.emplace(l_interface,
759                                             std::move(l_propertyMap));
760                 }
761             }
762         }
763     }
764     catch (const std::exception& l_ex)
765     {
766         o_errCode = error_code::STANDARD_EXCEPTION;
767     }
768 }
769 
770 /**
771  * @brief API to detect pass1 planar type.
772  *
773  * Based on HW version and IM keyword, This API detects is it is a pass1 planar
774  * or not.
775  * @param[out] o_errCode - To set error code in case of error.
776  *
777  * @return True if pass 1 planar, false otherwise.
778  */
isPass1Planar(uint16_t & o_errCode)779 inline bool isPass1Planar(uint16_t& o_errCode) noexcept
780 {
781     o_errCode = 0;
782     bool l_rc{false};
783     auto l_retVal = dbusUtility::readDbusProperty(
784         constants::pimServiceName, constants::systemVpdInvPath,
785         constants::viniInf, constants::kwdHW);
786 
787     auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
788 
789     l_retVal = dbusUtility::readDbusProperty(
790         constants::pimServiceName, constants::systemInvPath, constants::vsbpInf,
791         constants::kwdIM);
792 
793     auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
794 
795     if (l_hwVer && l_imValue)
796     {
797         if (l_hwVer->size() != constants::VALUE_2)
798         {
799             o_errCode = error_code::INVALID_KEYWORD_LENGTH;
800             return l_rc;
801         }
802 
803         if (l_imValue->size() != constants::VALUE_4)
804         {
805             o_errCode = error_code::INVALID_KEYWORD_LENGTH;
806             return l_rc;
807         }
808 
809         const types::BinaryVector l_everest{80, 00, 48, 00};
810         const types::BinaryVector l_fuji{96, 00, 32, 00};
811 
812         if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
813         {
814             if ((*l_hwVer).at(1) < constants::VALUE_21)
815             {
816                 l_rc = true;
817             }
818         }
819         else if ((*l_hwVer).at(1) < constants::VALUE_2)
820         {
821             l_rc = true;
822         }
823     }
824 
825     return l_rc;
826 }
827 
828 /**
829  * @brief API to detect if system configuration is that of PowerVS system.
830  *
831  * @param[in] i_imValue - IM value of the system.
832  * @param[out] o_errCode - To set error code in case of error.
833  * @return true if it is PowerVS configuration, false otherwise.
834  */
isPowerVsConfiguration(const types::BinaryVector & i_imValue,uint16_t & o_errCode)835 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue,
836                                    uint16_t& o_errCode)
837 {
838     o_errCode = 0;
839     if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
840     {
841         o_errCode = error_code::INVALID_INPUT_PARAMETER;
842         return false;
843     }
844 
845     // Should be a 0x5000XX series system.
846     if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
847         i_imValue.at(1) == constants::HEX_VALUE_00)
848     {
849         std::string l_imagePrefix = dbusUtility::getImagePrefix();
850 
851         // Check image for 0x500030XX series.
852         if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
853             ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
854              (l_imagePrefix == constants::powerVsImagePrefix_NY)))
855         {
856             logging::logMessage("PowerVS configuration");
857             return true;
858         }
859 
860         // Check image for 0X500010XX series.
861         if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
862             ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
863              (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
864         {
865             logging::logMessage("PowerVS configuration");
866             return true;
867         }
868     }
869     return false;
870 }
871 
872 /**
873  * @brief API to get CCIN for a given FRU from DBus.
874  *
875  * The API reads the CCIN for a FRU based on its inventory path.
876  *
877  * @param[in] i_invObjPath - Inventory path of the FRU.
878  * @param[out] o_errCode - To set error code in case of error.
879  *
880  * @return CCIN of the FRU on success, empty string otherwise.
881  */
getCcinFromDbus(const std::string & i_invObjPath,uint16_t & o_errCode)882 inline std::string getCcinFromDbus(const std::string& i_invObjPath,
883                                    uint16_t& o_errCode)
884 {
885     o_errCode = 0;
886     try
887     {
888         if (i_invObjPath.empty())
889         {
890             o_errCode = error_code::INVALID_INPUT_PARAMETER;
891             return std::string{};
892         }
893 
894         const auto& l_retValue = dbusUtility::readDbusProperty(
895             constants::pimServiceName, i_invObjPath, constants::viniInf,
896             constants::kwdCCIN);
897 
898         auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
899 
900         if (!l_ptrCcin)
901         {
902             o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
903             return std::string{};
904         }
905 
906         if ((*l_ptrCcin).size() != constants::VALUE_4)
907         {
908             o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
909             return std::string{};
910         }
911 
912         return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
913     }
914     catch (const std::exception& l_ex)
915     {
916         o_errCode = error_code::STANDARD_EXCEPTION;
917         return std::string{};
918     }
919 }
920 
921 /**
922  * @brief API to check if the current running image is a powerVS image.
923  *
924  * @return true if it is PowerVS image, false otherwise.
925  */
isPowerVsImage()926 inline bool isPowerVsImage()
927 {
928     std::string l_imagePrefix = dbusUtility::getImagePrefix();
929 
930     if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
931         (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
932         (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
933         (l_imagePrefix == constants::powerVsImagePrefix_NZ))
934     {
935         return true;
936     }
937     return false;
938 }
939 
940 /**
941  * @brief API to sync keyword update to inherited FRUs.
942  *
943  * For a given keyword update on a EEPROM path, this API syncs the keyword
944  * update to all inherited FRUs' respective interface, property on PIM.
945  *
946  * @param[in] i_fruPath - EEPROM path of FRU.
947  * @param[in] i_paramsToWriteData - Input details.
948  * @param[in] i_sysCfgJsonObj - System config JSON.
949  * @param[out] o_errCode - To set error code in case of error.
950  *
951  */
updateKwdOnInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)952 inline void updateKwdOnInheritedFrus(
953     const std::string& i_fruPath,
954     const types::WriteVpdParams& i_paramsToWriteData,
955     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
956 {
957     o_errCode = 0;
958     try
959     {
960         if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
961         {
962             o_errCode = error_code::INVALID_INPUT_PARAMETER;
963             return;
964         }
965 
966         if (!i_sysCfgJsonObj.contains("frus"))
967         {
968             o_errCode = error_code::INVALID_JSON;
969             return;
970         }
971 
972         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
973         {
974             o_errCode = error_code::FRU_PATH_NOT_FOUND;
975             return;
976         }
977 
978         const types::IpzData* l_ipzData =
979             std::get_if<types::IpzData>(&i_paramsToWriteData);
980 
981         if (!l_ipzData)
982         {
983             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
984             return;
985         }
986         //  iterate through all inventory paths for given EEPROM path,
987         //  except the base FRU.
988         //  if for an inventory path, "inherit" tag is true,
989         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
990         //  property
991 
992         types::ObjectMap l_objectInterfaceMap;
993 
994         auto l_populateInterfaceMap =
995             [&l_objectInterfaceMap,
996              &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
997                 // update inherited FRUs only
998                 if (l_Fru.value("inherit", true))
999                 {
1000                     l_objectInterfaceMap.emplace(
1001                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1002                         types::InterfaceMap{
1003                             {std::string{constants::ipzVpdInf +
1004                                          std::get<0>(*l_ipzData)},
1005                              types::PropertyMap{{std::get<1>(*l_ipzData),
1006                                                  std::get<2>(*l_ipzData)}}}});
1007                 }
1008             };
1009 
1010         // iterate through all FRUs except the base FRU
1011         std::for_each(
1012             i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
1013             i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
1014 
1015         if (!l_objectInterfaceMap.empty())
1016         {
1017             // notify PIM
1018             if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1019             {
1020                 o_errCode = error_code::DBUS_FAILURE;
1021                 return;
1022             }
1023         }
1024     }
1025     catch (const std::exception& l_ex)
1026     {
1027         o_errCode = error_code::STANDARD_EXCEPTION;
1028         return;
1029     }
1030 }
1031 
1032 /**
1033  * @brief API to get common interface(s) properties corresponding to given
1034  * record and keyword.
1035  *
1036  * For a given record and keyword, this API finds the corresponding common
1037  * interfaces(s) properties from the system config JSON and populates an
1038  * interface map with the respective properties and values.
1039  *
1040  * @param[in] i_paramsToWriteData - Input details.
1041  * @param[in] i_commonInterfaceJson - Common interface JSON object.
1042  * @param[out] o_errCode - To set error code in case of error.
1043  *
1044  * @return Returns a map of common interface(s) and properties corresponding to
1045  * the record and keyword. An empty map is returned if no such common
1046  * interface(s) and properties are found.
1047  */
getCommonInterfaceProperties(const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_commonInterfaceJson,uint16_t & o_errCode)1048 inline types::InterfaceMap getCommonInterfaceProperties(
1049     const types::WriteVpdParams& i_paramsToWriteData,
1050     const nlohmann::json& i_commonInterfaceJson, uint16_t& o_errCode) noexcept
1051 {
1052     types::InterfaceMap l_interfaceMap;
1053     o_errCode = 0;
1054     try
1055     {
1056         if (i_commonInterfaceJson.empty())
1057         {
1058             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1059             return l_interfaceMap;
1060         }
1061 
1062         const types::IpzData* l_ipzData =
1063             std::get_if<types::IpzData>(&i_paramsToWriteData);
1064 
1065         if (!l_ipzData)
1066         {
1067             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1068             return l_interfaceMap;
1069         }
1070 
1071         auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
1072                                        &l_interfaceMap, &o_errCode](
1073                                           const auto& l_interfacesPropPair) {
1074             if (l_interfacesPropPair.value().empty())
1075             {
1076                 return;
1077             }
1078 
1079             // find matching property value pair
1080             const auto l_matchPropValuePairIt = std::find_if(
1081                 l_interfacesPropPair.value().items().begin(),
1082                 l_interfacesPropPair.value().items().end(),
1083                 [&l_ipzData](const auto& l_propValuePair) {
1084                     return (l_propValuePair.value().value("recordName", "") ==
1085                                 std::get<0>(*l_ipzData) &&
1086                             l_propValuePair.value().value("keywordName", "") ==
1087                                 std::get<1>(*l_ipzData));
1088                 });
1089 
1090             if (l_matchPropValuePairIt !=
1091                 l_interfacesPropPair.value().items().end())
1092             {
1093                 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(),
1094                                                 std::get<2>(*l_ipzData).end());
1095 
1096                 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword(
1097                     l_kwd, l_matchPropValuePairIt.value().value("encoding", ""),
1098                     o_errCode);
1099 
1100                 if (l_encodedValue.empty() && o_errCode)
1101                 {
1102                     logging::logMessage(
1103                         "Failed to get encoded value for keyword : " + l_kwd +
1104                         ", error : " + commonUtility::getErrCodeMsg(o_errCode));
1105                 }
1106 
1107                 // add property map to interface map
1108                 l_interfaceMap.emplace(
1109                     l_interfacesPropPair.key(),
1110                     types::PropertyMap{
1111                         {l_matchPropValuePairIt.key(), l_encodedValue}});
1112             }
1113         };
1114 
1115         if (!i_commonInterfaceJson.empty())
1116         {
1117             // iterate through all common interfaces and populate interface map
1118             std::for_each(i_commonInterfaceJson.items().begin(),
1119                           i_commonInterfaceJson.items().end(),
1120                           l_populateInterfaceMap);
1121         }
1122     }
1123     catch (const std::exception& l_ex)
1124     {
1125         o_errCode = error_code::STANDARD_EXCEPTION;
1126     }
1127     return l_interfaceMap;
1128 }
1129 
1130 /**
1131  * @brief API to update common interface(s) properties when keyword is updated.
1132  *
1133  * For a given keyword update on a EEPROM path, this API syncs the keyword
1134  * update to respective common interface(s) properties of the base FRU and all
1135  * inherited FRUs.
1136  *
1137  * @param[in] i_fruPath - EEPROM path of FRU.
1138  * @param[in] i_paramsToWriteData - Input details.
1139  * @param[in] i_sysCfgJsonObj - System config JSON.
1140  * @param[out] o_errCode - To set error code in case of error.
1141  */
updateCiPropertyOfInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1142 inline void updateCiPropertyOfInheritedFrus(
1143     const std::string& i_fruPath,
1144     const types::WriteVpdParams& i_paramsToWriteData,
1145     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1146 {
1147     o_errCode = 0;
1148     try
1149     {
1150         if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1151         {
1152             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1153             return;
1154         }
1155 
1156         if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1157         {
1158             // no common interfaces in JSON, nothing to do
1159             return;
1160         }
1161 
1162         if (!i_sysCfgJsonObj.contains("frus"))
1163         {
1164             o_errCode = error_code::INVALID_JSON;
1165             return;
1166         }
1167 
1168         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1169         {
1170             o_errCode = error_code::FRU_PATH_NOT_FOUND;
1171             return;
1172         }
1173 
1174         if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1175         {
1176             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1177             return;
1178         }
1179 
1180         //  iterate through all inventory paths for given EEPROM path,
1181         //  if for an inventory path, "inherit" tag is true,
1182         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
1183         //  property
1184 
1185         types::ObjectMap l_objectInterfaceMap;
1186 
1187         const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1188             i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"],
1189             o_errCode);
1190 
1191         if (l_interfaceMap.empty())
1192         {
1193             if (o_errCode)
1194             {
1195                 logging::logMessage(
1196                     "Failed to get common interface property list, error : " +
1197                     commonUtility::getErrCodeMsg(o_errCode));
1198             }
1199             // nothing to do
1200             return;
1201         }
1202 
1203         auto l_populateObjectInterfaceMap =
1204             [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1205                                         l_interfaceMap)](const auto& l_Fru) {
1206                 if (l_Fru.value("inherit", true) &&
1207                     l_Fru.contains("inventoryPath"))
1208                 {
1209                     l_objectInterfaceMap.emplace(
1210                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1211                         l_interfaceMap);
1212                 }
1213             };
1214 
1215         std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1216                       i_sysCfgJsonObj["frus"][i_fruPath].end(),
1217                       l_populateObjectInterfaceMap);
1218 
1219         if (!l_objectInterfaceMap.empty())
1220         {
1221             // notify PIM
1222             if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1223             {
1224                 o_errCode = error_code::DBUS_FAILURE;
1225                 return;
1226             }
1227         }
1228     }
1229     catch (const std::exception& l_ex)
1230     {
1231         o_errCode = error_code::STANDARD_EXCEPTION;
1232     }
1233 }
1234 
1235 /**
1236  * @brief API to convert write VPD parameters to a string.
1237  *
1238  * @param[in] i_paramsToWriteData - write VPD parameters.
1239  * @param[out] o_errCode - To set error code in case of error.
1240  *
1241  * @return On success returns string representation of write VPD parameters,
1242  * otherwise returns an empty string.
1243  */
convertWriteVpdParamsToString(const types::WriteVpdParams & i_paramsToWriteData,uint16_t & o_errCode)1244 inline const std::string convertWriteVpdParamsToString(
1245     const types::WriteVpdParams& i_paramsToWriteData,
1246     uint16_t& o_errCode) noexcept
1247 {
1248     o_errCode = 0;
1249     try
1250     {
1251         if (const types::IpzData* l_ipzDataPtr =
1252                 std::get_if<types::IpzData>(&i_paramsToWriteData))
1253         {
1254             return std::string{
1255                 "Record: " + std::get<0>(*l_ipzDataPtr) +
1256                 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " +
1257                 commonUtility::convertByteVectorToHex(
1258                     std::get<2>(*l_ipzDataPtr))};
1259         }
1260         else if (const types::KwData* l_kwDataPtr =
1261                      std::get_if<types::KwData>(&i_paramsToWriteData))
1262         {
1263             return std::string{
1264                 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " +
1265                 commonUtility::convertByteVectorToHex(
1266                     std::get<1>(*l_kwDataPtr))};
1267         }
1268         else
1269         {
1270             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1271         }
1272     }
1273     catch (const std::exception& l_ex)
1274     {
1275         o_errCode = error_code::STANDARD_EXCEPTION;
1276     }
1277     return std::string{};
1278 }
1279 
1280 /**
1281  * @brief An API to read IM value from VPD.
1282  *
1283  * @param[in] i_parsedVpd - Parsed VPD.
1284  * @param[out] o_errCode - To set error code in case of error.
1285  */
getIMValue(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1286 inline std::string getIMValue(const types::IPZVpdMap& i_parsedVpd,
1287                               uint16_t& o_errCode) noexcept
1288 {
1289     o_errCode = 0;
1290     std::ostringstream l_imData;
1291     try
1292     {
1293         if (i_parsedVpd.empty())
1294         {
1295             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1296             return {};
1297         }
1298 
1299         const auto& l_itrToVSBP = i_parsedVpd.find("VSBP");
1300         if (l_itrToVSBP == i_parsedVpd.end())
1301         {
1302             o_errCode = error_code::RECORD_NOT_FOUND;
1303             return {};
1304         }
1305 
1306         const auto& l_itrToIM = (l_itrToVSBP->second).find("IM");
1307         if (l_itrToIM == (l_itrToVSBP->second).end())
1308         {
1309             o_errCode = error_code::KEYWORD_NOT_FOUND;
1310             return {};
1311         }
1312 
1313         types::BinaryVector l_imVal;
1314         std::copy(l_itrToIM->second.begin(), l_itrToIM->second.end(),
1315                   back_inserter(l_imVal));
1316 
1317         for (auto& l_aByte : l_imVal)
1318         {
1319             l_imData << std::setw(2) << std::setfill('0') << std::hex
1320                      << static_cast<int>(l_aByte);
1321         }
1322     }
1323     catch (const std::exception& l_ex)
1324     {
1325         logging::logMessage("Failed to get IM value with exception:" +
1326                             std::string(l_ex.what()));
1327         o_errCode = error_code::STANDARD_EXCEPTION;
1328     }
1329 
1330     return l_imData.str();
1331 }
1332 
1333 /**
1334  * @brief An API to read HW version from VPD.
1335  *
1336  * @param[in] i_parsedVpd - Parsed VPD.
1337  * @param[out] o_errCode - To set error code in case of error.
1338  */
getHWVersion(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1339 inline std::string getHWVersion(const types::IPZVpdMap& i_parsedVpd,
1340                                 uint16_t& o_errCode) noexcept
1341 {
1342     o_errCode = 0;
1343     std::ostringstream l_hwString;
1344     try
1345     {
1346         if (i_parsedVpd.empty())
1347         {
1348             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1349             return {};
1350         }
1351 
1352         const auto& l_itrToVINI = i_parsedVpd.find("VINI");
1353         if (l_itrToVINI == i_parsedVpd.end())
1354         {
1355             o_errCode = error_code::RECORD_NOT_FOUND;
1356             return {};
1357         }
1358 
1359         const auto& l_itrToHW = (l_itrToVINI->second).find("HW");
1360         if (l_itrToHW == (l_itrToVINI->second).end())
1361         {
1362             o_errCode = error_code::KEYWORD_NOT_FOUND;
1363             return {};
1364         }
1365 
1366         types::BinaryVector l_hwVal;
1367         std::copy(l_itrToHW->second.begin(), l_itrToHW->second.end(),
1368                   back_inserter(l_hwVal));
1369 
1370         // The planar pass only comes from the LSB of the HW keyword,
1371         // where as the MSB is used for other purposes such as signifying clock
1372         // termination.
1373         l_hwVal[0] = 0x00;
1374 
1375         for (auto& l_aByte : l_hwVal)
1376         {
1377             l_hwString << std::setw(2) << std::setfill('0') << std::hex
1378                        << static_cast<int>(l_aByte);
1379         }
1380     }
1381     catch (const std::exception& l_ex)
1382     {
1383         logging::logMessage("Failed to get HW version with exception:" +
1384                             std::string(l_ex.what()));
1385         o_errCode = error_code::STANDARD_EXCEPTION;
1386     }
1387 
1388     return l_hwString.str();
1389 }
1390 
1391 /**
1392  * @brief An API to set VPD collection status for a fru.
1393  *
1394  * This API updates the CollectionStatus property of the given FRU with the
1395  * given value.
1396  *
1397  * @param[in] i_vpdPath - Fru path (EEPROM or Inventory path)
1398  * @param[in] i_value - State to set.
1399  * @param[in] i_sysCfgJsonObj - System config json object.
1400  * @param[out] o_errCode - To set error code in case of error.
1401  */
setCollectionStatusProperty(const std::string & i_vpdPath,const types::VpdCollectionStatus & i_value,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1402 inline void setCollectionStatusProperty(
1403     const std::string& i_vpdPath, const types::VpdCollectionStatus& i_value,
1404     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1405 {
1406     o_errCode = 0;
1407     if (i_vpdPath.empty())
1408     {
1409         o_errCode = error_code::INVALID_INPUT_PARAMETER;
1410         return;
1411     }
1412 
1413     if (i_sysCfgJsonObj.empty() || !i_sysCfgJsonObj.contains("frus"))
1414     {
1415         o_errCode = error_code::INVALID_JSON;
1416         return;
1417     }
1418 
1419     types::PropertyMap l_timeStampMap;
1420     if (i_value == types::VpdCollectionStatus::Completed ||
1421         i_value == types::VpdCollectionStatus::Failed)
1422     {
1423         l_timeStampMap.emplace(
1424             "CompletedTime",
1425             types::DbusVariantType{commonUtility::getCurrentTimeSinceEpoch()});
1426     }
1427     else if (i_value == types::VpdCollectionStatus::InProgress)
1428     {
1429         l_timeStampMap.emplace(
1430             "StartTime",
1431             types::DbusVariantType{commonUtility::getCurrentTimeSinceEpoch()});
1432     }
1433     else if (i_value == types::VpdCollectionStatus::NotStarted)
1434     {
1435         l_timeStampMap.emplace("StartTime", 0);
1436         l_timeStampMap.emplace("CompletedTime", 0);
1437     }
1438 
1439     types::ObjectMap l_objectInterfaceMap;
1440 
1441     const auto& l_eepromPath =
1442         jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, i_vpdPath, o_errCode);
1443 
1444     if (l_eepromPath.empty() || o_errCode)
1445     {
1446         return;
1447     }
1448 
1449     for (const auto& l_Fru : i_sysCfgJsonObj["frus"][l_eepromPath])
1450     {
1451         sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
1452 
1453         types::PropertyMap l_propertyValueMap;
1454         l_propertyValueMap.emplace(
1455             "Status",
1456             types::CommonProgress::convertOperationStatusToString(i_value));
1457         l_propertyValueMap.insert(l_timeStampMap.begin(), l_timeStampMap.end());
1458 
1459         types::InterfaceMap l_interfaces;
1460         vpdSpecificUtility::insertOrMerge(l_interfaces,
1461                                           types::CommonProgress::interface,
1462                                           move(l_propertyValueMap), o_errCode);
1463 
1464         if (o_errCode)
1465         {
1466             Logger::getLoggerInstance()->logMessage(
1467                 "Failed to insert value into map, error : " +
1468                 commonUtility::getErrCodeMsg(o_errCode));
1469             return;
1470         }
1471 
1472         l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1473                                      std::move(l_interfaces));
1474     }
1475 
1476     // Notify PIM
1477     if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1478     {
1479         o_errCode = error_code::DBUS_FAILURE;
1480         return;
1481     }
1482 }
1483 
1484 /**
1485  * @brief API to reset data of a FRU and its sub-FRU populated under PIM.
1486  *
1487  * The API resets the data for specific interfaces of a FRU and its sub-FRUs
1488  * under PIM.
1489  *
1490  * Note: i_vpdPath should be either the base inventory path or the EEPROM path.
1491  *
1492  * @param[in] i_vpdPath - EEPROM/root inventory path of the FRU.
1493  * @param[in] i_sysCfgJsonObj - system config JSON.
1494  * @param[out] o_errCode - To set error code in case of error.
1495  */
resetObjTreeVpd(const std::string & i_vpdPath,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1496 inline void resetObjTreeVpd(const std::string& i_vpdPath,
1497                             const nlohmann::json& i_sysCfgJsonObj,
1498                             uint16_t& o_errCode) noexcept
1499 {
1500     o_errCode = 0;
1501     if (i_vpdPath.empty() || i_sysCfgJsonObj.empty())
1502     {
1503         o_errCode = error_code::INVALID_INPUT_PARAMETER;
1504         return;
1505     }
1506 
1507     try
1508     {
1509         const std::string& l_fruPath = jsonUtility::getFruPathFromJson(
1510             i_sysCfgJsonObj, i_vpdPath, o_errCode);
1511 
1512         if (o_errCode)
1513         {
1514             return;
1515         }
1516 
1517         types::ObjectMap l_objectMap;
1518 
1519         const auto& l_fruItems = i_sysCfgJsonObj["frus"][l_fruPath];
1520 
1521         for (const auto& l_inventoryItem : l_fruItems)
1522         {
1523             const std::string& l_objectPath =
1524                 l_inventoryItem.value("inventoryPath", "");
1525 
1526             if (l_inventoryItem.value("synthesized", false))
1527             {
1528                 continue;
1529             }
1530 
1531             types::InterfaceMap l_interfaceMap;
1532             resetDataUnderPIM(l_objectPath, l_interfaceMap,
1533                               l_inventoryItem.value("handlePresence", true),
1534                               o_errCode);
1535 
1536             if (o_errCode)
1537             {
1538                 logging::logMessage(
1539                     "Failed to get data to clear on DBus for path [" +
1540                     l_objectPath +
1541                     "], error : " + commonUtility::getErrCodeMsg(o_errCode));
1542 
1543                 continue;
1544             }
1545 
1546             l_objectMap.emplace(l_objectPath, l_interfaceMap);
1547         }
1548 
1549         if (!dbusUtility::callPIM(std::move(l_objectMap)))
1550         {
1551             o_errCode = error_code::DBUS_FAILURE;
1552         }
1553     }
1554     catch (const std::exception& l_ex)
1555     {
1556         logging::logMessage(
1557             "Failed to reset FRU data on DBus for FRU [" + i_vpdPath +
1558             "], error : " + std::string(l_ex.what()));
1559 
1560         o_errCode = error_code::STANDARD_EXCEPTION;
1561     }
1562 }
1563 } // namespace vpdSpecificUtility
1564 } // namespace vpd
1565