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