xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/vpd_specific_utility.hpp (revision fa131de66bd43bbc129a728a369be067afe069b7)
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 unexpanded location code with ND/SC support.
344  *
345  * Supports: Ufcs-ND0-SCx, Ufcs-ND0-SCxx, Ufcs-ND0-NDx, Ufcs-ND0-NDxx (with
346  * optional suffixes)
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 error, unexpanded is returned.
352  */
getExpandedLocationCode(const std::string & unexpandedLocationCode,const types::VPDMapVariant & parsedVpdMap,uint16_t & o_errCode)353 inline std::string getExpandedLocationCode(
354     const std::string& unexpandedLocationCode,
355     const types::VPDMapVariant& parsedVpdMap, uint16_t& o_errCode)
356 {
357     o_errCode = 0;
358     if (unexpandedLocationCode.empty())
359     {
360         o_errCode = error_code::INVALID_INPUT_PARAMETER;
361         return unexpandedLocationCode;
362     }
363     try
364     {
365         // Determine location code type and position
366         size_t pos = unexpandedLocationCode.find("fcs");
367         bool isFcs = (pos != std::string::npos);
368         if (!isFcs)
369         {
370             pos = unexpandedLocationCode.find("mts");
371             if (pos == std::string::npos)
372             {
373                 o_errCode = error_code::FAILED_TO_DETECT_LOCATION_CODE_TYPE;
374                 return unexpandedLocationCode;
375             }
376         }
377         // Set record and keyword parameters based on type
378         const std::string& recordName =
379             isFcs ? constants::recVCEN : constants::recVSYS;
380         const std::string& kwd1 = isFcs ? constants::kwdFC : constants::kwdTM;
381         const std::string& kwdInterface =
382             isFcs ? constants::vcenInf : constants::vsysInf;
383         const std::string kwd2 = constants::kwdSE;
384         // Retrieve keyword values
385         std::string firstKwdValue, secondKwdValue;
386         bool useDBus = true;
387         // Try to get from parsed VPD map first
388         if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
389         {
390             // Check if map is not empty and contains the required record
391             if (!ipzVpdMap->empty())
392             {
393                 auto recordItr = ipzVpdMap->find(recordName);
394                 if (recordItr != ipzVpdMap->end())
395                 {
396                     // Map has the record, use it
397                     firstKwdValue =
398                         getKwVal(recordItr->second, kwd1, o_errCode);
399                     if (o_errCode != 0)
400                         return unexpandedLocationCode;
401                     secondKwdValue =
402                         getKwVal(recordItr->second, kwd2, o_errCode);
403                     if (o_errCode != 0)
404                         return unexpandedLocationCode;
405                     useDBus = false;
406                 }
407             }
408         }
409         // Fallback to DBus if map is empty or doesn't have record
410         if (useDBus)
411         {
412             auto mapperRetValue = dbusUtility::getObjectMap(
413                 std::string(constants::systemVpdInvPath), {kwdInterface});
414             if (mapperRetValue.empty())
415             {
416                 o_errCode = error_code::DBUS_FAILURE;
417                 return unexpandedLocationCode;
418             }
419             const std::string& serviceName = std::get<0>(mapperRetValue[0]);
420             // Helper lambda to read and convert DBus property
421             auto readKwdValue = [&](const std::string& kwd) -> std::string {
422                 auto retVal = dbusUtility::readDbusProperty(
423                     serviceName, std::string(constants::systemVpdInvPath),
424                     kwdInterface, kwd);
425                 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
426                 {
427                     return std::string(
428                         reinterpret_cast<const char*>(kwdVal->data()),
429                         kwdVal->size());
430                 }
431                 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
432                 return "";
433             };
434             firstKwdValue = readKwdValue(kwd1);
435             if (o_errCode != 0)
436                 return unexpandedLocationCode;
437             secondKwdValue = readKwdValue(kwd2);
438             if (o_errCode != 0)
439                 return unexpandedLocationCode;
440         }
441         // Build expanded location code
442         std::string expanded = unexpandedLocationCode;
443         if (isFcs)
444         {
445             // FCS: Must have -NDx, -NDxx, -SCx, or -SCxx pattern
446             std::string suffix = unexpandedLocationCode.substr(pos + 3);
447             std::regex ndScPattern(R"(^-((ND|SC)\d{1,2})(-.*)?)");
448             std::smatch match;
449             if (!std::regex_search(suffix, match, ndScPattern))
450             {
451                 o_errCode = error_code::INVALID_LOCATION_CODE_FORMAT;
452                 return unexpandedLocationCode;
453             }
454             // Build: FC[0:4].NDx.SE or FC[0:4].SCxx.SE (+ optional suffix)
455             expanded.replace(pos, 3 + suffix.length(),
456                              firstKwdValue.substr(0, 4) + "." + match[1].str() +
457                                  "." + secondKwdValue + match[3].str());
458         }
459         else
460         {
461             // MTS: Replace dashes with dots in TM value
462             std::replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
463             expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
464         }
465         return expanded;
466     }
467     catch (const std::exception& ex)
468     {
469         o_errCode = error_code::STANDARD_EXCEPTION;
470         return unexpandedLocationCode;
471     }
472 }
473 
474 #if 0
475 /**
476  * @brief API to expand unpanded location code.
477  *
478  * Note: The API handles all the exception internally, in case of any error
479  * unexpanded location code will be returned as it is.
480  *
481  * @param[in] unexpandedLocationCode - Unexpanded location code.
482  * @param[in] parsedVpdMap - Parsed VPD map.
483  * @param[out] o_errCode - To set error code in case of error.
484  * @return Expanded location code. In case of any error, unexpanded is returned
485  * as it is.
486  */
487 inline std::string getExpandedLocationCode(
488     const std::string& unexpandedLocationCode,
489     const types::VPDMapVariant& parsedVpdMap, uint16_t& o_errCode)
490 {
491     o_errCode = 0;
492     if (unexpandedLocationCode.empty())
493     {
494         o_errCode = error_code::INVALID_INPUT_PARAMETER;
495         return unexpandedLocationCode;
496     }
497 
498     auto expanded{unexpandedLocationCode};
499 
500     try
501     {
502         // Expanded location code is formed by combining two keywords
503         // depending on type in unexpanded one. Second one is always "SE".
504         std::string kwd1, kwd2{constants::kwdSE};
505 
506         // interface to search for required keywords;
507         std::string kwdInterface;
508 
509         // record which holds the required keywords.
510         std::string recordName;
511 
512         auto pos = unexpandedLocationCode.find("fcs");
513         if (pos != std::string::npos)
514         {
515             kwd1 = constants::kwdFC;
516             kwdInterface = constants::vcenInf;
517             recordName = constants::recVCEN;
518         }
519         else
520         {
521             pos = unexpandedLocationCode.find("mts");
522             if (pos != std::string::npos)
523             {
524                 kwd1 = constants::kwdTM;
525                 kwdInterface = constants::vsysInf;
526                 recordName = constants::recVSYS;
527             }
528             else
529             {
530                 o_errCode = error_code::FAILED_TO_DETECT_LOCATION_CODE_TYPE;
531                 return expanded;
532             }
533         }
534 
535         std::string firstKwdValue, secondKwdValue;
536 
537         if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
538             ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
539         {
540             auto itrToVCEN = (*ipzVpdMap).find(recordName);
541             firstKwdValue = getKwVal(itrToVCEN->second, kwd1, o_errCode);
542             if (firstKwdValue.empty())
543             {
544                 o_errCode = error_code::KEYWORD_NOT_FOUND;
545                 return expanded;
546             }
547 
548             secondKwdValue = getKwVal(itrToVCEN->second, kwd2, o_errCode);
549             if (secondKwdValue.empty())
550             {
551                 o_errCode = error_code::KEYWORD_NOT_FOUND;
552                 return expanded;
553             }
554         }
555         else
556         {
557             std::vector<std::string> interfaceList = {kwdInterface};
558 
559             types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
560                 std::string(constants::systemVpdInvPath), interfaceList);
561 
562             if (mapperRetValue.empty())
563             {
564                 o_errCode = error_code::DBUS_FAILURE;
565                 return expanded;
566             }
567 
568             const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
569 
570             auto retVal = dbusUtility::readDbusProperty(
571                 serviceName, std::string(constants::systemVpdInvPath),
572                 kwdInterface, kwd1);
573 
574             if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
575             {
576                 firstKwdValue.assign(
577                     reinterpret_cast<const char*>(kwdVal->data()),
578                     kwdVal->size());
579             }
580             else
581             {
582                 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
583                 return expanded;
584             }
585 
586             retVal = dbusUtility::readDbusProperty(
587                 serviceName, std::string(constants::systemVpdInvPath),
588                 kwdInterface, kwd2);
589 
590             if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
591             {
592                 secondKwdValue.assign(
593                     reinterpret_cast<const char*>(kwdVal->data()),
594                     kwdVal->size());
595             }
596             else
597             {
598                 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
599                 return expanded;
600             }
601         }
602 
603         if (unexpandedLocationCode.find("fcs") != std::string::npos)
604         {
605             // TODO: See if ND0 can be placed in the JSON
606             expanded.replace(
607                 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
608         }
609         else
610         {
611             replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
612             expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
613         }
614     }
615     catch (const std::exception& ex)
616     {
617         o_errCode = error_code::STANDARD_EXCEPTION;
618     }
619 
620     return expanded;
621 }
622 
623 #endif
624 
625 /**
626  * @brief An API to get VPD in a vector.
627  *
628  * The vector is required by the respective parser to fill the VPD map.
629  * Note: API throws exception in case of failure. Caller needs to handle.
630  *
631  * @param[in] vpdFilePath - EEPROM path of the FRU.
632  * @param[out] vpdVector - VPD in vector form.
633  * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
634  * @param[out] o_errCode - To set error code in case of error.
635  */
getVpdDataInVector(const std::string & vpdFilePath,types::BinaryVector & vpdVector,size_t & vpdStartOffset,uint16_t & o_errCode)636 inline void getVpdDataInVector(const std::string& vpdFilePath,
637                                types::BinaryVector& vpdVector,
638                                size_t& vpdStartOffset, uint16_t& o_errCode)
639 {
640     o_errCode = 0;
641     if (vpdFilePath.empty())
642     {
643         o_errCode = error_code::INVALID_INPUT_PARAMETER;
644         return;
645     }
646 
647     try
648     {
649         std::fstream vpdFileStream;
650         vpdFileStream.exceptions(
651             std::ifstream::badbit | std::ifstream::failbit);
652         vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
653         auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
654                                       static_cast<uintmax_t>(65504));
655         vpdVector.resize(vpdSizeToRead);
656 
657         vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
658         vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
659                            vpdSizeToRead);
660 
661         vpdVector.resize(vpdFileStream.gcount());
662         vpdFileStream.clear(std::ios_base::eofbit);
663     }
664     catch (const std::ifstream::failure& fail)
665     {
666         o_errCode = error_code::FILE_SYSTEM_ERROR;
667         return;
668     }
669 }
670 
671 /**
672  * @brief An API to get D-bus representation of given VPD keyword.
673  *
674  * @param[in] i_keywordName - VPD keyword name.
675  * @param[out] o_errCode - To set error code in case of error.
676  *
677  * @return D-bus representation of given keyword.
678  */
getDbusPropNameForGivenKw(const std::string & i_keywordName,uint16_t & o_errCode)679 inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName,
680                                              uint16_t& o_errCode)
681 {
682     o_errCode = 0;
683     if (i_keywordName.empty())
684     {
685         o_errCode = error_code::INVALID_INPUT_PARAMETER;
686         return std::string{};
687     }
688     // Check for "#" prefixed VPD keyword.
689     if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
690         (i_keywordName.at(0) == constants::POUND_KW))
691     {
692         // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
693         // prefixed keywords.
694         return (std::string(constants::POUND_KW_PREFIX) +
695                 i_keywordName.substr(1));
696     }
697 
698     // Return the keyword name back, if D-bus representation is same as the VPD
699     // keyword name.
700     return i_keywordName;
701 }
702 
703 /**
704  * @brief API to find CCIN in parsed VPD map.
705  *
706  * Few FRUs need some special handling. To identify those FRUs CCIN are used.
707  * The API will check from parsed VPD map if the FRU is the one with desired
708  * CCIN.
709  *
710  * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
711  * @param[in] i_parsedVpdMap - Parsed VPD map.
712  * @param[out] o_errCode - To set error code in case of error.
713  *
714  * @return True if found, false otherwise.
715  */
findCcinInVpd(const nlohmann::json & i_JsonObject,const types::VPDMapVariant & i_parsedVpdMap,uint16_t & o_errCode)716 inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
717                           const types::VPDMapVariant& i_parsedVpdMap,
718                           uint16_t& o_errCode) noexcept
719 {
720     o_errCode = 0;
721     bool l_rc{false};
722     try
723     {
724         if (i_JsonObject.empty() ||
725             std::holds_alternative<std::monostate>(i_parsedVpdMap))
726         {
727             o_errCode = error_code::INVALID_INPUT_PARAMETER;
728             return l_rc;
729         }
730 
731         if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
732         {
733             auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
734             if (l_itrToRec == (*l_ipzVPDMap).end())
735             {
736                 o_errCode = error_code::RECORD_NOT_FOUND;
737                 return l_rc;
738             }
739 
740             std::string l_ccinFromVpd{vpdSpecificUtility::getKwVal(
741                 l_itrToRec->second, "CC", o_errCode)};
742             if (l_ccinFromVpd.empty())
743             {
744                 o_errCode = error_code::KEYWORD_NOT_FOUND;
745                 return l_rc;
746             }
747 
748             transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
749                       l_ccinFromVpd.begin(), ::toupper);
750 
751             for (std::string l_ccinValue : i_JsonObject["ccin"])
752             {
753                 transform(l_ccinValue.begin(), l_ccinValue.end(),
754                           l_ccinValue.begin(), ::toupper);
755 
756                 if (l_ccinValue.compare(l_ccinFromVpd) ==
757                     constants::STR_CMP_SUCCESS)
758                 {
759                     // CCIN found
760                     l_rc = true;
761                 }
762             }
763 
764             if (!l_rc)
765             {
766                 logging::logMessage("No match found for CCIN");
767             }
768         }
769         else
770         {
771             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
772         }
773     }
774     catch (const std::exception& l_ex)
775     {
776         o_errCode = error_code::STANDARD_EXCEPTION;
777     }
778     return l_rc;
779 }
780 
781 /**
782  * @brief API to reset data of a FRU populated under PIM.
783  *
784  * This API resets the data for particular interfaces of a FRU under PIM.
785  *
786  * @param[in] i_objectPath - DBus object path of the FRU.
787  * @param[in] io_interfaceMap - Interface and its properties map.
788  * @param[in] i_clearPresence - Indicates whether to clear present property or
789  * not.
790  * @param[out] o_errCode - To set error code in case of error.
791  */
resetDataUnderPIM(const std::string & i_objectPath,types::InterfaceMap & io_interfaceMap,bool i_clearPresence,uint16_t & o_errCode)792 inline void resetDataUnderPIM(const std::string& i_objectPath,
793                               types::InterfaceMap& io_interfaceMap,
794                               bool i_clearPresence, uint16_t& o_errCode)
795 {
796     o_errCode = 0;
797     if (i_objectPath.empty())
798     {
799         o_errCode = error_code::INVALID_INPUT_PARAMETER;
800         return;
801     }
802 
803     try
804     {
805         std::vector<std::string> l_interfaces;
806         const types::MapperGetObject& l_getObjectMap =
807             dbusUtility::getObjectMap(i_objectPath, l_interfaces);
808 
809         const std::vector<std::string>& l_vpdRelatedInterfaces{
810             constants::operationalStatusInf, constants::inventoryItemInf,
811             constants::assetInf, constants::vpdCollectionInterface};
812 
813         for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
814         {
815             if (l_service.compare(constants::pimServiceName) !=
816                 constants::STR_CMP_SUCCESS)
817             {
818                 continue;
819             }
820 
821             for (const auto& l_interface : l_interfaceList)
822             {
823                 if ((l_interface.find(constants::ipzVpdInf) !=
824                          std::string::npos &&
825                      l_interface != constants::locationCodeInf) ||
826                     ((std::find(l_vpdRelatedInterfaces.begin(),
827                                 l_vpdRelatedInterfaces.end(), l_interface)) !=
828                      l_vpdRelatedInterfaces.end()))
829                 {
830                     const types::PropertyMap& l_propertyValueMap =
831                         dbusUtility::getPropertyMap(l_service, i_objectPath,
832                                                     l_interface);
833 
834                     types::PropertyMap l_propertyMap;
835 
836                     for (const auto& l_aProperty : l_propertyValueMap)
837                     {
838                         const std::string& l_propertyName = l_aProperty.first;
839                         const auto& l_propertyValue = l_aProperty.second;
840 
841                         if (std::holds_alternative<types::BinaryVector>(
842                                 l_propertyValue))
843                         {
844                             l_propertyMap.emplace(l_propertyName,
845                                                   types::BinaryVector{});
846                         }
847                         else if (std::holds_alternative<std::string>(
848                                      l_propertyValue))
849                         {
850                             if (l_propertyName.compare("Status") ==
851                                 constants::STR_CMP_SUCCESS)
852                             {
853                                 l_propertyMap.emplace(
854                                     l_propertyName,
855                                     constants::vpdCollectionNotStarted);
856                                 l_propertyMap.emplace("StartTime", 0);
857                                 l_propertyMap.emplace("CompletedTime", 0);
858                             }
859                             else if (l_propertyName.compare("PrettyName") ==
860                                      constants::STR_CMP_SUCCESS)
861                             {
862                                 // The FRU name is constant and independent of
863                                 // its presence state. So, it should not get
864                                 // reset.
865                                 continue;
866                             }
867                             else
868                             {
869                                 l_propertyMap.emplace(l_propertyName,
870                                                       std::string{});
871                             }
872                         }
873                         else if (std::holds_alternative<bool>(l_propertyValue))
874                         {
875                             if (l_propertyName.compare("Present") ==
876                                 constants::STR_CMP_SUCCESS)
877                             {
878                                 if (i_clearPresence)
879                                 {
880                                     l_propertyMap.emplace(l_propertyName,
881                                                           false);
882                                 }
883                             }
884                             else if (l_propertyName.compare("Functional") ==
885                                      constants::STR_CMP_SUCCESS)
886                             {
887                                 // Since FRU is not present functional property
888                                 // is considered as true.
889                                 l_propertyMap.emplace(l_propertyName, true);
890                             }
891                         }
892                     }
893                     io_interfaceMap.emplace(l_interface,
894                                             std::move(l_propertyMap));
895                 }
896             }
897         }
898     }
899     catch (const std::exception& l_ex)
900     {
901         o_errCode = error_code::STANDARD_EXCEPTION;
902     }
903 }
904 
905 /**
906  * @brief API to detect pass1 planar type.
907  *
908  * Based on HW version and IM keyword, This API detects is it is a pass1 planar
909  * or not.
910  * @param[out] o_errCode - To set error code in case of error.
911  *
912  * @return True if pass 1 planar, false otherwise.
913  */
isPass1Planar(uint16_t & o_errCode)914 inline bool isPass1Planar(uint16_t& o_errCode) noexcept
915 {
916     o_errCode = 0;
917     bool l_rc{false};
918     const auto l_hwVar = dbusUtility::readDbusProperty(
919         constants::pimServiceName, constants::systemVpdInvPath,
920         constants::viniInf, constants::kwdHW);
921 
922     auto l_hwVer = std::get_if<types::BinaryVector>(&l_hwVar);
923     if (l_hwVer == nullptr)
924     {
925         o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
926         return l_rc;
927     }
928 
929     const auto l_imVar = dbusUtility::readDbusProperty(
930         constants::pimServiceName, constants::systemVpdInvPath,
931         constants::vsbpInf, constants::kwdIM);
932 
933     auto l_imValue = std::get_if<types::BinaryVector>(&l_imVar);
934     if (l_imValue == nullptr)
935     {
936         o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
937         return l_rc;
938     }
939 
940     if (l_hwVer->size() != constants::VALUE_2)
941     {
942         o_errCode = error_code::INVALID_KEYWORD_LENGTH;
943         return l_rc;
944     }
945 
946     if (l_imValue->size() != constants::VALUE_4)
947     {
948         o_errCode = error_code::INVALID_KEYWORD_LENGTH;
949         return l_rc;
950     }
951 
952     const types::BinaryVector l_everest{80, 00, 48, 00};
953     const types::BinaryVector l_fuji{96, 00, 32, 00};
954 
955     if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
956     {
957         if ((*l_hwVer).at(1) < constants::VALUE_21)
958         {
959             l_rc = true;
960         }
961     }
962     else if ((*l_hwVer).at(1) < constants::VALUE_2)
963     {
964         l_rc = true;
965     }
966 
967     return l_rc;
968 }
969 
970 /**
971  * @brief API to detect if system configuration is that of PowerVS system.
972  *
973  * @param[in] i_imValue - IM value of the system.
974  * @param[out] o_errCode - To set error code in case of error.
975  * @return true if it is PowerVS configuration, false otherwise.
976  */
isPowerVsConfiguration(const types::BinaryVector & i_imValue,uint16_t & o_errCode)977 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue,
978                                    uint16_t& o_errCode)
979 {
980     o_errCode = 0;
981     if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
982     {
983         o_errCode = error_code::INVALID_INPUT_PARAMETER;
984         return false;
985     }
986 
987     // Should be a 0x5000XX series system.
988     if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
989         i_imValue.at(1) == constants::HEX_VALUE_00)
990     {
991         std::string l_imagePrefix = dbusUtility::getImagePrefix();
992 
993         // Check image for 0x500030XX series.
994         if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
995             ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
996              (l_imagePrefix == constants::powerVsImagePrefix_NY)))
997         {
998             logging::logMessage("PowerVS configuration");
999             return true;
1000         }
1001 
1002         // Check image for 0X500010XX series.
1003         if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
1004             ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
1005              (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
1006         {
1007             logging::logMessage("PowerVS configuration");
1008             return true;
1009         }
1010     }
1011     return false;
1012 }
1013 
1014 /**
1015  * @brief API to get CCIN for a given FRU from DBus.
1016  *
1017  * The API reads the CCIN for a FRU based on its inventory path.
1018  *
1019  * @param[in] i_invObjPath - Inventory path of the FRU.
1020  * @param[out] o_errCode - To set error code in case of error.
1021  *
1022  * @return CCIN of the FRU on success, empty string otherwise.
1023  */
getCcinFromDbus(const std::string & i_invObjPath,uint16_t & o_errCode)1024 inline std::string getCcinFromDbus(const std::string& i_invObjPath,
1025                                    uint16_t& o_errCode)
1026 {
1027     o_errCode = 0;
1028     try
1029     {
1030         if (i_invObjPath.empty())
1031         {
1032             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1033             return std::string{};
1034         }
1035 
1036         const auto& l_retValue = dbusUtility::readDbusProperty(
1037             constants::pimServiceName, i_invObjPath, constants::viniInf,
1038             constants::kwdCCIN);
1039 
1040         auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
1041 
1042         if (!l_ptrCcin)
1043         {
1044             o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
1045             return std::string{};
1046         }
1047 
1048         if ((*l_ptrCcin).size() != constants::VALUE_4)
1049         {
1050             o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
1051             return std::string{};
1052         }
1053 
1054         return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
1055     }
1056     catch (const std::exception& l_ex)
1057     {
1058         o_errCode = error_code::STANDARD_EXCEPTION;
1059         return std::string{};
1060     }
1061 }
1062 
1063 /**
1064  * @brief API to check if the current running image is a powerVS image.
1065  *
1066  * @return true if it is PowerVS image, false otherwise.
1067  */
isPowerVsImage()1068 inline bool isPowerVsImage()
1069 {
1070     std::string l_imagePrefix = dbusUtility::getImagePrefix();
1071 
1072     if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
1073         (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
1074         (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
1075         (l_imagePrefix == constants::powerVsImagePrefix_NZ))
1076     {
1077         return true;
1078     }
1079     return false;
1080 }
1081 
1082 /**
1083  * @brief API to sync keyword update to inherited FRUs.
1084  *
1085  * For a given keyword update on a EEPROM path, this API syncs the keyword
1086  * update to all inherited FRUs' respective interface, property on PIM.
1087  *
1088  * @param[in] i_fruPath - EEPROM path of FRU.
1089  * @param[in] i_paramsToWriteData - Input details.
1090  * @param[in] i_sysCfgJsonObj - System config JSON.
1091  * @param[out] o_errCode - To set error code in case of error.
1092  *
1093  */
updateKwdOnInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1094 inline void updateKwdOnInheritedFrus(
1095     const std::string& i_fruPath,
1096     const types::WriteVpdParams& i_paramsToWriteData,
1097     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1098 {
1099     o_errCode = 0;
1100     try
1101     {
1102         if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1103         {
1104             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1105             return;
1106         }
1107 
1108         if (!i_sysCfgJsonObj.contains("frus"))
1109         {
1110             o_errCode = error_code::INVALID_JSON;
1111             return;
1112         }
1113 
1114         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1115         {
1116             o_errCode = error_code::FRU_PATH_NOT_FOUND;
1117             return;
1118         }
1119 
1120         const types::IpzData* l_ipzData =
1121             std::get_if<types::IpzData>(&i_paramsToWriteData);
1122 
1123         if (!l_ipzData)
1124         {
1125             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1126             return;
1127         }
1128         //  iterate through all inventory paths for given EEPROM path,
1129         //  except the base FRU.
1130         //  if for an inventory path, "inherit" tag is true,
1131         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
1132         //  property
1133 
1134         types::ObjectMap l_objectInterfaceMap;
1135 
1136         auto l_populateInterfaceMap =
1137             [&l_objectInterfaceMap,
1138              &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
1139                 // update inherited FRUs only
1140                 if (l_Fru.value("inherit", true))
1141                 {
1142                     l_objectInterfaceMap.emplace(
1143                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1144                         types::InterfaceMap{
1145                             {std::string{constants::ipzVpdInf +
1146                                          std::get<0>(*l_ipzData)},
1147                              types::PropertyMap{{std::get<1>(*l_ipzData),
1148                                                  std::get<2>(*l_ipzData)}}}});
1149                 }
1150             };
1151 
1152         // iterate through all FRUs except the base FRU
1153         std::for_each(
1154             i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
1155             i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
1156 
1157         if (!l_objectInterfaceMap.empty())
1158         {
1159             // Call method to update the dbus
1160             if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1161             {
1162                 o_errCode = error_code::DBUS_FAILURE;
1163                 return;
1164             }
1165         }
1166     }
1167     catch (const std::exception& l_ex)
1168     {
1169         o_errCode = error_code::STANDARD_EXCEPTION;
1170         return;
1171     }
1172 }
1173 
1174 /**
1175  * @brief API to get common interface(s) properties corresponding to given
1176  * record and keyword.
1177  *
1178  * For a given record and keyword, this API finds the corresponding common
1179  * interfaces(s) properties from the system config JSON and populates an
1180  * interface map with the respective properties and values.
1181  *
1182  * @param[in] i_paramsToWriteData - Input details.
1183  * @param[in] i_commonInterfaceJson - Common interface JSON object.
1184  * @param[out] o_errCode - To set error code in case of error.
1185  *
1186  * @return Returns a map of common interface(s) and properties corresponding to
1187  * the record and keyword. An empty map is returned if no such common
1188  * interface(s) and properties are found.
1189  */
getCommonInterfaceProperties(const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_commonInterfaceJson,uint16_t & o_errCode)1190 inline types::InterfaceMap getCommonInterfaceProperties(
1191     const types::WriteVpdParams& i_paramsToWriteData,
1192     const nlohmann::json& i_commonInterfaceJson, uint16_t& o_errCode) noexcept
1193 {
1194     types::InterfaceMap l_interfaceMap;
1195     o_errCode = 0;
1196     try
1197     {
1198         if (i_commonInterfaceJson.empty())
1199         {
1200             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1201             return l_interfaceMap;
1202         }
1203 
1204         const types::IpzData* l_ipzData =
1205             std::get_if<types::IpzData>(&i_paramsToWriteData);
1206 
1207         if (!l_ipzData)
1208         {
1209             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1210             return l_interfaceMap;
1211         }
1212 
1213         auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
1214                                        &l_interfaceMap, &o_errCode](
1215                                           const auto& l_interfacesPropPair) {
1216             if (l_interfacesPropPair.value().empty())
1217             {
1218                 return;
1219             }
1220 
1221             // find matching property value pair
1222             const auto l_matchPropValuePairIt = std::find_if(
1223                 l_interfacesPropPair.value().items().begin(),
1224                 l_interfacesPropPair.value().items().end(),
1225                 [&l_ipzData](const auto& l_propValuePair) {
1226                     return (l_propValuePair.value().value("recordName", "") ==
1227                                 std::get<0>(*l_ipzData) &&
1228                             l_propValuePair.value().value("keywordName", "") ==
1229                                 std::get<1>(*l_ipzData));
1230                 });
1231 
1232             if (l_matchPropValuePairIt !=
1233                 l_interfacesPropPair.value().items().end())
1234             {
1235                 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(),
1236                                                 std::get<2>(*l_ipzData).end());
1237 
1238                 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword(
1239                     l_kwd, l_matchPropValuePairIt.value().value("encoding", ""),
1240                     o_errCode);
1241 
1242                 if (l_encodedValue.empty() && o_errCode)
1243                 {
1244                     logging::logMessage(
1245                         "Failed to get encoded value for keyword : " + l_kwd +
1246                         ", error : " + commonUtility::getErrCodeMsg(o_errCode));
1247                 }
1248 
1249                 // add property map to interface map
1250                 l_interfaceMap.emplace(
1251                     l_interfacesPropPair.key(),
1252                     types::PropertyMap{
1253                         {l_matchPropValuePairIt.key(), l_encodedValue}});
1254             }
1255         };
1256 
1257         if (!i_commonInterfaceJson.empty())
1258         {
1259             // iterate through all common interfaces and populate interface map
1260             std::for_each(i_commonInterfaceJson.items().begin(),
1261                           i_commonInterfaceJson.items().end(),
1262                           l_populateInterfaceMap);
1263         }
1264     }
1265     catch (const std::exception& l_ex)
1266     {
1267         o_errCode = error_code::STANDARD_EXCEPTION;
1268     }
1269     return l_interfaceMap;
1270 }
1271 
1272 /**
1273  * @brief API to update common interface(s) properties when keyword is updated.
1274  *
1275  * For a given keyword update on a EEPROM path, this API syncs the keyword
1276  * update to respective common interface(s) properties of the base FRU and all
1277  * inherited FRUs.
1278  *
1279  * @param[in] i_fruPath - EEPROM path of FRU.
1280  * @param[in] i_paramsToWriteData - Input details.
1281  * @param[in] i_sysCfgJsonObj - System config JSON.
1282  * @param[out] o_errCode - To set error code in case of error.
1283  */
updateCiPropertyOfInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1284 inline void updateCiPropertyOfInheritedFrus(
1285     const std::string& i_fruPath,
1286     const types::WriteVpdParams& i_paramsToWriteData,
1287     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1288 {
1289     o_errCode = 0;
1290     try
1291     {
1292         if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1293         {
1294             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1295             return;
1296         }
1297 
1298         if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1299         {
1300             // no common interfaces in JSON, nothing to do
1301             return;
1302         }
1303 
1304         if (!i_sysCfgJsonObj.contains("frus"))
1305         {
1306             o_errCode = error_code::INVALID_JSON;
1307             return;
1308         }
1309 
1310         if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1311         {
1312             o_errCode = error_code::FRU_PATH_NOT_FOUND;
1313             return;
1314         }
1315 
1316         if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1317         {
1318             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1319             return;
1320         }
1321 
1322         //  iterate through all inventory paths for given EEPROM path,
1323         //  if for an inventory path, "inherit" tag is true,
1324         //  update the inventory path's com.ibm.ipzvpd.<record>,keyword
1325         //  property
1326 
1327         types::ObjectMap l_objectInterfaceMap;
1328 
1329         const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1330             i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"],
1331             o_errCode);
1332 
1333         if (l_interfaceMap.empty())
1334         {
1335             if (o_errCode)
1336             {
1337                 logging::logMessage(
1338                     "Failed to get common interface property list, error : " +
1339                     commonUtility::getErrCodeMsg(o_errCode));
1340             }
1341             // nothing to do
1342             return;
1343         }
1344 
1345         auto l_populateObjectInterfaceMap =
1346             [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1347                                         l_interfaceMap)](const auto& l_Fru) {
1348                 if (l_Fru.value("inherit", true) &&
1349                     l_Fru.contains("inventoryPath"))
1350                 {
1351                     l_objectInterfaceMap.emplace(
1352                         sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1353                         l_interfaceMap);
1354                 }
1355             };
1356 
1357         std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1358                       i_sysCfgJsonObj["frus"][i_fruPath].end(),
1359                       l_populateObjectInterfaceMap);
1360 
1361         if (!l_objectInterfaceMap.empty())
1362         {
1363             // Call method to update the dbus
1364             if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1365             {
1366                 o_errCode = error_code::DBUS_FAILURE;
1367                 return;
1368             }
1369         }
1370     }
1371     catch (const std::exception& l_ex)
1372     {
1373         o_errCode = error_code::STANDARD_EXCEPTION;
1374     }
1375 }
1376 
1377 /**
1378  * @brief API to convert write VPD parameters to a string.
1379  *
1380  * @param[in] i_paramsToWriteData - write VPD parameters.
1381  * @param[out] o_errCode - To set error code in case of error.
1382  *
1383  * @return On success returns string representation of write VPD parameters,
1384  * otherwise returns an empty string.
1385  */
convertWriteVpdParamsToString(const types::WriteVpdParams & i_paramsToWriteData,uint16_t & o_errCode)1386 inline const std::string convertWriteVpdParamsToString(
1387     const types::WriteVpdParams& i_paramsToWriteData,
1388     uint16_t& o_errCode) noexcept
1389 {
1390     o_errCode = 0;
1391     try
1392     {
1393         if (const types::IpzData* l_ipzDataPtr =
1394                 std::get_if<types::IpzData>(&i_paramsToWriteData))
1395         {
1396             return std::string{
1397                 "Record: " + std::get<0>(*l_ipzDataPtr) +
1398                 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " +
1399                 commonUtility::convertByteVectorToHex(
1400                     std::get<2>(*l_ipzDataPtr))};
1401         }
1402         else if (const types::KwData* l_kwDataPtr =
1403                      std::get_if<types::KwData>(&i_paramsToWriteData))
1404         {
1405             return std::string{
1406                 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " +
1407                 commonUtility::convertByteVectorToHex(
1408                     std::get<1>(*l_kwDataPtr))};
1409         }
1410         else
1411         {
1412             o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1413         }
1414     }
1415     catch (const std::exception& l_ex)
1416     {
1417         o_errCode = error_code::STANDARD_EXCEPTION;
1418     }
1419     return std::string{};
1420 }
1421 
1422 /**
1423  * @brief An API to read IM value from VPD.
1424  *
1425  * @param[in] i_parsedVpd - Parsed VPD.
1426  * @param[out] o_errCode - To set error code in case of error.
1427  */
getIMValue(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1428 inline std::string getIMValue(const types::IPZVpdMap& i_parsedVpd,
1429                               uint16_t& o_errCode) noexcept
1430 {
1431     o_errCode = 0;
1432     std::ostringstream l_imData;
1433     try
1434     {
1435         if (i_parsedVpd.empty())
1436         {
1437             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1438             return {};
1439         }
1440 
1441         const auto& l_itrToVSBP = i_parsedVpd.find("VSBP");
1442         if (l_itrToVSBP == i_parsedVpd.end())
1443         {
1444             o_errCode = error_code::RECORD_NOT_FOUND;
1445             return {};
1446         }
1447 
1448         const auto& l_itrToIM = (l_itrToVSBP->second).find("IM");
1449         if (l_itrToIM == (l_itrToVSBP->second).end())
1450         {
1451             o_errCode = error_code::KEYWORD_NOT_FOUND;
1452             return {};
1453         }
1454 
1455         types::BinaryVector l_imVal;
1456         std::copy(l_itrToIM->second.begin(), l_itrToIM->second.end(),
1457                   back_inserter(l_imVal));
1458 
1459         for (auto& l_aByte : l_imVal)
1460         {
1461             l_imData << std::setw(2) << std::setfill('0') << std::hex
1462                      << static_cast<int>(l_aByte);
1463         }
1464     }
1465     catch (const std::exception& l_ex)
1466     {
1467         logging::logMessage("Failed to get IM value with exception:" +
1468                             std::string(l_ex.what()));
1469         o_errCode = error_code::STANDARD_EXCEPTION;
1470     }
1471 
1472     return l_imData.str();
1473 }
1474 
1475 /**
1476  * @brief An API to read HW version from VPD.
1477  *
1478  * @param[in] i_parsedVpd - Parsed VPD.
1479  * @param[out] o_errCode - To set error code in case of error.
1480  */
getHWVersion(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1481 inline std::string getHWVersion(const types::IPZVpdMap& i_parsedVpd,
1482                                 uint16_t& o_errCode) noexcept
1483 {
1484     o_errCode = 0;
1485     std::ostringstream l_hwString;
1486     try
1487     {
1488         if (i_parsedVpd.empty())
1489         {
1490             o_errCode = error_code::INVALID_INPUT_PARAMETER;
1491             return {};
1492         }
1493 
1494         const auto& l_itrToVINI = i_parsedVpd.find("VINI");
1495         if (l_itrToVINI == i_parsedVpd.end())
1496         {
1497             o_errCode = error_code::RECORD_NOT_FOUND;
1498             return {};
1499         }
1500 
1501         const auto& l_itrToHW = (l_itrToVINI->second).find("HW");
1502         if (l_itrToHW == (l_itrToVINI->second).end())
1503         {
1504             o_errCode = error_code::KEYWORD_NOT_FOUND;
1505             return {};
1506         }
1507 
1508         types::BinaryVector l_hwVal;
1509         std::copy(l_itrToHW->second.begin(), l_itrToHW->second.end(),
1510                   back_inserter(l_hwVal));
1511 
1512         // The planar pass only comes from the LSB of the HW keyword,
1513         // where as the MSB is used for other purposes such as signifying clock
1514         // termination.
1515         l_hwVal[0] = 0x00;
1516 
1517         for (auto& l_aByte : l_hwVal)
1518         {
1519             l_hwString << std::setw(2) << std::setfill('0') << std::hex
1520                        << static_cast<int>(l_aByte);
1521         }
1522     }
1523     catch (const std::exception& l_ex)
1524     {
1525         logging::logMessage("Failed to get HW version with exception:" +
1526                             std::string(l_ex.what()));
1527         o_errCode = error_code::STANDARD_EXCEPTION;
1528     }
1529 
1530     return l_hwString.str();
1531 }
1532 
1533 /**
1534  * @brief An API to set VPD collection status for a fru.
1535  *
1536  * This API updates the CollectionStatus property of the given FRU with the
1537  * given value.
1538  *
1539  * @param[in] i_vpdPath - Fru path (EEPROM or Inventory path)
1540  * @param[in] i_value - State to set.
1541  * @param[in] i_sysCfgJsonObj - System config json object.
1542  * @param[out] o_errCode - To set error code in case of error.
1543  */
setCollectionStatusProperty(const std::string & i_vpdPath,const types::VpdCollectionStatus & i_value,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1544 inline void setCollectionStatusProperty(
1545     const std::string& i_vpdPath, const types::VpdCollectionStatus& i_value,
1546     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1547 {
1548     o_errCode = 0;
1549     if (i_vpdPath.empty())
1550     {
1551         o_errCode = error_code::INVALID_INPUT_PARAMETER;
1552         return;
1553     }
1554 
1555     if (i_sysCfgJsonObj.empty() || !i_sysCfgJsonObj.contains("frus"))
1556     {
1557         o_errCode = error_code::INVALID_JSON;
1558         return;
1559     }
1560 
1561     types::PropertyMap l_timeStampMap;
1562     if (i_value == types::VpdCollectionStatus::Completed ||
1563         i_value == types::VpdCollectionStatus::Failed)
1564     {
1565         l_timeStampMap.emplace(
1566             "CompletedTime",
1567             types::DbusVariantType{commonUtility::getCurrentTimeSinceEpoch()});
1568     }
1569     else if (i_value == types::VpdCollectionStatus::InProgress)
1570     {
1571         l_timeStampMap.emplace(
1572             "StartTime",
1573             types::DbusVariantType{commonUtility::getCurrentTimeSinceEpoch()});
1574     }
1575     else if (i_value == types::VpdCollectionStatus::NotStarted)
1576     {
1577         l_timeStampMap.emplace("StartTime", 0);
1578         l_timeStampMap.emplace("CompletedTime", 0);
1579     }
1580 
1581     types::ObjectMap l_objectInterfaceMap;
1582 
1583     const auto& l_eepromPath =
1584         jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, i_vpdPath, o_errCode);
1585 
1586     if (l_eepromPath.empty() || o_errCode)
1587     {
1588         return;
1589     }
1590 
1591     for (const auto& l_Fru : i_sysCfgJsonObj["frus"][l_eepromPath])
1592     {
1593         sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
1594 
1595         types::PropertyMap l_propertyValueMap;
1596         l_propertyValueMap.emplace(
1597             "Status",
1598             types::CommonProgress::convertOperationStatusToString(i_value));
1599         l_propertyValueMap.insert(l_timeStampMap.begin(), l_timeStampMap.end());
1600 
1601         types::InterfaceMap l_interfaces;
1602         vpdSpecificUtility::insertOrMerge(l_interfaces,
1603                                           types::CommonProgress::interface,
1604                                           move(l_propertyValueMap), o_errCode);
1605 
1606         if (o_errCode)
1607         {
1608             Logger::getLoggerInstance()->logMessage(
1609                 "Failed to insert value into map, error : " +
1610                 commonUtility::getErrCodeMsg(o_errCode));
1611             return;
1612         }
1613 
1614         l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1615                                      std::move(l_interfaces));
1616     }
1617 
1618     // Call dbus method to update on dbus
1619     if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1620     {
1621         o_errCode = error_code::DBUS_FAILURE;
1622         return;
1623     }
1624 }
1625 
1626 /**
1627  * @brief API to reset data of a FRU and its sub-FRU populated under PIM.
1628  *
1629  * The API resets the data for specific interfaces of a FRU and its sub-FRUs
1630  * under PIM.
1631  *
1632  * Note: i_vpdPath should be either the base inventory path or the EEPROM path.
1633  *
1634  * @param[in] i_vpdPath - EEPROM/root inventory path of the FRU.
1635  * @param[in] i_sysCfgJsonObj - system config JSON.
1636  * @param[out] o_errCode - To set error code in case of error.
1637  */
resetObjTreeVpd(const std::string & i_vpdPath,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1638 inline void resetObjTreeVpd(const std::string& i_vpdPath,
1639                             const nlohmann::json& i_sysCfgJsonObj,
1640                             uint16_t& o_errCode) noexcept
1641 {
1642     o_errCode = 0;
1643     if (i_vpdPath.empty() || i_sysCfgJsonObj.empty())
1644     {
1645         o_errCode = error_code::INVALID_INPUT_PARAMETER;
1646         return;
1647     }
1648 
1649     try
1650     {
1651         const std::string& l_fruPath = jsonUtility::getFruPathFromJson(
1652             i_sysCfgJsonObj, i_vpdPath, o_errCode);
1653 
1654         if (o_errCode)
1655         {
1656             return;
1657         }
1658 
1659         types::ObjectMap l_objectMap;
1660 
1661         const auto& l_fruItems = i_sysCfgJsonObj["frus"][l_fruPath];
1662 
1663         for (const auto& l_inventoryItem : l_fruItems)
1664         {
1665             const std::string& l_objectPath =
1666                 l_inventoryItem.value("inventoryPath", "");
1667 
1668             if (l_inventoryItem.value("synthesized", false))
1669             {
1670                 continue;
1671             }
1672 
1673             types::InterfaceMap l_interfaceMap;
1674             resetDataUnderPIM(l_objectPath, l_interfaceMap,
1675                               l_inventoryItem.value("handlePresence", true),
1676                               o_errCode);
1677 
1678             if (o_errCode)
1679             {
1680                 logging::logMessage(
1681                     "Failed to get data to clear on DBus for path [" +
1682                     l_objectPath +
1683                     "], error : " + commonUtility::getErrCodeMsg(o_errCode));
1684 
1685                 continue;
1686             }
1687 
1688             l_objectMap.emplace(l_objectPath, l_interfaceMap);
1689         }
1690 
1691         if (!dbusUtility::publishVpdOnDBus(std::move(l_objectMap)))
1692         {
1693             o_errCode = error_code::DBUS_FAILURE;
1694         }
1695     }
1696     catch (const std::exception& l_ex)
1697     {
1698         logging::logMessage(
1699             "Failed to reset FRU data on DBus for FRU [" + i_vpdPath +
1700             "], error : " + std::string(l_ex.what()));
1701 
1702         o_errCode = error_code::STANDARD_EXCEPTION;
1703     }
1704 }
1705 } // namespace vpdSpecificUtility
1706 } // namespace vpd
1707