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