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