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