1 #pragma once
2
3 #include "config.h"
4
5 #include "constants.hpp"
6 #include "exceptions.hpp"
7 #include "logger.hpp"
8 #include "types.hpp"
9
10 #include <nlohmann/json.hpp>
11 #include <utility/common_utility.hpp>
12 #include <utility/dbus_utility.hpp>
13 #include <utility/event_logger_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 o_errCode = 0;
41 std::string l_badVpdFileName{constants::badVpdDir};
42
43 if (i_vpdFilePath.empty())
44 {
45 o_errCode = error_code::INVALID_INPUT_PARAMETER;
46 return l_badVpdFileName;
47 }
48
49 try
50 {
51 if (i_vpdFilePath.find("i2c") != std::string::npos)
52 {
53 l_badVpdFileName += "i2c-";
54 std::regex l_i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
55 std::smatch l_match;
56 if (std::regex_search(i_vpdFilePath, l_match, l_i2cPattern))
57 {
58 l_badVpdFileName += l_match.str(2);
59 }
60 }
61 else if (i_vpdFilePath.find("spi") != std::string::npos)
62 {
63 std::regex l_spiPattern("((spi)[0-9]+)(.0)");
64 std::smatch l_match;
65 if (std::regex_search(i_vpdFilePath, l_match, l_spiPattern))
66 {
67 l_badVpdFileName += l_match.str(1);
68 }
69 }
70 }
71 catch (const std::exception& l_ex)
72 {
73 l_badVpdFileName.clear();
74 o_errCode = error_code::STANDARD_EXCEPTION;
75 }
76 return l_badVpdFileName;
77 }
78
79 /**
80 * @brief API which dumps the broken/bad vpd in a directory.
81 * When the vpd is bad, this API places the bad vpd file inside
82 * "/var/lib/vpd/dumps" in BMC, in order to collect bad VPD data as a part of
83 * user initiated BMC dump.
84 *
85 *
86 * @param[in] i_vpdFilePath - vpd file path
87 * @param[in] i_vpdVector - vpd vector
88 * @param[out] o_errCode - To set error code in case of error.
89 *
90 * @return On success returns 0, otherwise returns -1.
91 */
dumpBadVpd(const std::string & i_vpdFilePath,const types::BinaryVector & i_vpdVector,uint16_t & o_errCode)92 inline int dumpBadVpd(const std::string& i_vpdFilePath,
93 const types::BinaryVector& i_vpdVector,
94 uint16_t& o_errCode) noexcept
95 {
96 o_errCode = 0;
97 if (i_vpdFilePath.empty() || i_vpdVector.empty())
98 {
99 o_errCode = error_code::INVALID_INPUT_PARAMETER;
100 return constants::FAILURE;
101 }
102
103 int l_rc{constants::FAILURE};
104 try
105 {
106 std::filesystem::create_directory(constants::badVpdDir);
107 auto l_badVpdPath = generateBadVPDFileName(i_vpdFilePath, o_errCode);
108
109 if (l_badVpdPath.empty())
110 {
111 if (o_errCode)
112 {
113 logging::logMessage("Failed to create bad VPD file name : " +
114 commonUtility::getErrCodeMsg(o_errCode));
115 }
116
117 return constants::FAILURE;
118 }
119
120 if (std::filesystem::exists(l_badVpdPath))
121 {
122 std::error_code l_ec;
123 std::filesystem::remove(l_badVpdPath, l_ec);
124 if (l_ec) // error code
125 {
126 o_errCode = error_code::FILE_SYSTEM_ERROR;
127 return constants::FAILURE;
128 }
129 }
130
131 std::ofstream l_badVpdFileStream(l_badVpdPath, std::ofstream::binary);
132 if (!l_badVpdFileStream.is_open())
133 {
134 o_errCode = error_code::FILE_ACCESS_ERROR;
135 return constants::FAILURE;
136 }
137
138 l_badVpdFileStream.write(
139 reinterpret_cast<const char*>(i_vpdVector.data()),
140 i_vpdVector.size());
141
142 l_rc = constants::SUCCESS;
143 }
144 catch (const std::exception& l_ex)
145 {
146 o_errCode = error_code::STANDARD_EXCEPTION;
147 }
148 return l_rc;
149 }
150
151 /**
152 * @brief An API to read value of a keyword.
153 *
154 *
155 * @param[in] i_kwdValueMap - A map having Kwd value pair.
156 * @param[in] i_kwd - keyword name.
157 * @param[out] o_errCode - To set error code in case of error.
158 *
159 * @return On success returns value of the keyword read from map, otherwise
160 * returns empty string.
161 */
getKwVal(const types::IPZKwdValueMap & i_kwdValueMap,const std::string & i_kwd,uint16_t & o_errCode)162 inline std::string getKwVal(const types::IPZKwdValueMap& i_kwdValueMap,
163 const std::string& i_kwd,
164 uint16_t& o_errCode) noexcept
165 {
166 o_errCode = 0;
167 std::string l_kwdValue;
168 if (i_kwd.empty() || i_kwdValueMap.empty())
169 {
170 o_errCode = error_code::INVALID_INPUT_PARAMETER;
171 return l_kwdValue;
172 }
173
174 auto l_itrToKwd = i_kwdValueMap.find(i_kwd);
175 if (l_itrToKwd != i_kwdValueMap.end())
176 {
177 l_kwdValue = l_itrToKwd->second;
178 }
179 else
180 {
181 o_errCode = error_code::KEYWORD_NOT_FOUND;
182 }
183
184 return l_kwdValue;
185 }
186
187 /**
188 * @brief An API to process encoding of a keyword.
189 *
190 * @param[in] i_keyword - Keyword to be processed.
191 * @param[in] i_encoding - Type of encoding.
192 * @param[out] o_errCode - To set error code in case of error.
193 *
194 * @return Value after being processed for encoded type.
195 */
encodeKeyword(const std::string & i_keyword,const std::string & i_encoding,uint16_t & o_errCode)196 inline std::string encodeKeyword(const std::string& i_keyword,
197 const std::string& i_encoding,
198 uint16_t& o_errCode) noexcept
199 {
200 o_errCode = 0;
201 if (i_keyword.empty())
202 {
203 o_errCode = error_code::INVALID_INPUT_PARAMETER;
204 return std::string{};
205 }
206
207 // Default value is keyword value
208 std::string l_result(i_keyword.begin(), i_keyword.end());
209
210 if (i_encoding.empty())
211 {
212 return l_result;
213 }
214
215 try
216 {
217 if (i_encoding == "MAC")
218 {
219 l_result.clear();
220 size_t l_firstByte = i_keyword[0];
221
222 auto l_hexValue = commonUtility::toHex(l_firstByte >> 4);
223
224 if (!l_hexValue)
225 {
226 o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
227 return std::string{};
228 }
229
230 l_result += l_hexValue;
231
232 l_hexValue = commonUtility::toHex(l_firstByte & 0x0f);
233
234 if (!l_hexValue)
235 {
236 o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
237 return std::string{};
238 }
239
240 l_result += l_hexValue;
241
242 for (size_t i = 1; i < i_keyword.size(); ++i)
243 {
244 l_result += ":";
245
246 l_hexValue = commonUtility::toHex(i_keyword[i] >> 4);
247
248 if (!l_hexValue)
249 {
250 o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
251 return std::string{};
252 }
253
254 l_result += l_hexValue;
255
256 l_hexValue = commonUtility::toHex(i_keyword[i] & 0x0f);
257
258 if (!l_hexValue)
259 {
260 o_errCode = error_code::OUT_OF_BOUND_EXCEPTION;
261 return std::string{};
262 }
263
264 l_result += l_hexValue;
265 }
266 }
267 else if (i_encoding == "DATE")
268 {
269 // Date, represent as
270 // <year>-<month>-<day> <hour>:<min>
271 l_result.clear();
272 static constexpr uint8_t skipPrefix = 3;
273
274 auto strItr = i_keyword.begin();
275 advance(strItr, skipPrefix);
276 for_each(strItr, i_keyword.end(),
277 [&l_result](size_t c) { l_result += c; });
278
279 l_result.insert(constants::BD_YEAR_END, 1, '-');
280 l_result.insert(constants::BD_MONTH_END, 1, '-');
281 l_result.insert(constants::BD_DAY_END, 1, ' ');
282 l_result.insert(constants::BD_HOUR_END, 1, ':');
283 }
284 }
285 catch (const std::exception& l_ex)
286 {
287 l_result.clear();
288 o_errCode = error_code::STANDARD_EXCEPTION;
289 }
290
291 return l_result;
292 }
293
294 /**
295 * @brief Helper function to insert or merge in map.
296 *
297 * This method checks in an interface if the given interface exists. If the
298 * interface key already exists, property map is inserted corresponding to it.
299 * If the key does'nt exist then given interface and property map pair is newly
300 * created. If the property present in propertymap already exist in the
301 * InterfaceMap, then the new property value is ignored.
302 *
303 * @param[in,out] io_map - Interface map.
304 * @param[in] i_interface - Interface to be processed.
305 * @param[in] i_propertyMap - new property map that needs to be emplaced.
306 * @param[out] o_errCode - To set error code in case of error.
307 *
308 * @return On success returns 0, otherwise returns -1.
309 */
insertOrMerge(types::InterfaceMap & io_map,const std::string & i_interface,types::PropertyMap && i_propertyMap,uint16_t & o_errCode)310 inline int insertOrMerge(types::InterfaceMap& io_map,
311 const std::string& i_interface,
312 types::PropertyMap&& i_propertyMap,
313 uint16_t& o_errCode) noexcept
314 {
315 o_errCode = 0;
316 int l_rc{constants::FAILURE};
317
318 try
319 {
320 if (io_map.find(i_interface) != io_map.end())
321 {
322 auto& l_prop = io_map.at(i_interface);
323 std::for_each(i_propertyMap.begin(), i_propertyMap.end(),
324 [&l_prop](auto l_keyValue) {
325 l_prop[l_keyValue.first] = l_keyValue.second;
326 });
327 }
328 else
329 {
330 io_map.emplace(i_interface, i_propertyMap);
331 }
332
333 l_rc = constants::SUCCESS;
334 }
335 catch (const std::exception& l_ex)
336 {
337 o_errCode = error_code::STANDARD_EXCEPTION;
338 }
339 return l_rc;
340 }
341
342 /**
343 * @brief API to expand unexpanded location code with ND/SC support.
344 *
345 * Supports: Ufcs-ND0-SCx, Ufcs-ND0-SCxx, Ufcs-ND0-NDx, Ufcs-ND0-NDxx (with
346 * optional suffixes)
347 *
348 * @param[in] unexpandedLocationCode - Unexpanded location code.
349 * @param[in] parsedVpdMap - Parsed VPD map.
350 * @param[out] o_errCode - To set error code in case of error.
351 * @return Expanded location code. In case of error, unexpanded is returned.
352 */
getExpandedLocationCode(const std::string & unexpandedLocationCode,const types::VPDMapVariant & parsedVpdMap,uint16_t & o_errCode)353 inline std::string getExpandedLocationCode(
354 const std::string& unexpandedLocationCode,
355 const types::VPDMapVariant& parsedVpdMap, uint16_t& o_errCode)
356 {
357 o_errCode = 0;
358 if (unexpandedLocationCode.empty())
359 {
360 o_errCode = error_code::INVALID_INPUT_PARAMETER;
361 return unexpandedLocationCode;
362 }
363 try
364 {
365 // Determine location code type and position
366 size_t pos = unexpandedLocationCode.find("fcs");
367 bool isFcs = (pos != std::string::npos);
368 if (!isFcs)
369 {
370 pos = unexpandedLocationCode.find("mts");
371 if (pos == std::string::npos)
372 {
373 o_errCode = error_code::FAILED_TO_DETECT_LOCATION_CODE_TYPE;
374 return unexpandedLocationCode;
375 }
376 }
377 // Set record and keyword parameters based on type
378 const std::string& recordName =
379 isFcs ? constants::recVCEN : constants::recVSYS;
380 const std::string& kwd1 = isFcs ? constants::kwdFC : constants::kwdTM;
381 const std::string& kwdInterface =
382 isFcs ? constants::vcenInf : constants::vsysInf;
383 const std::string kwd2 = constants::kwdSE;
384 // Retrieve keyword values
385 std::string firstKwdValue, secondKwdValue;
386 bool useDBus = true;
387 // Try to get from parsed VPD map first
388 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
389 {
390 // Check if map is not empty and contains the required record
391 if (!ipzVpdMap->empty())
392 {
393 auto recordItr = ipzVpdMap->find(recordName);
394 if (recordItr != ipzVpdMap->end())
395 {
396 // Map has the record, use it
397 firstKwdValue =
398 getKwVal(recordItr->second, kwd1, o_errCode);
399 if (o_errCode != 0)
400 return unexpandedLocationCode;
401 secondKwdValue =
402 getKwVal(recordItr->second, kwd2, o_errCode);
403 if (o_errCode != 0)
404 return unexpandedLocationCode;
405 useDBus = false;
406 }
407 }
408 }
409 // Fallback to DBus if map is empty or doesn't have record
410 if (useDBus)
411 {
412 auto mapperRetValue = dbusUtility::getObjectMap(
413 std::string(constants::systemVpdInvPath), {kwdInterface});
414 if (mapperRetValue.empty())
415 {
416 o_errCode = error_code::DBUS_FAILURE;
417 return unexpandedLocationCode;
418 }
419 const std::string& serviceName = std::get<0>(mapperRetValue[0]);
420 // Helper lambda to read and convert DBus property
421 auto readKwdValue = [&](const std::string& kwd) -> std::string {
422 auto retVal = dbusUtility::readDbusProperty(
423 serviceName, std::string(constants::systemVpdInvPath),
424 kwdInterface, kwd);
425 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
426 {
427 return std::string(
428 reinterpret_cast<const char*>(kwdVal->data()),
429 kwdVal->size());
430 }
431 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
432 return "";
433 };
434 firstKwdValue = readKwdValue(kwd1);
435 if (o_errCode != 0)
436 return unexpandedLocationCode;
437 secondKwdValue = readKwdValue(kwd2);
438 if (o_errCode != 0)
439 return unexpandedLocationCode;
440 }
441 // Build expanded location code
442 std::string expanded = unexpandedLocationCode;
443 if (isFcs)
444 {
445 // FCS: Must have -NDx, -NDxx, -SCx, or -SCxx pattern
446 std::string suffix = unexpandedLocationCode.substr(pos + 3);
447 std::regex ndScPattern(R"(^-((ND|SC)\d{1,2})(-.*)?)");
448 std::smatch match;
449 if (!std::regex_search(suffix, match, ndScPattern))
450 {
451 o_errCode = error_code::INVALID_LOCATION_CODE_FORMAT;
452 return unexpandedLocationCode;
453 }
454 // Build: FC[0:4].NDx.SE or FC[0:4].SCxx.SE (+ optional suffix)
455 expanded.replace(pos, 3 + suffix.length(),
456 firstKwdValue.substr(0, 4) + "." + match[1].str() +
457 "." + secondKwdValue + match[3].str());
458 }
459 else
460 {
461 // MTS: Replace dashes with dots in TM value
462 std::replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
463 expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
464 }
465 return expanded;
466 }
467 catch (const std::exception& ex)
468 {
469 o_errCode = error_code::STANDARD_EXCEPTION;
470 return unexpandedLocationCode;
471 }
472 }
473
474 #if 0
475 /**
476 * @brief API to expand unpanded location code.
477 *
478 * Note: The API handles all the exception internally, in case of any error
479 * unexpanded location code will be returned as it is.
480 *
481 * @param[in] unexpandedLocationCode - Unexpanded location code.
482 * @param[in] parsedVpdMap - Parsed VPD map.
483 * @param[out] o_errCode - To set error code in case of error.
484 * @return Expanded location code. In case of any error, unexpanded is returned
485 * as it is.
486 */
487 inline std::string getExpandedLocationCode(
488 const std::string& unexpandedLocationCode,
489 const types::VPDMapVariant& parsedVpdMap, uint16_t& o_errCode)
490 {
491 o_errCode = 0;
492 if (unexpandedLocationCode.empty())
493 {
494 o_errCode = error_code::INVALID_INPUT_PARAMETER;
495 return unexpandedLocationCode;
496 }
497
498 auto expanded{unexpandedLocationCode};
499
500 try
501 {
502 // Expanded location code is formed by combining two keywords
503 // depending on type in unexpanded one. Second one is always "SE".
504 std::string kwd1, kwd2{constants::kwdSE};
505
506 // interface to search for required keywords;
507 std::string kwdInterface;
508
509 // record which holds the required keywords.
510 std::string recordName;
511
512 auto pos = unexpandedLocationCode.find("fcs");
513 if (pos != std::string::npos)
514 {
515 kwd1 = constants::kwdFC;
516 kwdInterface = constants::vcenInf;
517 recordName = constants::recVCEN;
518 }
519 else
520 {
521 pos = unexpandedLocationCode.find("mts");
522 if (pos != std::string::npos)
523 {
524 kwd1 = constants::kwdTM;
525 kwdInterface = constants::vsysInf;
526 recordName = constants::recVSYS;
527 }
528 else
529 {
530 o_errCode = error_code::FAILED_TO_DETECT_LOCATION_CODE_TYPE;
531 return expanded;
532 }
533 }
534
535 std::string firstKwdValue, secondKwdValue;
536
537 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
538 ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
539 {
540 auto itrToVCEN = (*ipzVpdMap).find(recordName);
541 firstKwdValue = getKwVal(itrToVCEN->second, kwd1, o_errCode);
542 if (firstKwdValue.empty())
543 {
544 o_errCode = error_code::KEYWORD_NOT_FOUND;
545 return expanded;
546 }
547
548 secondKwdValue = getKwVal(itrToVCEN->second, kwd2, o_errCode);
549 if (secondKwdValue.empty())
550 {
551 o_errCode = error_code::KEYWORD_NOT_FOUND;
552 return expanded;
553 }
554 }
555 else
556 {
557 std::vector<std::string> interfaceList = {kwdInterface};
558
559 types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
560 std::string(constants::systemVpdInvPath), interfaceList);
561
562 if (mapperRetValue.empty())
563 {
564 o_errCode = error_code::DBUS_FAILURE;
565 return expanded;
566 }
567
568 const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
569
570 auto retVal = dbusUtility::readDbusProperty(
571 serviceName, std::string(constants::systemVpdInvPath),
572 kwdInterface, kwd1);
573
574 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
575 {
576 firstKwdValue.assign(
577 reinterpret_cast<const char*>(kwdVal->data()),
578 kwdVal->size());
579 }
580 else
581 {
582 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
583 return expanded;
584 }
585
586 retVal = dbusUtility::readDbusProperty(
587 serviceName, std::string(constants::systemVpdInvPath),
588 kwdInterface, kwd2);
589
590 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
591 {
592 secondKwdValue.assign(
593 reinterpret_cast<const char*>(kwdVal->data()),
594 kwdVal->size());
595 }
596 else
597 {
598 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
599 return expanded;
600 }
601 }
602
603 if (unexpandedLocationCode.find("fcs") != std::string::npos)
604 {
605 // TODO: See if ND0 can be placed in the JSON
606 expanded.replace(
607 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
608 }
609 else
610 {
611 replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
612 expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
613 }
614 }
615 catch (const std::exception& ex)
616 {
617 o_errCode = error_code::STANDARD_EXCEPTION;
618 }
619
620 return expanded;
621 }
622
623 #endif
624
625 /**
626 * @brief An API to get VPD in a vector.
627 *
628 * The vector is required by the respective parser to fill the VPD map.
629 * Note: API throws exception in case of failure. Caller needs to handle.
630 *
631 * @param[in] vpdFilePath - EEPROM path of the FRU.
632 * @param[out] vpdVector - VPD in vector form.
633 * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
634 * @param[out] o_errCode - To set error code in case of error.
635 */
getVpdDataInVector(const std::string & vpdFilePath,types::BinaryVector & vpdVector,size_t & vpdStartOffset,uint16_t & o_errCode)636 inline void getVpdDataInVector(const std::string& vpdFilePath,
637 types::BinaryVector& vpdVector,
638 size_t& vpdStartOffset, uint16_t& o_errCode)
639 {
640 o_errCode = 0;
641 if (vpdFilePath.empty())
642 {
643 o_errCode = error_code::INVALID_INPUT_PARAMETER;
644 return;
645 }
646
647 try
648 {
649 std::fstream vpdFileStream;
650 vpdFileStream.exceptions(
651 std::ifstream::badbit | std::ifstream::failbit);
652 vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
653 auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
654 static_cast<uintmax_t>(65504));
655 vpdVector.resize(vpdSizeToRead);
656
657 vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
658 vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
659 vpdSizeToRead);
660
661 vpdVector.resize(vpdFileStream.gcount());
662 vpdFileStream.clear(std::ios_base::eofbit);
663 }
664 catch (const std::ifstream::failure& fail)
665 {
666 o_errCode = error_code::FILE_SYSTEM_ERROR;
667 return;
668 }
669 }
670
671 /**
672 * @brief An API to get D-bus representation of given VPD keyword.
673 *
674 * @param[in] i_keywordName - VPD keyword name.
675 * @param[out] o_errCode - To set error code in case of error.
676 *
677 * @return D-bus representation of given keyword.
678 */
getDbusPropNameForGivenKw(const std::string & i_keywordName,uint16_t & o_errCode)679 inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName,
680 uint16_t& o_errCode)
681 {
682 o_errCode = 0;
683 if (i_keywordName.empty())
684 {
685 o_errCode = error_code::INVALID_INPUT_PARAMETER;
686 return std::string{};
687 }
688 // Check for "#" prefixed VPD keyword.
689 if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
690 (i_keywordName.at(0) == constants::POUND_KW))
691 {
692 // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
693 // prefixed keywords.
694 return (std::string(constants::POUND_KW_PREFIX) +
695 i_keywordName.substr(1));
696 }
697
698 // Return the keyword name back, if D-bus representation is same as the VPD
699 // keyword name.
700 return i_keywordName;
701 }
702
703 /**
704 * @brief API to find CCIN in parsed VPD map.
705 *
706 * Few FRUs need some special handling. To identify those FRUs CCIN are used.
707 * The API will check from parsed VPD map if the FRU is the one with desired
708 * CCIN.
709 *
710 * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
711 * @param[in] i_parsedVpdMap - Parsed VPD map.
712 * @param[out] o_errCode - To set error code in case of error.
713 *
714 * @return True if found, false otherwise.
715 */
findCcinInVpd(const nlohmann::json & i_JsonObject,const types::VPDMapVariant & i_parsedVpdMap,uint16_t & o_errCode)716 inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
717 const types::VPDMapVariant& i_parsedVpdMap,
718 uint16_t& o_errCode) noexcept
719 {
720 o_errCode = 0;
721 bool l_rc{false};
722 try
723 {
724 if (i_JsonObject.empty() ||
725 std::holds_alternative<std::monostate>(i_parsedVpdMap))
726 {
727 o_errCode = error_code::INVALID_INPUT_PARAMETER;
728 return l_rc;
729 }
730
731 if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
732 {
733 auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
734 if (l_itrToRec == (*l_ipzVPDMap).end())
735 {
736 o_errCode = error_code::RECORD_NOT_FOUND;
737 return l_rc;
738 }
739
740 std::string l_ccinFromVpd{vpdSpecificUtility::getKwVal(
741 l_itrToRec->second, "CC", o_errCode)};
742 if (l_ccinFromVpd.empty())
743 {
744 o_errCode = error_code::KEYWORD_NOT_FOUND;
745 return l_rc;
746 }
747
748 transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
749 l_ccinFromVpd.begin(), ::toupper);
750
751 for (std::string l_ccinValue : i_JsonObject["ccin"])
752 {
753 transform(l_ccinValue.begin(), l_ccinValue.end(),
754 l_ccinValue.begin(), ::toupper);
755
756 if (l_ccinValue.compare(l_ccinFromVpd) ==
757 constants::STR_CMP_SUCCESS)
758 {
759 // CCIN found
760 l_rc = true;
761 }
762 }
763
764 if (!l_rc)
765 {
766 logging::logMessage("No match found for CCIN");
767 }
768 }
769 else
770 {
771 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
772 }
773 }
774 catch (const std::exception& l_ex)
775 {
776 o_errCode = error_code::STANDARD_EXCEPTION;
777 }
778 return l_rc;
779 }
780
781 /**
782 * @brief API to reset data of a FRU populated under PIM.
783 *
784 * This API resets the data for particular interfaces of a FRU under PIM.
785 *
786 * @param[in] i_objectPath - DBus object path of the FRU.
787 * @param[in] io_interfaceMap - Interface and its properties map.
788 * @param[in] i_clearPresence - Indicates whether to clear present property or
789 * not.
790 * @param[out] o_errCode - To set error code in case of error.
791 */
resetDataUnderPIM(const std::string & i_objectPath,types::InterfaceMap & io_interfaceMap,bool i_clearPresence,uint16_t & o_errCode)792 inline void resetDataUnderPIM(const std::string& i_objectPath,
793 types::InterfaceMap& io_interfaceMap,
794 bool i_clearPresence, uint16_t& o_errCode)
795 {
796 o_errCode = 0;
797 if (i_objectPath.empty())
798 {
799 o_errCode = error_code::INVALID_INPUT_PARAMETER;
800 return;
801 }
802
803 try
804 {
805 std::vector<std::string> l_interfaces;
806 const types::MapperGetObject& l_getObjectMap =
807 dbusUtility::getObjectMap(i_objectPath, l_interfaces);
808
809 const std::vector<std::string>& l_vpdRelatedInterfaces{
810 constants::operationalStatusInf, constants::inventoryItemInf,
811 constants::assetInf, constants::vpdCollectionInterface};
812
813 for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
814 {
815 if (l_service.compare(constants::pimServiceName) !=
816 constants::STR_CMP_SUCCESS)
817 {
818 continue;
819 }
820
821 for (const auto& l_interface : l_interfaceList)
822 {
823 if ((l_interface.find(constants::ipzVpdInf) !=
824 std::string::npos &&
825 l_interface != constants::locationCodeInf) ||
826 ((std::find(l_vpdRelatedInterfaces.begin(),
827 l_vpdRelatedInterfaces.end(), l_interface)) !=
828 l_vpdRelatedInterfaces.end()))
829 {
830 const types::PropertyMap& l_propertyValueMap =
831 dbusUtility::getPropertyMap(l_service, i_objectPath,
832 l_interface);
833
834 types::PropertyMap l_propertyMap;
835
836 for (const auto& l_aProperty : l_propertyValueMap)
837 {
838 const std::string& l_propertyName = l_aProperty.first;
839 const auto& l_propertyValue = l_aProperty.second;
840
841 if (std::holds_alternative<types::BinaryVector>(
842 l_propertyValue))
843 {
844 l_propertyMap.emplace(l_propertyName,
845 types::BinaryVector{});
846 }
847 else if (std::holds_alternative<std::string>(
848 l_propertyValue))
849 {
850 if (l_propertyName.compare("Status") ==
851 constants::STR_CMP_SUCCESS)
852 {
853 l_propertyMap.emplace(
854 l_propertyName,
855 constants::vpdCollectionNotStarted);
856 l_propertyMap.emplace("StartTime", 0);
857 l_propertyMap.emplace("CompletedTime", 0);
858 }
859 else if (l_propertyName.compare("PrettyName") ==
860 constants::STR_CMP_SUCCESS)
861 {
862 // The FRU name is constant and independent of
863 // its presence state. So, it should not get
864 // reset.
865 continue;
866 }
867 else
868 {
869 l_propertyMap.emplace(l_propertyName,
870 std::string{});
871 }
872 }
873 else if (std::holds_alternative<bool>(l_propertyValue))
874 {
875 if (l_propertyName.compare("Present") ==
876 constants::STR_CMP_SUCCESS)
877 {
878 if (i_clearPresence)
879 {
880 l_propertyMap.emplace(l_propertyName,
881 false);
882 }
883 }
884 else if (l_propertyName.compare("Functional") ==
885 constants::STR_CMP_SUCCESS)
886 {
887 // Since FRU is not present functional property
888 // is considered as true.
889 l_propertyMap.emplace(l_propertyName, true);
890 }
891 }
892 }
893 io_interfaceMap.emplace(l_interface,
894 std::move(l_propertyMap));
895 }
896 }
897 }
898 }
899 catch (const std::exception& l_ex)
900 {
901 o_errCode = error_code::STANDARD_EXCEPTION;
902 }
903 }
904
905 /**
906 * @brief API to detect pass1 planar type.
907 *
908 * Based on HW version and IM keyword, This API detects is it is a pass1 planar
909 * or not.
910 * @param[out] o_errCode - To set error code in case of error.
911 *
912 * @return True if pass 1 planar, false otherwise.
913 */
isPass1Planar(uint16_t & o_errCode)914 inline bool isPass1Planar(uint16_t& o_errCode) noexcept
915 {
916 o_errCode = 0;
917 bool l_rc{false};
918 const auto l_hwVar = dbusUtility::readDbusProperty(
919 constants::pimServiceName, constants::systemVpdInvPath,
920 constants::viniInf, constants::kwdHW);
921
922 auto l_hwVer = std::get_if<types::BinaryVector>(&l_hwVar);
923 if (l_hwVer == nullptr)
924 {
925 o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
926 return l_rc;
927 }
928
929 const auto l_imVar = dbusUtility::readDbusProperty(
930 constants::pimServiceName, constants::systemVpdInvPath,
931 constants::vsbpInf, constants::kwdIM);
932
933 auto l_imValue = std::get_if<types::BinaryVector>(&l_imVar);
934 if (l_imValue == nullptr)
935 {
936 o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
937 return l_rc;
938 }
939
940 if (l_hwVer->size() != constants::VALUE_2)
941 {
942 o_errCode = error_code::INVALID_KEYWORD_LENGTH;
943 return l_rc;
944 }
945
946 if (l_imValue->size() != constants::VALUE_4)
947 {
948 o_errCode = error_code::INVALID_KEYWORD_LENGTH;
949 return l_rc;
950 }
951
952 const types::BinaryVector l_everest{80, 00, 48, 00};
953 const types::BinaryVector l_fuji{96, 00, 32, 00};
954
955 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
956 {
957 if ((*l_hwVer).at(1) < constants::VALUE_21)
958 {
959 l_rc = true;
960 }
961 }
962 else if ((*l_hwVer).at(1) < constants::VALUE_2)
963 {
964 l_rc = true;
965 }
966
967 return l_rc;
968 }
969
970 /**
971 * @brief API to detect if system configuration is that of PowerVS system.
972 *
973 * @param[in] i_imValue - IM value of the system.
974 * @param[out] o_errCode - To set error code in case of error.
975 * @return true if it is PowerVS configuration, false otherwise.
976 */
isPowerVsConfiguration(const types::BinaryVector & i_imValue,uint16_t & o_errCode)977 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue,
978 uint16_t& o_errCode)
979 {
980 o_errCode = 0;
981 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
982 {
983 o_errCode = error_code::INVALID_INPUT_PARAMETER;
984 return false;
985 }
986
987 // Should be a 0x5000XX series system.
988 if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
989 i_imValue.at(1) == constants::HEX_VALUE_00)
990 {
991 std::string l_imagePrefix = dbusUtility::getImagePrefix();
992
993 // Check image for 0x500030XX series.
994 if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
995 ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
996 (l_imagePrefix == constants::powerVsImagePrefix_NY)))
997 {
998 logging::logMessage("PowerVS configuration");
999 return true;
1000 }
1001
1002 // Check image for 0X500010XX series.
1003 if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
1004 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
1005 (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
1006 {
1007 logging::logMessage("PowerVS configuration");
1008 return true;
1009 }
1010 }
1011 return false;
1012 }
1013
1014 /**
1015 * @brief API to get CCIN for a given FRU from DBus.
1016 *
1017 * The API reads the CCIN for a FRU based on its inventory path.
1018 *
1019 * @param[in] i_invObjPath - Inventory path of the FRU.
1020 * @param[out] o_errCode - To set error code in case of error.
1021 *
1022 * @return CCIN of the FRU on success, empty string otherwise.
1023 */
getCcinFromDbus(const std::string & i_invObjPath,uint16_t & o_errCode)1024 inline std::string getCcinFromDbus(const std::string& i_invObjPath,
1025 uint16_t& o_errCode)
1026 {
1027 o_errCode = 0;
1028 try
1029 {
1030 if (i_invObjPath.empty())
1031 {
1032 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1033 return std::string{};
1034 }
1035
1036 const auto& l_retValue = dbusUtility::readDbusProperty(
1037 constants::pimServiceName, i_invObjPath, constants::viniInf,
1038 constants::kwdCCIN);
1039
1040 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
1041
1042 if (!l_ptrCcin)
1043 {
1044 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
1045 return std::string{};
1046 }
1047
1048 if ((*l_ptrCcin).size() != constants::VALUE_4)
1049 {
1050 o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
1051 return std::string{};
1052 }
1053
1054 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
1055 }
1056 catch (const std::exception& l_ex)
1057 {
1058 o_errCode = error_code::STANDARD_EXCEPTION;
1059 return std::string{};
1060 }
1061 }
1062
1063 /**
1064 * @brief API to check if the current running image is a powerVS image.
1065 *
1066 * @return true if it is PowerVS image, false otherwise.
1067 */
isPowerVsImage()1068 inline bool isPowerVsImage()
1069 {
1070 std::string l_imagePrefix = dbusUtility::getImagePrefix();
1071
1072 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
1073 (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
1074 (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
1075 (l_imagePrefix == constants::powerVsImagePrefix_NZ))
1076 {
1077 return true;
1078 }
1079 return false;
1080 }
1081
1082 /**
1083 * @brief API to sync keyword update to inherited FRUs.
1084 *
1085 * For a given keyword update on a EEPROM path, this API syncs the keyword
1086 * update to all inherited FRUs' respective interface, property on PIM.
1087 *
1088 * @param[in] i_fruPath - EEPROM path of FRU.
1089 * @param[in] i_paramsToWriteData - Input details.
1090 * @param[in] i_sysCfgJsonObj - System config JSON.
1091 * @param[out] o_errCode - To set error code in case of error.
1092 *
1093 */
updateKwdOnInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1094 inline void updateKwdOnInheritedFrus(
1095 const std::string& i_fruPath,
1096 const types::WriteVpdParams& i_paramsToWriteData,
1097 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1098 {
1099 o_errCode = 0;
1100 try
1101 {
1102 if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1103 {
1104 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1105 return;
1106 }
1107
1108 if (!i_sysCfgJsonObj.contains("frus"))
1109 {
1110 o_errCode = error_code::INVALID_JSON;
1111 return;
1112 }
1113
1114 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1115 {
1116 o_errCode = error_code::FRU_PATH_NOT_FOUND;
1117 return;
1118 }
1119
1120 const types::IpzData* l_ipzData =
1121 std::get_if<types::IpzData>(&i_paramsToWriteData);
1122
1123 if (!l_ipzData)
1124 {
1125 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1126 return;
1127 }
1128 // iterate through all inventory paths for given EEPROM path,
1129 // except the base FRU.
1130 // if for an inventory path, "inherit" tag is true,
1131 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
1132 // property
1133
1134 types::ObjectMap l_objectInterfaceMap;
1135
1136 auto l_populateInterfaceMap =
1137 [&l_objectInterfaceMap,
1138 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
1139 // update inherited FRUs only
1140 if (l_Fru.value("inherit", true))
1141 {
1142 l_objectInterfaceMap.emplace(
1143 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1144 types::InterfaceMap{
1145 {std::string{constants::ipzVpdInf +
1146 std::get<0>(*l_ipzData)},
1147 types::PropertyMap{{std::get<1>(*l_ipzData),
1148 std::get<2>(*l_ipzData)}}}});
1149 }
1150 };
1151
1152 // iterate through all FRUs except the base FRU
1153 std::for_each(
1154 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
1155 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
1156
1157 if (!l_objectInterfaceMap.empty())
1158 {
1159 // Call method to update the dbus
1160 if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1161 {
1162 o_errCode = error_code::DBUS_FAILURE;
1163 return;
1164 }
1165 }
1166 }
1167 catch (const std::exception& l_ex)
1168 {
1169 o_errCode = error_code::STANDARD_EXCEPTION;
1170 return;
1171 }
1172 }
1173
1174 /**
1175 * @brief API to get common interface(s) properties corresponding to given
1176 * record and keyword.
1177 *
1178 * For a given record and keyword, this API finds the corresponding common
1179 * interfaces(s) properties from the system config JSON and populates an
1180 * interface map with the respective properties and values.
1181 *
1182 * @param[in] i_paramsToWriteData - Input details.
1183 * @param[in] i_commonInterfaceJson - Common interface JSON object.
1184 * @param[out] o_errCode - To set error code in case of error.
1185 *
1186 * @return Returns a map of common interface(s) and properties corresponding to
1187 * the record and keyword. An empty map is returned if no such common
1188 * interface(s) and properties are found.
1189 */
getCommonInterfaceProperties(const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_commonInterfaceJson,uint16_t & o_errCode)1190 inline types::InterfaceMap getCommonInterfaceProperties(
1191 const types::WriteVpdParams& i_paramsToWriteData,
1192 const nlohmann::json& i_commonInterfaceJson, uint16_t& o_errCode) noexcept
1193 {
1194 types::InterfaceMap l_interfaceMap;
1195 o_errCode = 0;
1196 try
1197 {
1198 if (i_commonInterfaceJson.empty())
1199 {
1200 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1201 return l_interfaceMap;
1202 }
1203
1204 const types::IpzData* l_ipzData =
1205 std::get_if<types::IpzData>(&i_paramsToWriteData);
1206
1207 if (!l_ipzData)
1208 {
1209 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1210 return l_interfaceMap;
1211 }
1212
1213 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
1214 &l_interfaceMap, &o_errCode](
1215 const auto& l_interfacesPropPair) {
1216 if (l_interfacesPropPair.value().empty())
1217 {
1218 return;
1219 }
1220
1221 // find matching property value pair
1222 const auto l_matchPropValuePairIt = std::find_if(
1223 l_interfacesPropPair.value().items().begin(),
1224 l_interfacesPropPair.value().items().end(),
1225 [&l_ipzData](const auto& l_propValuePair) {
1226 return (l_propValuePair.value().value("recordName", "") ==
1227 std::get<0>(*l_ipzData) &&
1228 l_propValuePair.value().value("keywordName", "") ==
1229 std::get<1>(*l_ipzData));
1230 });
1231
1232 if (l_matchPropValuePairIt !=
1233 l_interfacesPropPair.value().items().end())
1234 {
1235 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(),
1236 std::get<2>(*l_ipzData).end());
1237
1238 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword(
1239 l_kwd, l_matchPropValuePairIt.value().value("encoding", ""),
1240 o_errCode);
1241
1242 if (l_encodedValue.empty() && o_errCode)
1243 {
1244 logging::logMessage(
1245 "Failed to get encoded value for keyword : " + l_kwd +
1246 ", error : " + commonUtility::getErrCodeMsg(o_errCode));
1247 }
1248
1249 // add property map to interface map
1250 l_interfaceMap.emplace(
1251 l_interfacesPropPair.key(),
1252 types::PropertyMap{
1253 {l_matchPropValuePairIt.key(), l_encodedValue}});
1254 }
1255 };
1256
1257 if (!i_commonInterfaceJson.empty())
1258 {
1259 // iterate through all common interfaces and populate interface map
1260 std::for_each(i_commonInterfaceJson.items().begin(),
1261 i_commonInterfaceJson.items().end(),
1262 l_populateInterfaceMap);
1263 }
1264 }
1265 catch (const std::exception& l_ex)
1266 {
1267 o_errCode = error_code::STANDARD_EXCEPTION;
1268 }
1269 return l_interfaceMap;
1270 }
1271
1272 /**
1273 * @brief API to update common interface(s) properties when keyword is updated.
1274 *
1275 * For a given keyword update on a EEPROM path, this API syncs the keyword
1276 * update to respective common interface(s) properties of the base FRU and all
1277 * inherited FRUs.
1278 *
1279 * @param[in] i_fruPath - EEPROM path of FRU.
1280 * @param[in] i_paramsToWriteData - Input details.
1281 * @param[in] i_sysCfgJsonObj - System config JSON.
1282 * @param[out] o_errCode - To set error code in case of error.
1283 */
updateCiPropertyOfInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1284 inline void updateCiPropertyOfInheritedFrus(
1285 const std::string& i_fruPath,
1286 const types::WriteVpdParams& i_paramsToWriteData,
1287 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1288 {
1289 o_errCode = 0;
1290 try
1291 {
1292 if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1293 {
1294 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1295 return;
1296 }
1297
1298 if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1299 {
1300 // no common interfaces in JSON, nothing to do
1301 return;
1302 }
1303
1304 if (!i_sysCfgJsonObj.contains("frus"))
1305 {
1306 o_errCode = error_code::INVALID_JSON;
1307 return;
1308 }
1309
1310 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1311 {
1312 o_errCode = error_code::FRU_PATH_NOT_FOUND;
1313 return;
1314 }
1315
1316 if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1317 {
1318 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1319 return;
1320 }
1321
1322 // iterate through all inventory paths for given EEPROM path,
1323 // if for an inventory path, "inherit" tag is true,
1324 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
1325 // property
1326
1327 types::ObjectMap l_objectInterfaceMap;
1328
1329 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1330 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"],
1331 o_errCode);
1332
1333 if (l_interfaceMap.empty())
1334 {
1335 if (o_errCode)
1336 {
1337 logging::logMessage(
1338 "Failed to get common interface property list, error : " +
1339 commonUtility::getErrCodeMsg(o_errCode));
1340 }
1341 // nothing to do
1342 return;
1343 }
1344
1345 auto l_populateObjectInterfaceMap =
1346 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1347 l_interfaceMap)](const auto& l_Fru) {
1348 if (l_Fru.value("inherit", true) &&
1349 l_Fru.contains("inventoryPath"))
1350 {
1351 l_objectInterfaceMap.emplace(
1352 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1353 l_interfaceMap);
1354 }
1355 };
1356
1357 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1358 i_sysCfgJsonObj["frus"][i_fruPath].end(),
1359 l_populateObjectInterfaceMap);
1360
1361 if (!l_objectInterfaceMap.empty())
1362 {
1363 // Call method to update the dbus
1364 if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1365 {
1366 o_errCode = error_code::DBUS_FAILURE;
1367 return;
1368 }
1369 }
1370 }
1371 catch (const std::exception& l_ex)
1372 {
1373 o_errCode = error_code::STANDARD_EXCEPTION;
1374 }
1375 }
1376
1377 /**
1378 * @brief API to convert write VPD parameters to a string.
1379 *
1380 * @param[in] i_paramsToWriteData - write VPD parameters.
1381 * @param[out] o_errCode - To set error code in case of error.
1382 *
1383 * @return On success returns string representation of write VPD parameters,
1384 * otherwise returns an empty string.
1385 */
convertWriteVpdParamsToString(const types::WriteVpdParams & i_paramsToWriteData,uint16_t & o_errCode)1386 inline const std::string convertWriteVpdParamsToString(
1387 const types::WriteVpdParams& i_paramsToWriteData,
1388 uint16_t& o_errCode) noexcept
1389 {
1390 o_errCode = 0;
1391 try
1392 {
1393 if (const types::IpzData* l_ipzDataPtr =
1394 std::get_if<types::IpzData>(&i_paramsToWriteData))
1395 {
1396 return std::string{
1397 "Record: " + std::get<0>(*l_ipzDataPtr) +
1398 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " +
1399 commonUtility::convertByteVectorToHex(
1400 std::get<2>(*l_ipzDataPtr))};
1401 }
1402 else if (const types::KwData* l_kwDataPtr =
1403 std::get_if<types::KwData>(&i_paramsToWriteData))
1404 {
1405 return std::string{
1406 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " +
1407 commonUtility::convertByteVectorToHex(
1408 std::get<1>(*l_kwDataPtr))};
1409 }
1410 else
1411 {
1412 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1413 }
1414 }
1415 catch (const std::exception& l_ex)
1416 {
1417 o_errCode = error_code::STANDARD_EXCEPTION;
1418 }
1419 return std::string{};
1420 }
1421
1422 /**
1423 * @brief An API to read IM value from VPD.
1424 *
1425 * @param[in] i_parsedVpd - Parsed VPD.
1426 * @param[out] o_errCode - To set error code in case of error.
1427 */
getIMValue(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1428 inline std::string getIMValue(const types::IPZVpdMap& i_parsedVpd,
1429 uint16_t& o_errCode) noexcept
1430 {
1431 o_errCode = 0;
1432 std::ostringstream l_imData;
1433 try
1434 {
1435 if (i_parsedVpd.empty())
1436 {
1437 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1438 return {};
1439 }
1440
1441 const auto& l_itrToVSBP = i_parsedVpd.find("VSBP");
1442 if (l_itrToVSBP == i_parsedVpd.end())
1443 {
1444 o_errCode = error_code::RECORD_NOT_FOUND;
1445 return {};
1446 }
1447
1448 const auto& l_itrToIM = (l_itrToVSBP->second).find("IM");
1449 if (l_itrToIM == (l_itrToVSBP->second).end())
1450 {
1451 o_errCode = error_code::KEYWORD_NOT_FOUND;
1452 return {};
1453 }
1454
1455 types::BinaryVector l_imVal;
1456 std::copy(l_itrToIM->second.begin(), l_itrToIM->second.end(),
1457 back_inserter(l_imVal));
1458
1459 for (auto& l_aByte : l_imVal)
1460 {
1461 l_imData << std::setw(2) << std::setfill('0') << std::hex
1462 << static_cast<int>(l_aByte);
1463 }
1464 }
1465 catch (const std::exception& l_ex)
1466 {
1467 logging::logMessage("Failed to get IM value with exception:" +
1468 std::string(l_ex.what()));
1469 o_errCode = error_code::STANDARD_EXCEPTION;
1470 }
1471
1472 return l_imData.str();
1473 }
1474
1475 /**
1476 * @brief An API to read HW version from VPD.
1477 *
1478 * @param[in] i_parsedVpd - Parsed VPD.
1479 * @param[out] o_errCode - To set error code in case of error.
1480 */
getHWVersion(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1481 inline std::string getHWVersion(const types::IPZVpdMap& i_parsedVpd,
1482 uint16_t& o_errCode) noexcept
1483 {
1484 o_errCode = 0;
1485 std::ostringstream l_hwString;
1486 try
1487 {
1488 if (i_parsedVpd.empty())
1489 {
1490 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1491 return {};
1492 }
1493
1494 const auto& l_itrToVINI = i_parsedVpd.find("VINI");
1495 if (l_itrToVINI == i_parsedVpd.end())
1496 {
1497 o_errCode = error_code::RECORD_NOT_FOUND;
1498 return {};
1499 }
1500
1501 const auto& l_itrToHW = (l_itrToVINI->second).find("HW");
1502 if (l_itrToHW == (l_itrToVINI->second).end())
1503 {
1504 o_errCode = error_code::KEYWORD_NOT_FOUND;
1505 return {};
1506 }
1507
1508 types::BinaryVector l_hwVal;
1509 std::copy(l_itrToHW->second.begin(), l_itrToHW->second.end(),
1510 back_inserter(l_hwVal));
1511
1512 // The planar pass only comes from the LSB of the HW keyword,
1513 // where as the MSB is used for other purposes such as signifying clock
1514 // termination.
1515 l_hwVal[0] = 0x00;
1516
1517 for (auto& l_aByte : l_hwVal)
1518 {
1519 l_hwString << std::setw(2) << std::setfill('0') << std::hex
1520 << static_cast<int>(l_aByte);
1521 }
1522 }
1523 catch (const std::exception& l_ex)
1524 {
1525 logging::logMessage("Failed to get HW version with exception:" +
1526 std::string(l_ex.what()));
1527 o_errCode = error_code::STANDARD_EXCEPTION;
1528 }
1529
1530 return l_hwString.str();
1531 }
1532
1533 /**
1534 * @brief An API to set VPD collection status for a fru.
1535 *
1536 * This API updates the CollectionStatus property of the given FRU with the
1537 * given value.
1538 *
1539 * @param[in] i_vpdPath - Fru path (EEPROM or Inventory path)
1540 * @param[in] i_value - State to set.
1541 * @param[in] i_sysCfgJsonObj - System config json object.
1542 * @param[out] o_errCode - To set error code in case of error.
1543 */
setCollectionStatusProperty(const std::string & i_vpdPath,const types::VpdCollectionStatus & i_value,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1544 inline void setCollectionStatusProperty(
1545 const std::string& i_vpdPath, const types::VpdCollectionStatus& i_value,
1546 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1547 {
1548 o_errCode = 0;
1549 if (i_vpdPath.empty())
1550 {
1551 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1552 return;
1553 }
1554
1555 if (i_sysCfgJsonObj.empty() || !i_sysCfgJsonObj.contains("frus"))
1556 {
1557 o_errCode = error_code::INVALID_JSON;
1558 return;
1559 }
1560
1561 types::PropertyMap l_timeStampMap;
1562 if (i_value == types::VpdCollectionStatus::Completed ||
1563 i_value == types::VpdCollectionStatus::Failed)
1564 {
1565 l_timeStampMap.emplace(
1566 "CompletedTime",
1567 types::DbusVariantType{commonUtility::getCurrentTimeSinceEpoch()});
1568 }
1569 else if (i_value == types::VpdCollectionStatus::InProgress)
1570 {
1571 l_timeStampMap.emplace(
1572 "StartTime",
1573 types::DbusVariantType{commonUtility::getCurrentTimeSinceEpoch()});
1574 }
1575 else if (i_value == types::VpdCollectionStatus::NotStarted)
1576 {
1577 l_timeStampMap.emplace("StartTime", 0);
1578 l_timeStampMap.emplace("CompletedTime", 0);
1579 }
1580
1581 types::ObjectMap l_objectInterfaceMap;
1582
1583 const auto& l_eepromPath =
1584 jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, i_vpdPath, o_errCode);
1585
1586 if (l_eepromPath.empty() || o_errCode)
1587 {
1588 return;
1589 }
1590
1591 for (const auto& l_Fru : i_sysCfgJsonObj["frus"][l_eepromPath])
1592 {
1593 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
1594
1595 types::PropertyMap l_propertyValueMap;
1596 l_propertyValueMap.emplace(
1597 "Status",
1598 types::CommonProgress::convertOperationStatusToString(i_value));
1599 l_propertyValueMap.insert(l_timeStampMap.begin(), l_timeStampMap.end());
1600
1601 types::InterfaceMap l_interfaces;
1602 vpdSpecificUtility::insertOrMerge(l_interfaces,
1603 types::CommonProgress::interface,
1604 move(l_propertyValueMap), o_errCode);
1605
1606 if (o_errCode)
1607 {
1608 Logger::getLoggerInstance()->logMessage(
1609 "Failed to insert value into map, error : " +
1610 commonUtility::getErrCodeMsg(o_errCode));
1611 return;
1612 }
1613
1614 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1615 std::move(l_interfaces));
1616 }
1617
1618 // Call dbus method to update on dbus
1619 if (!dbusUtility::publishVpdOnDBus(move(l_objectInterfaceMap)))
1620 {
1621 o_errCode = error_code::DBUS_FAILURE;
1622 return;
1623 }
1624 }
1625
1626 /**
1627 * @brief API to reset data of a FRU and its sub-FRU populated under PIM.
1628 *
1629 * The API resets the data for specific interfaces of a FRU and its sub-FRUs
1630 * under PIM.
1631 *
1632 * Note: i_vpdPath should be either the base inventory path or the EEPROM path.
1633 *
1634 * @param[in] i_vpdPath - EEPROM/root inventory path of the FRU.
1635 * @param[in] i_sysCfgJsonObj - system config JSON.
1636 * @param[out] o_errCode - To set error code in case of error.
1637 */
resetObjTreeVpd(const std::string & i_vpdPath,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1638 inline void resetObjTreeVpd(const std::string& i_vpdPath,
1639 const nlohmann::json& i_sysCfgJsonObj,
1640 uint16_t& o_errCode) noexcept
1641 {
1642 o_errCode = 0;
1643 if (i_vpdPath.empty() || i_sysCfgJsonObj.empty())
1644 {
1645 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1646 return;
1647 }
1648
1649 try
1650 {
1651 const std::string& l_fruPath = jsonUtility::getFruPathFromJson(
1652 i_sysCfgJsonObj, i_vpdPath, o_errCode);
1653
1654 if (o_errCode)
1655 {
1656 return;
1657 }
1658
1659 types::ObjectMap l_objectMap;
1660
1661 const auto& l_fruItems = i_sysCfgJsonObj["frus"][l_fruPath];
1662
1663 for (const auto& l_inventoryItem : l_fruItems)
1664 {
1665 const std::string& l_objectPath =
1666 l_inventoryItem.value("inventoryPath", "");
1667
1668 if (l_inventoryItem.value("synthesized", false))
1669 {
1670 continue;
1671 }
1672
1673 types::InterfaceMap l_interfaceMap;
1674 resetDataUnderPIM(l_objectPath, l_interfaceMap,
1675 l_inventoryItem.value("handlePresence", true),
1676 o_errCode);
1677
1678 if (o_errCode)
1679 {
1680 logging::logMessage(
1681 "Failed to get data to clear on DBus for path [" +
1682 l_objectPath +
1683 "], error : " + commonUtility::getErrCodeMsg(o_errCode));
1684
1685 continue;
1686 }
1687
1688 l_objectMap.emplace(l_objectPath, l_interfaceMap);
1689 }
1690
1691 if (!dbusUtility::publishVpdOnDBus(std::move(l_objectMap)))
1692 {
1693 o_errCode = error_code::DBUS_FAILURE;
1694 }
1695 }
1696 catch (const std::exception& l_ex)
1697 {
1698 logging::logMessage(
1699 "Failed to reset FRU data on DBus for FRU [" + i_vpdPath +
1700 "], error : " + std::string(l_ex.what()));
1701
1702 o_errCode = error_code::STANDARD_EXCEPTION;
1703 }
1704 }
1705 } // namespace vpdSpecificUtility
1706 } // namespace vpd
1707