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