xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/vpd_specific_utility.hpp (revision b1e822e563c0feb0d2fc43c9542262ac053ab803)
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
723                             {
724                                 l_propertyMap.emplace(l_propertyName,
725                                                       std::string{});
726                             }
727                         }
728                         else if (std::holds_alternative<bool>(l_propertyValue))
729                         {
730                             if (l_propertyName.compare("Present") ==
731                                 constants::STR_CMP_SUCCESS)
732                             {
733                                 l_propertyMap.emplace(l_propertyName, false);
734                             }
735                             else if (l_propertyName.compare("Functional") ==
736                                      constants::STR_CMP_SUCCESS)
737                             {
738                                 // Since FRU is not present functional property
739                                 // is considered as true.
740                                 l_propertyMap.emplace(l_propertyName, true);
741                             }
742                         }
743                     }
744                     io_interfaceMap.emplace(l_interface,
745                                             std::move(l_propertyMap));
746                 }
747             }
748         }
749     }
750     catch (const std::exception& l_ex)
751     {
752         o_errCode = error_code::STANDARD_EXCEPTION;
753     }
754 }
755 
756 /**
757  * @brief API to detect pass1 planar type.
758  *
759  * Based on HW version and IM keyword, This API detects is it is a pass1 planar
760  * or not.
761  * @param[out] o_errCode - To set error code in case of error.
762  *
763  * @return True if pass 1 planar, false otherwise.
764  */
isPass1Planar(uint16_t & o_errCode)765 inline bool isPass1Planar(uint16_t& o_errCode) noexcept
766 {
767     o_errCode = 0;
768     bool l_rc{false};
769     auto l_retVal = dbusUtility::readDbusProperty(
770         constants::pimServiceName, constants::systemVpdInvPath,
771         constants::viniInf, constants::kwdHW);
772 
773     auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
774 
775     l_retVal = dbusUtility::readDbusProperty(
776         constants::pimServiceName, constants::systemInvPath, constants::vsbpInf,
777         constants::kwdIM);
778 
779     auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
780 
781     if (l_hwVer && l_imValue)
782     {
783         if (l_hwVer->size() != constants::VALUE_2)
784         {
785             o_errCode = error_code::INVALID_KEYWORD_LENGTH;
786             return l_rc;
787         }
788 
789         if (l_imValue->size() != constants::VALUE_4)
790         {
791             o_errCode = error_code::INVALID_KEYWORD_LENGTH;
792             return l_rc;
793         }
794 
795         const types::BinaryVector l_everest{80, 00, 48, 00};
796         const types::BinaryVector l_fuji{96, 00, 32, 00};
797 
798         if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
799         {
800             if ((*l_hwVer).at(1) < constants::VALUE_21)
801             {
802                 l_rc = true;
803             }
804         }
805         else if ((*l_hwVer).at(1) < constants::VALUE_2)
806         {
807             l_rc = true;
808         }
809     }
810 
811     return l_rc;
812 }
813 
814 /**
815  * @brief API to detect if system configuration is that of PowerVS system.
816  *
817  * @param[in] i_imValue - IM value of the system.
818  * @param[out] o_errCode - To set error code in case of error.
819  * @return true if it is PowerVS configuration, false otherwise.
820  */
isPowerVsConfiguration(const types::BinaryVector & i_imValue,uint16_t & o_errCode)821 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue,
822                                    uint16_t& o_errCode)
823 {
824     o_errCode = 0;
825     if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
826     {
827         o_errCode = error_code::INVALID_INPUT_PARAMETER;
828         return false;
829     }
830 
831     // Should be a 0x5000XX series system.
832     if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
833         i_imValue.at(1) == constants::HEX_VALUE_00)
834     {
835         std::string l_imagePrefix = dbusUtility::getImagePrefix();
836 
837         // Check image for 0x500030XX series.
838         if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
839             ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
840              (l_imagePrefix == constants::powerVsImagePrefix_NY)))
841         {
842             logging::logMessage("PowerVS configuration");
843             return true;
844         }
845 
846         // Check image for 0X500010XX series.
847         if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
848             ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
849              (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
850         {
851             logging::logMessage("PowerVS configuration");
852             return true;
853         }
854     }
855     return false;
856 }
857 
858 /**
859  * @brief API to get CCIN for a given FRU from DBus.
860  *
861  * The API reads the CCIN for a FRU based on its inventory path.
862  *
863  * @param[in] i_invObjPath - Inventory path of the FRU.
864  * @param[out] o_errCode - To set error code in case of error.
865  *
866  * @return CCIN of the FRU on success, empty string otherwise.
867  */
getCcinFromDbus(const std::string & i_invObjPath,uint16_t & o_errCode)868 inline std::string getCcinFromDbus(const std::string& i_invObjPath,
869                                    uint16_t& o_errCode)
870 {
871     o_errCode = 0;
872     try
873     {
874         if (i_invObjPath.empty())
875         {
876             o_errCode = error_code::INVALID_INPUT_PARAMETER;
877             return std::string{};
878         }
879 
880         const auto& l_retValue = dbusUtility::readDbusProperty(
881             constants::pimServiceName, i_invObjPath, constants::viniInf,
882             constants::kwdCCIN);
883 
884         auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
885 
886         if (!l_ptrCcin)
887         {
888             o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
889             return std::string{};
890         }
891 
892         if ((*l_ptrCcin).size() != constants::VALUE_4)
893         {
894             o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
895             return std::string{};
896         }
897 
898         return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
899     }
900     catch (const std::exception& l_ex)
901     {
902         o_errCode = error_code::STANDARD_EXCEPTION;
903         return std::string{};
904     }
905 }
906 
907 /**
908  * @brief API to check if the current running image is a powerVS image.
909  *
910  * @return true if it is PowerVS image, false otherwise.
911  */
isPowerVsImage()912 inline bool isPowerVsImage()
913 {
914     std::string l_imagePrefix = dbusUtility::getImagePrefix();
915 
916     if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
917         (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
918         (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
919         (l_imagePrefix == constants::powerVsImagePrefix_NZ))
920     {
921         return true;
922     }
923     return false;
924 }
925 
926 /**
927  * @brief API to sync keyword update to inherited FRUs.
928  *
929  * For a given keyword update on a EEPROM path, this API syncs the keyword
930  * update to all inherited FRUs' respective interface, property on PIM.
931  *
932  * @param[in] i_fruPath - EEPROM path of FRU.
933  * @param[in] i_paramsToWriteData - Input details.
934  * @param[in] i_sysCfgJsonObj - System config JSON.
935  * @param[out] o_errCode - To set error code in case of error.
936  *
937  */
updateKwdOnInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)938 inline void updateKwdOnInheritedFrus(
939     const std::string& i_fruPath,
940     const types::WriteVpdParams& i_paramsToWriteData,
941     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
942 {
943     o_errCode = 0;
944     try
945     {
946         if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
947         {
948             o_errCode = error_code::INVALID_INPUT_PARAMETER;
949             return;
950         }
951 
952         if (!i_sysCfgJsonObj.contains("frus"))
953         {
954             o_errCode = error_code::INVALID_JSON;
955             return;
956         }
957 
958         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
959         {
960             o_errCode = error_code::FRU_PATH_NOT_FOUND;
961             return;
962         }
963 
964         const types::IpzData* l_ipzData =
965             std::get_if<types::IpzData>(&i_paramsToWriteData);
966 
967         if (!l_ipzData)
968         {
969             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
970             return;
971         }
972         //  iterate through all inventory paths for given EEPROM path,
973         //  except the base FRU.
974         //  if for an inventory path, "inherit" tag is true,
975         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
976         //  property
977 
978         types::ObjectMap l_objectInterfaceMap;
979 
980         auto l_populateInterfaceMap =
981             [&l_objectInterfaceMap,
982              &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
983                 // update inherited FRUs only
984                 if (l_Fru.value("inherit", true))
985                 {
986                     l_objectInterfaceMap.emplace(
987                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
988                         types::InterfaceMap{
989                             {std::string{constants::ipzVpdInf +
990                                          std::get<0>(*l_ipzData)},
991                              types::PropertyMap{{std::get<1>(*l_ipzData),
992                                                  std::get<2>(*l_ipzData)}}}});
993                 }
994             };
995 
996         // iterate through all FRUs except the base FRU
997         std::for_each(
998             i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
999             i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
1000 
1001         if (!l_objectInterfaceMap.empty())
1002         {
1003             // notify PIM
1004             if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1005             {
1006                 o_errCode = error_code::DBUS_FAILURE;
1007                 return;
1008             }
1009         }
1010     }
1011     catch (const std::exception& l_ex)
1012     {
1013         o_errCode = error_code::STANDARD_EXCEPTION;
1014         return;
1015     }
1016 }
1017 
1018 /**
1019  * @brief API to get common interface(s) properties corresponding to given
1020  * record and keyword.
1021  *
1022  * For a given record and keyword, this API finds the corresponding common
1023  * interfaces(s) properties from the system config JSON and populates an
1024  * interface map with the respective properties and values.
1025  *
1026  * @param[in] i_paramsToWriteData - Input details.
1027  * @param[in] i_commonInterfaceJson - Common interface JSON object.
1028  * @param[out] o_errCode - To set error code in case of error.
1029  *
1030  * @return Returns a map of common interface(s) and properties corresponding to
1031  * the record and keyword. An empty map is returned if no such common
1032  * interface(s) and properties are found.
1033  */
getCommonInterfaceProperties(const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_commonInterfaceJson,uint16_t & o_errCode)1034 inline types::InterfaceMap getCommonInterfaceProperties(
1035     const types::WriteVpdParams& i_paramsToWriteData,
1036     const nlohmann::json& i_commonInterfaceJson, uint16_t& o_errCode) noexcept
1037 {
1038     types::InterfaceMap l_interfaceMap;
1039     o_errCode = 0;
1040     try
1041     {
1042         if (i_commonInterfaceJson.empty())
1043         {
1044             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1045             return l_interfaceMap;
1046         }
1047 
1048         const types::IpzData* l_ipzData =
1049             std::get_if<types::IpzData>(&i_paramsToWriteData);
1050 
1051         if (!l_ipzData)
1052         {
1053             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1054             return l_interfaceMap;
1055         }
1056 
1057         auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
1058                                        &l_interfaceMap, &o_errCode](
1059                                           const auto& l_interfacesPropPair) {
1060             if (l_interfacesPropPair.value().empty())
1061             {
1062                 return;
1063             }
1064 
1065             // find matching property value pair
1066             const auto l_matchPropValuePairIt = std::find_if(
1067                 l_interfacesPropPair.value().items().begin(),
1068                 l_interfacesPropPair.value().items().end(),
1069                 [&l_ipzData](const auto& l_propValuePair) {
1070                     return (l_propValuePair.value().value("recordName", "") ==
1071                                 std::get<0>(*l_ipzData) &&
1072                             l_propValuePair.value().value("keywordName", "") ==
1073                                 std::get<1>(*l_ipzData));
1074                 });
1075 
1076             if (l_matchPropValuePairIt !=
1077                 l_interfacesPropPair.value().items().end())
1078             {
1079                 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(),
1080                                                 std::get<2>(*l_ipzData).end());
1081 
1082                 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword(
1083                     l_kwd, l_matchPropValuePairIt.value().value("encoding", ""),
1084                     o_errCode);
1085 
1086                 if (l_encodedValue.empty() && o_errCode)
1087                 {
1088                     logging::logMessage(
1089                         "Failed to get encoded value for keyword : " + l_kwd +
1090                         ", error : " + commonUtility::getErrCodeMsg(o_errCode));
1091                 }
1092 
1093                 // add property map to interface map
1094                 l_interfaceMap.emplace(
1095                     l_interfacesPropPair.key(),
1096                     types::PropertyMap{
1097                         {l_matchPropValuePairIt.key(), l_encodedValue}});
1098             }
1099         };
1100 
1101         if (!i_commonInterfaceJson.empty())
1102         {
1103             // iterate through all common interfaces and populate interface map
1104             std::for_each(i_commonInterfaceJson.items().begin(),
1105                           i_commonInterfaceJson.items().end(),
1106                           l_populateInterfaceMap);
1107         }
1108     }
1109     catch (const std::exception& l_ex)
1110     {
1111         o_errCode = error_code::STANDARD_EXCEPTION;
1112     }
1113     return l_interfaceMap;
1114 }
1115 
1116 /**
1117  * @brief API to update common interface(s) properties when keyword is updated.
1118  *
1119  * For a given keyword update on a EEPROM path, this API syncs the keyword
1120  * update to respective common interface(s) properties of the base FRU and all
1121  * inherited FRUs.
1122  *
1123  * @param[in] i_fruPath - EEPROM path of FRU.
1124  * @param[in] i_paramsToWriteData - Input details.
1125  * @param[in] i_sysCfgJsonObj - System config JSON.
1126  * @param[out] o_errCode - To set error code in case of error.
1127  */
updateCiPropertyOfInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1128 inline void updateCiPropertyOfInheritedFrus(
1129     const std::string& i_fruPath,
1130     const types::WriteVpdParams& i_paramsToWriteData,
1131     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1132 {
1133     o_errCode = 0;
1134     try
1135     {
1136         if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1137         {
1138             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1139             return;
1140         }
1141 
1142         if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1143         {
1144             // no common interfaces in JSON, nothing to do
1145             return;
1146         }
1147 
1148         if (!i_sysCfgJsonObj.contains("frus"))
1149         {
1150             o_errCode = error_code::INVALID_JSON;
1151             return;
1152         }
1153 
1154         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1155         {
1156             o_errCode = error_code::FRU_PATH_NOT_FOUND;
1157             return;
1158         }
1159 
1160         if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1161         {
1162             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1163             return;
1164         }
1165 
1166         //  iterate through all inventory paths for given EEPROM path,
1167         //  if for an inventory path, "inherit" tag is true,
1168         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
1169         //  property
1170 
1171         types::ObjectMap l_objectInterfaceMap;
1172 
1173         const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1174             i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"],
1175             o_errCode);
1176 
1177         if (l_interfaceMap.empty())
1178         {
1179             if (o_errCode)
1180             {
1181                 logging::logMessage(
1182                     "Failed to get common interface property list, error : " +
1183                     commonUtility::getErrCodeMsg(o_errCode));
1184             }
1185             // nothing to do
1186             return;
1187         }
1188 
1189         auto l_populateObjectInterfaceMap =
1190             [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1191                                         l_interfaceMap)](const auto& l_Fru) {
1192                 if (l_Fru.value("inherit", true) &&
1193                     l_Fru.contains("inventoryPath"))
1194                 {
1195                     l_objectInterfaceMap.emplace(
1196                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1197                         l_interfaceMap);
1198                 }
1199             };
1200 
1201         std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1202                       i_sysCfgJsonObj["frus"][i_fruPath].end(),
1203                       l_populateObjectInterfaceMap);
1204 
1205         if (!l_objectInterfaceMap.empty())
1206         {
1207             // notify PIM
1208             if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1209             {
1210                 o_errCode = error_code::DBUS_FAILURE;
1211                 return;
1212             }
1213         }
1214     }
1215     catch (const std::exception& l_ex)
1216     {
1217         o_errCode = error_code::STANDARD_EXCEPTION;
1218     }
1219 }
1220 
1221 /**
1222  * @brief API to convert write VPD parameters to a string.
1223  *
1224  * @param[in] i_paramsToWriteData - write VPD parameters.
1225  * @param[out] o_errCode - To set error code in case of error.
1226  *
1227  * @return On success returns string representation of write VPD parameters,
1228  * otherwise returns an empty string.
1229  */
convertWriteVpdParamsToString(const types::WriteVpdParams & i_paramsToWriteData,uint16_t & o_errCode)1230 inline const std::string convertWriteVpdParamsToString(
1231     const types::WriteVpdParams& i_paramsToWriteData,
1232     uint16_t& o_errCode) noexcept
1233 {
1234     o_errCode = 0;
1235     try
1236     {
1237         if (const types::IpzData* l_ipzDataPtr =
1238                 std::get_if<types::IpzData>(&i_paramsToWriteData))
1239         {
1240             return std::string{
1241                 "Record: " + std::get<0>(*l_ipzDataPtr) +
1242                 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " +
1243                 commonUtility::convertByteVectorToHex(
1244                     std::get<2>(*l_ipzDataPtr))};
1245         }
1246         else if (const types::KwData* l_kwDataPtr =
1247                      std::get_if<types::KwData>(&i_paramsToWriteData))
1248         {
1249             return std::string{
1250                 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " +
1251                 commonUtility::convertByteVectorToHex(
1252                     std::get<1>(*l_kwDataPtr))};
1253         }
1254         else
1255         {
1256             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1257         }
1258     }
1259     catch (const std::exception& l_ex)
1260     {
1261         o_errCode = error_code::STANDARD_EXCEPTION;
1262     }
1263     return std::string{};
1264 }
1265 
1266 } // namespace vpdSpecificUtility
1267 } // namespace vpd
1268