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