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 unpanded location code.
344 *
345 * Note: The API handles all the exception internally, in case of any error
346 * unexpanded location code will be returned as it is.
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 any error, unexpanded is returned
352 * as it is.
353 */
getExpandedLocationCode(const std::string & unexpandedLocationCode,const types::VPDMapVariant & parsedVpdMap,uint16_t & o_errCode)354 inline std::string getExpandedLocationCode(
355 const std::string& unexpandedLocationCode,
356 const types::VPDMapVariant& parsedVpdMap, uint16_t& o_errCode)
357 {
358 o_errCode = 0;
359 if (unexpandedLocationCode.empty())
360 {
361 o_errCode = error_code::INVALID_INPUT_PARAMETER;
362 return unexpandedLocationCode;
363 }
364
365 auto expanded{unexpandedLocationCode};
366
367 try
368 {
369 // Expanded location code is formed by combining two keywords
370 // depending on type in unexpanded one. Second one is always "SE".
371 std::string kwd1, kwd2{constants::kwdSE};
372
373 // interface to search for required keywords;
374 std::string kwdInterface;
375
376 // record which holds the required keywords.
377 std::string recordName;
378
379 auto pos = unexpandedLocationCode.find("fcs");
380 if (pos != std::string::npos)
381 {
382 kwd1 = constants::kwdFC;
383 kwdInterface = constants::vcenInf;
384 recordName = constants::recVCEN;
385 }
386 else
387 {
388 pos = unexpandedLocationCode.find("mts");
389 if (pos != std::string::npos)
390 {
391 kwd1 = constants::kwdTM;
392 kwdInterface = constants::vsysInf;
393 recordName = constants::recVSYS;
394 }
395 else
396 {
397 o_errCode = error_code::FAILED_TO_DETECT_LOCATION_CODE_TYPE;
398 return expanded;
399 }
400 }
401
402 std::string firstKwdValue, secondKwdValue;
403
404 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
405 ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
406 {
407 auto itrToVCEN = (*ipzVpdMap).find(recordName);
408 firstKwdValue = getKwVal(itrToVCEN->second, kwd1, o_errCode);
409 if (firstKwdValue.empty())
410 {
411 o_errCode = error_code::KEYWORD_NOT_FOUND;
412 return expanded;
413 }
414
415 secondKwdValue = getKwVal(itrToVCEN->second, kwd2, o_errCode);
416 if (secondKwdValue.empty())
417 {
418 o_errCode = error_code::KEYWORD_NOT_FOUND;
419 return expanded;
420 }
421 }
422 else
423 {
424 std::vector<std::string> interfaceList = {kwdInterface};
425
426 types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
427 std::string(constants::systemVpdInvPath), interfaceList);
428
429 if (mapperRetValue.empty())
430 {
431 o_errCode = error_code::DBUS_FAILURE;
432 return expanded;
433 }
434
435 const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
436
437 auto retVal = dbusUtility::readDbusProperty(
438 serviceName, std::string(constants::systemVpdInvPath),
439 kwdInterface, kwd1);
440
441 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
442 {
443 firstKwdValue.assign(
444 reinterpret_cast<const char*>(kwdVal->data()),
445 kwdVal->size());
446 }
447 else
448 {
449 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
450 return expanded;
451 }
452
453 retVal = dbusUtility::readDbusProperty(
454 serviceName, std::string(constants::systemVpdInvPath),
455 kwdInterface, kwd2);
456
457 if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
458 {
459 secondKwdValue.assign(
460 reinterpret_cast<const char*>(kwdVal->data()),
461 kwdVal->size());
462 }
463 else
464 {
465 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
466 return expanded;
467 }
468 }
469
470 if (unexpandedLocationCode.find("fcs") != std::string::npos)
471 {
472 // TODO: See if ND0 can be placed in the JSON
473 expanded.replace(
474 pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
475 }
476 else
477 {
478 replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
479 expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
480 }
481 }
482 catch (const std::exception& ex)
483 {
484 o_errCode = error_code::STANDARD_EXCEPTION;
485 }
486
487 return expanded;
488 }
489
490 /**
491 * @brief An API to get VPD in a vector.
492 *
493 * The vector is required by the respective parser to fill the VPD map.
494 * Note: API throws exception in case of failure. Caller needs to handle.
495 *
496 * @param[in] vpdFilePath - EEPROM path of the FRU.
497 * @param[out] vpdVector - VPD in vector form.
498 * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
499 * @param[out] o_errCode - To set error code in case of error.
500 */
getVpdDataInVector(const std::string & vpdFilePath,types::BinaryVector & vpdVector,size_t & vpdStartOffset,uint16_t & o_errCode)501 inline void getVpdDataInVector(const std::string& vpdFilePath,
502 types::BinaryVector& vpdVector,
503 size_t& vpdStartOffset, uint16_t& o_errCode)
504 {
505 o_errCode = 0;
506 if (vpdFilePath.empty())
507 {
508 o_errCode = error_code::INVALID_INPUT_PARAMETER;
509 return;
510 }
511
512 try
513 {
514 std::fstream vpdFileStream;
515 vpdFileStream.exceptions(
516 std::ifstream::badbit | std::ifstream::failbit);
517 vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
518 auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
519 static_cast<uintmax_t>(65504));
520 vpdVector.resize(vpdSizeToRead);
521
522 vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
523 vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
524 vpdSizeToRead);
525
526 vpdVector.resize(vpdFileStream.gcount());
527 vpdFileStream.clear(std::ios_base::eofbit);
528 }
529 catch (const std::ifstream::failure& fail)
530 {
531 o_errCode = error_code::FILE_SYSTEM_ERROR;
532 return;
533 }
534 }
535
536 /**
537 * @brief An API to get D-bus representation of given VPD keyword.
538 *
539 * @param[in] i_keywordName - VPD keyword name.
540 * @param[out] o_errCode - To set error code in case of error.
541 *
542 * @return D-bus representation of given keyword.
543 */
getDbusPropNameForGivenKw(const std::string & i_keywordName,uint16_t & o_errCode)544 inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName,
545 uint16_t& o_errCode)
546 {
547 o_errCode = 0;
548 if (i_keywordName.empty())
549 {
550 o_errCode = error_code::INVALID_INPUT_PARAMETER;
551 return std::string{};
552 }
553 // Check for "#" prefixed VPD keyword.
554 if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
555 (i_keywordName.at(0) == constants::POUND_KW))
556 {
557 // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
558 // prefixed keywords.
559 return (std::string(constants::POUND_KW_PREFIX) +
560 i_keywordName.substr(1));
561 }
562
563 // Return the keyword name back, if D-bus representation is same as the VPD
564 // keyword name.
565 return i_keywordName;
566 }
567
568 /**
569 * @brief API to find CCIN in parsed VPD map.
570 *
571 * Few FRUs need some special handling. To identify those FRUs CCIN are used.
572 * The API will check from parsed VPD map if the FRU is the one with desired
573 * CCIN.
574 *
575 * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
576 * @param[in] i_parsedVpdMap - Parsed VPD map.
577 * @param[out] o_errCode - To set error code in case of error.
578 *
579 * @return True if found, false otherwise.
580 */
findCcinInVpd(const nlohmann::json & i_JsonObject,const types::VPDMapVariant & i_parsedVpdMap,uint16_t & o_errCode)581 inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
582 const types::VPDMapVariant& i_parsedVpdMap,
583 uint16_t& o_errCode) noexcept
584 {
585 o_errCode = 0;
586 bool l_rc{false};
587 try
588 {
589 if (i_JsonObject.empty() ||
590 std::holds_alternative<std::monostate>(i_parsedVpdMap))
591 {
592 o_errCode = error_code::INVALID_INPUT_PARAMETER;
593 return l_rc;
594 }
595
596 if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
597 {
598 auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
599 if (l_itrToRec == (*l_ipzVPDMap).end())
600 {
601 o_errCode = error_code::RECORD_NOT_FOUND;
602 return l_rc;
603 }
604
605 std::string l_ccinFromVpd{vpdSpecificUtility::getKwVal(
606 l_itrToRec->second, "CC", o_errCode)};
607 if (l_ccinFromVpd.empty())
608 {
609 o_errCode = error_code::KEYWORD_NOT_FOUND;
610 return l_rc;
611 }
612
613 transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
614 l_ccinFromVpd.begin(), ::toupper);
615
616 for (std::string l_ccinValue : i_JsonObject["ccin"])
617 {
618 transform(l_ccinValue.begin(), l_ccinValue.end(),
619 l_ccinValue.begin(), ::toupper);
620
621 if (l_ccinValue.compare(l_ccinFromVpd) ==
622 constants::STR_CMP_SUCCESS)
623 {
624 // CCIN found
625 l_rc = true;
626 }
627 }
628
629 if (!l_rc)
630 {
631 logging::logMessage("No match found for CCIN");
632 }
633 }
634 else
635 {
636 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
637 }
638 }
639 catch (const std::exception& l_ex)
640 {
641 o_errCode = error_code::STANDARD_EXCEPTION;
642 }
643 return l_rc;
644 }
645
646 /**
647 * @brief API to reset data of a FRU populated under PIM.
648 *
649 * This API resets the data for particular interfaces of a FRU under PIM.
650 *
651 * @param[in] i_objectPath - DBus object path of the FRU.
652 * @param[in] io_interfaceMap - Interface and its properties map.
653 * @param[out] o_errCode - To set error code in case of error.
654 */
resetDataUnderPIM(const std::string & i_objectPath,types::InterfaceMap & io_interfaceMap,uint16_t & o_errCode)655 inline void resetDataUnderPIM(const std::string& i_objectPath,
656 types::InterfaceMap& io_interfaceMap,
657 uint16_t& o_errCode)
658 {
659 o_errCode = 0;
660 if (i_objectPath.empty())
661 {
662 o_errCode = error_code::INVALID_INPUT_PARAMETER;
663 return;
664 }
665
666 try
667 {
668 std::vector<std::string> l_interfaces;
669 const types::MapperGetObject& l_getObjectMap =
670 dbusUtility::getObjectMap(i_objectPath, l_interfaces);
671
672 const std::vector<std::string>& l_vpdRelatedInterfaces{
673 constants::operationalStatusInf, constants::inventoryItemInf,
674 constants::assetInf, constants::vpdCollectionInterface};
675
676 for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
677 {
678 if (l_service.compare(constants::pimServiceName) !=
679 constants::STR_CMP_SUCCESS)
680 {
681 continue;
682 }
683
684 for (const auto& l_interface : l_interfaceList)
685 {
686 if ((l_interface.find(constants::ipzVpdInf) !=
687 std::string::npos &&
688 l_interface != constants::locationCodeInf) ||
689 ((std::find(l_vpdRelatedInterfaces.begin(),
690 l_vpdRelatedInterfaces.end(), l_interface)) !=
691 l_vpdRelatedInterfaces.end()))
692 {
693 const types::PropertyMap& l_propertyValueMap =
694 dbusUtility::getPropertyMap(l_service, i_objectPath,
695 l_interface);
696
697 types::PropertyMap l_propertyMap;
698
699 for (const auto& l_aProperty : l_propertyValueMap)
700 {
701 const std::string& l_propertyName = l_aProperty.first;
702 const auto& l_propertyValue = l_aProperty.second;
703
704 if (std::holds_alternative<types::BinaryVector>(
705 l_propertyValue))
706 {
707 l_propertyMap.emplace(l_propertyName,
708 types::BinaryVector{});
709 }
710 else if (std::holds_alternative<std::string>(
711 l_propertyValue))
712 {
713 if (l_propertyName.compare("Status") ==
714 constants::STR_CMP_SUCCESS)
715 {
716 l_propertyMap.emplace(
717 l_propertyName,
718 constants::vpdCollectionNotStarted);
719 l_propertyMap.emplace("StartTime", 0);
720 l_propertyMap.emplace("CompletedTime", 0);
721 }
722 else if (l_propertyName.compare("PrettyName") ==
723 constants::STR_CMP_SUCCESS)
724 {
725 // The FRU name is constant and independent of
726 // its presence state. So, it should not get
727 // reset.
728 continue;
729 }
730 else
731 {
732 l_propertyMap.emplace(l_propertyName,
733 std::string{});
734 }
735 }
736 else if (std::holds_alternative<bool>(l_propertyValue))
737 {
738 if (l_propertyName.compare("Present") ==
739 constants::STR_CMP_SUCCESS)
740 {
741 l_propertyMap.emplace(l_propertyName, false);
742 }
743 else if (l_propertyName.compare("Functional") ==
744 constants::STR_CMP_SUCCESS)
745 {
746 // Since FRU is not present functional property
747 // is considered as true.
748 l_propertyMap.emplace(l_propertyName, true);
749 }
750 }
751 }
752 io_interfaceMap.emplace(l_interface,
753 std::move(l_propertyMap));
754 }
755 }
756 }
757 }
758 catch (const std::exception& l_ex)
759 {
760 o_errCode = error_code::STANDARD_EXCEPTION;
761 }
762 }
763
764 /**
765 * @brief API to detect pass1 planar type.
766 *
767 * Based on HW version and IM keyword, This API detects is it is a pass1 planar
768 * or not.
769 * @param[out] o_errCode - To set error code in case of error.
770 *
771 * @return True if pass 1 planar, false otherwise.
772 */
isPass1Planar(uint16_t & o_errCode)773 inline bool isPass1Planar(uint16_t& o_errCode) noexcept
774 {
775 o_errCode = 0;
776 bool l_rc{false};
777 auto l_retVal = dbusUtility::readDbusProperty(
778 constants::pimServiceName, constants::systemVpdInvPath,
779 constants::viniInf, constants::kwdHW);
780
781 auto l_hwVer = std::get_if<types::BinaryVector>(&l_retVal);
782
783 l_retVal = dbusUtility::readDbusProperty(
784 constants::pimServiceName, constants::systemInvPath, constants::vsbpInf,
785 constants::kwdIM);
786
787 auto l_imValue = std::get_if<types::BinaryVector>(&l_retVal);
788
789 if (l_hwVer && l_imValue)
790 {
791 if (l_hwVer->size() != constants::VALUE_2)
792 {
793 o_errCode = error_code::INVALID_KEYWORD_LENGTH;
794 return l_rc;
795 }
796
797 if (l_imValue->size() != constants::VALUE_4)
798 {
799 o_errCode = error_code::INVALID_KEYWORD_LENGTH;
800 return l_rc;
801 }
802
803 const types::BinaryVector l_everest{80, 00, 48, 00};
804 const types::BinaryVector l_fuji{96, 00, 32, 00};
805
806 if (((*l_imValue) == l_everest) || ((*l_imValue) == l_fuji))
807 {
808 if ((*l_hwVer).at(1) < constants::VALUE_21)
809 {
810 l_rc = true;
811 }
812 }
813 else if ((*l_hwVer).at(1) < constants::VALUE_2)
814 {
815 l_rc = true;
816 }
817 }
818
819 return l_rc;
820 }
821
822 /**
823 * @brief API to detect if system configuration is that of PowerVS system.
824 *
825 * @param[in] i_imValue - IM value of the system.
826 * @param[out] o_errCode - To set error code in case of error.
827 * @return true if it is PowerVS configuration, false otherwise.
828 */
isPowerVsConfiguration(const types::BinaryVector & i_imValue,uint16_t & o_errCode)829 inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue,
830 uint16_t& o_errCode)
831 {
832 o_errCode = 0;
833 if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
834 {
835 o_errCode = error_code::INVALID_INPUT_PARAMETER;
836 return false;
837 }
838
839 // Should be a 0x5000XX series system.
840 if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
841 i_imValue.at(1) == constants::HEX_VALUE_00)
842 {
843 std::string l_imagePrefix = dbusUtility::getImagePrefix();
844
845 // Check image for 0x500030XX series.
846 if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
847 ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
848 (l_imagePrefix == constants::powerVsImagePrefix_NY)))
849 {
850 logging::logMessage("PowerVS configuration");
851 return true;
852 }
853
854 // Check image for 0X500010XX series.
855 if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
856 ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
857 (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
858 {
859 logging::logMessage("PowerVS configuration");
860 return true;
861 }
862 }
863 return false;
864 }
865
866 /**
867 * @brief API to get CCIN for a given FRU from DBus.
868 *
869 * The API reads the CCIN for a FRU based on its inventory path.
870 *
871 * @param[in] i_invObjPath - Inventory path of the FRU.
872 * @param[out] o_errCode - To set error code in case of error.
873 *
874 * @return CCIN of the FRU on success, empty string otherwise.
875 */
getCcinFromDbus(const std::string & i_invObjPath,uint16_t & o_errCode)876 inline std::string getCcinFromDbus(const std::string& i_invObjPath,
877 uint16_t& o_errCode)
878 {
879 o_errCode = 0;
880 try
881 {
882 if (i_invObjPath.empty())
883 {
884 o_errCode = error_code::INVALID_INPUT_PARAMETER;
885 return std::string{};
886 }
887
888 const auto& l_retValue = dbusUtility::readDbusProperty(
889 constants::pimServiceName, i_invObjPath, constants::viniInf,
890 constants::kwdCCIN);
891
892 auto l_ptrCcin = std::get_if<types::BinaryVector>(&l_retValue);
893
894 if (!l_ptrCcin)
895 {
896 o_errCode = error_code::RECEIVED_INVALID_KWD_TYPE_FROM_DBUS;
897 return std::string{};
898 }
899
900 if ((*l_ptrCcin).size() != constants::VALUE_4)
901 {
902 o_errCode = error_code::INVALID_VALUE_READ_FROM_DBUS;
903 return std::string{};
904 }
905
906 return std::string((*l_ptrCcin).begin(), (*l_ptrCcin).end());
907 }
908 catch (const std::exception& l_ex)
909 {
910 o_errCode = error_code::STANDARD_EXCEPTION;
911 return std::string{};
912 }
913 }
914
915 /**
916 * @brief API to check if the current running image is a powerVS image.
917 *
918 * @return true if it is PowerVS image, false otherwise.
919 */
isPowerVsImage()920 inline bool isPowerVsImage()
921 {
922 std::string l_imagePrefix = dbusUtility::getImagePrefix();
923
924 if ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
925 (l_imagePrefix == constants::powerVsImagePrefix_NY) ||
926 (l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
927 (l_imagePrefix == constants::powerVsImagePrefix_NZ))
928 {
929 return true;
930 }
931 return false;
932 }
933
934 /**
935 * @brief API to sync keyword update to inherited FRUs.
936 *
937 * For a given keyword update on a EEPROM path, this API syncs the keyword
938 * update to all inherited FRUs' respective interface, property on PIM.
939 *
940 * @param[in] i_fruPath - EEPROM path of FRU.
941 * @param[in] i_paramsToWriteData - Input details.
942 * @param[in] i_sysCfgJsonObj - System config JSON.
943 * @param[out] o_errCode - To set error code in case of error.
944 *
945 */
updateKwdOnInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)946 inline void updateKwdOnInheritedFrus(
947 const std::string& i_fruPath,
948 const types::WriteVpdParams& i_paramsToWriteData,
949 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
950 {
951 o_errCode = 0;
952 try
953 {
954 if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
955 {
956 o_errCode = error_code::INVALID_INPUT_PARAMETER;
957 return;
958 }
959
960 if (!i_sysCfgJsonObj.contains("frus"))
961 {
962 o_errCode = error_code::INVALID_JSON;
963 return;
964 }
965
966 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
967 {
968 o_errCode = error_code::FRU_PATH_NOT_FOUND;
969 return;
970 }
971
972 const types::IpzData* l_ipzData =
973 std::get_if<types::IpzData>(&i_paramsToWriteData);
974
975 if (!l_ipzData)
976 {
977 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
978 return;
979 }
980 // iterate through all inventory paths for given EEPROM path,
981 // except the base FRU.
982 // if for an inventory path, "inherit" tag is true,
983 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
984 // property
985
986 types::ObjectMap l_objectInterfaceMap;
987
988 auto l_populateInterfaceMap =
989 [&l_objectInterfaceMap,
990 &l_ipzData = std::as_const(l_ipzData)](const auto& l_Fru) {
991 // update inherited FRUs only
992 if (l_Fru.value("inherit", true))
993 {
994 l_objectInterfaceMap.emplace(
995 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
996 types::InterfaceMap{
997 {std::string{constants::ipzVpdInf +
998 std::get<0>(*l_ipzData)},
999 types::PropertyMap{{std::get<1>(*l_ipzData),
1000 std::get<2>(*l_ipzData)}}}});
1001 }
1002 };
1003
1004 // iterate through all FRUs except the base FRU
1005 std::for_each(
1006 i_sysCfgJsonObj["frus"][i_fruPath].begin() + constants::VALUE_1,
1007 i_sysCfgJsonObj["frus"][i_fruPath].end(), l_populateInterfaceMap);
1008
1009 if (!l_objectInterfaceMap.empty())
1010 {
1011 // notify PIM
1012 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1013 {
1014 o_errCode = error_code::DBUS_FAILURE;
1015 return;
1016 }
1017 }
1018 }
1019 catch (const std::exception& l_ex)
1020 {
1021 o_errCode = error_code::STANDARD_EXCEPTION;
1022 return;
1023 }
1024 }
1025
1026 /**
1027 * @brief API to get common interface(s) properties corresponding to given
1028 * record and keyword.
1029 *
1030 * For a given record and keyword, this API finds the corresponding common
1031 * interfaces(s) properties from the system config JSON and populates an
1032 * interface map with the respective properties and values.
1033 *
1034 * @param[in] i_paramsToWriteData - Input details.
1035 * @param[in] i_commonInterfaceJson - Common interface JSON object.
1036 * @param[out] o_errCode - To set error code in case of error.
1037 *
1038 * @return Returns a map of common interface(s) and properties corresponding to
1039 * the record and keyword. An empty map is returned if no such common
1040 * interface(s) and properties are found.
1041 */
getCommonInterfaceProperties(const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_commonInterfaceJson,uint16_t & o_errCode)1042 inline types::InterfaceMap getCommonInterfaceProperties(
1043 const types::WriteVpdParams& i_paramsToWriteData,
1044 const nlohmann::json& i_commonInterfaceJson, uint16_t& o_errCode) noexcept
1045 {
1046 types::InterfaceMap l_interfaceMap;
1047 o_errCode = 0;
1048 try
1049 {
1050 if (i_commonInterfaceJson.empty())
1051 {
1052 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1053 return l_interfaceMap;
1054 }
1055
1056 const types::IpzData* l_ipzData =
1057 std::get_if<types::IpzData>(&i_paramsToWriteData);
1058
1059 if (!l_ipzData)
1060 {
1061 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1062 return l_interfaceMap;
1063 }
1064
1065 auto l_populateInterfaceMap = [&l_ipzData = std::as_const(l_ipzData),
1066 &l_interfaceMap, &o_errCode](
1067 const auto& l_interfacesPropPair) {
1068 if (l_interfacesPropPair.value().empty())
1069 {
1070 return;
1071 }
1072
1073 // find matching property value pair
1074 const auto l_matchPropValuePairIt = std::find_if(
1075 l_interfacesPropPair.value().items().begin(),
1076 l_interfacesPropPair.value().items().end(),
1077 [&l_ipzData](const auto& l_propValuePair) {
1078 return (l_propValuePair.value().value("recordName", "") ==
1079 std::get<0>(*l_ipzData) &&
1080 l_propValuePair.value().value("keywordName", "") ==
1081 std::get<1>(*l_ipzData));
1082 });
1083
1084 if (l_matchPropValuePairIt !=
1085 l_interfacesPropPair.value().items().end())
1086 {
1087 std::string l_kwd = std::string(std::get<2>(*l_ipzData).begin(),
1088 std::get<2>(*l_ipzData).end());
1089
1090 std::string l_encodedValue = vpdSpecificUtility::encodeKeyword(
1091 l_kwd, l_matchPropValuePairIt.value().value("encoding", ""),
1092 o_errCode);
1093
1094 if (l_encodedValue.empty() && o_errCode)
1095 {
1096 logging::logMessage(
1097 "Failed to get encoded value for keyword : " + l_kwd +
1098 ", error : " + commonUtility::getErrCodeMsg(o_errCode));
1099 }
1100
1101 // add property map to interface map
1102 l_interfaceMap.emplace(
1103 l_interfacesPropPair.key(),
1104 types::PropertyMap{
1105 {l_matchPropValuePairIt.key(), l_encodedValue}});
1106 }
1107 };
1108
1109 if (!i_commonInterfaceJson.empty())
1110 {
1111 // iterate through all common interfaces and populate interface map
1112 std::for_each(i_commonInterfaceJson.items().begin(),
1113 i_commonInterfaceJson.items().end(),
1114 l_populateInterfaceMap);
1115 }
1116 }
1117 catch (const std::exception& l_ex)
1118 {
1119 o_errCode = error_code::STANDARD_EXCEPTION;
1120 }
1121 return l_interfaceMap;
1122 }
1123
1124 /**
1125 * @brief API to update common interface(s) properties when keyword is updated.
1126 *
1127 * For a given keyword update on a EEPROM path, this API syncs the keyword
1128 * update to respective common interface(s) properties of the base FRU and all
1129 * inherited FRUs.
1130 *
1131 * @param[in] i_fruPath - EEPROM path of FRU.
1132 * @param[in] i_paramsToWriteData - Input details.
1133 * @param[in] i_sysCfgJsonObj - System config JSON.
1134 * @param[out] o_errCode - To set error code in case of error.
1135 */
updateCiPropertyOfInheritedFrus(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData,const nlohmann::json & i_sysCfgJsonObj,uint16_t & o_errCode)1136 inline void updateCiPropertyOfInheritedFrus(
1137 const std::string& i_fruPath,
1138 const types::WriteVpdParams& i_paramsToWriteData,
1139 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) noexcept
1140 {
1141 o_errCode = 0;
1142 try
1143 {
1144 if (i_fruPath.empty() || i_sysCfgJsonObj.empty())
1145 {
1146 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1147 return;
1148 }
1149
1150 if (!i_sysCfgJsonObj.contains("commonInterfaces"))
1151 {
1152 // no common interfaces in JSON, nothing to do
1153 return;
1154 }
1155
1156 if (!i_sysCfgJsonObj.contains("frus"))
1157 {
1158 o_errCode = error_code::INVALID_JSON;
1159 return;
1160 }
1161
1162 if (!i_sysCfgJsonObj["frus"].contains(i_fruPath))
1163 {
1164 o_errCode = error_code::FRU_PATH_NOT_FOUND;
1165 return;
1166 }
1167
1168 if (!std::get_if<types::IpzData>(&i_paramsToWriteData))
1169 {
1170 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1171 return;
1172 }
1173
1174 // iterate through all inventory paths for given EEPROM path,
1175 // if for an inventory path, "inherit" tag is true,
1176 // update the inventory path's com.ibm.ipzvpd.<record>,keyword
1177 // property
1178
1179 types::ObjectMap l_objectInterfaceMap;
1180
1181 const types::InterfaceMap l_interfaceMap = getCommonInterfaceProperties(
1182 i_paramsToWriteData, i_sysCfgJsonObj["commonInterfaces"],
1183 o_errCode);
1184
1185 if (l_interfaceMap.empty())
1186 {
1187 if (o_errCode)
1188 {
1189 logging::logMessage(
1190 "Failed to get common interface property list, error : " +
1191 commonUtility::getErrCodeMsg(o_errCode));
1192 }
1193 // nothing to do
1194 return;
1195 }
1196
1197 auto l_populateObjectInterfaceMap =
1198 [&l_objectInterfaceMap, &l_interfaceMap = std::as_const(
1199 l_interfaceMap)](const auto& l_Fru) {
1200 if (l_Fru.value("inherit", true) &&
1201 l_Fru.contains("inventoryPath"))
1202 {
1203 l_objectInterfaceMap.emplace(
1204 sdbusplus::message::object_path{l_Fru["inventoryPath"]},
1205 l_interfaceMap);
1206 }
1207 };
1208
1209 std::for_each(i_sysCfgJsonObj["frus"][i_fruPath].begin(),
1210 i_sysCfgJsonObj["frus"][i_fruPath].end(),
1211 l_populateObjectInterfaceMap);
1212
1213 if (!l_objectInterfaceMap.empty())
1214 {
1215 // notify PIM
1216 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1217 {
1218 o_errCode = error_code::DBUS_FAILURE;
1219 return;
1220 }
1221 }
1222 }
1223 catch (const std::exception& l_ex)
1224 {
1225 o_errCode = error_code::STANDARD_EXCEPTION;
1226 }
1227 }
1228
1229 /**
1230 * @brief API to convert write VPD parameters to a string.
1231 *
1232 * @param[in] i_paramsToWriteData - write VPD parameters.
1233 * @param[out] o_errCode - To set error code in case of error.
1234 *
1235 * @return On success returns string representation of write VPD parameters,
1236 * otherwise returns an empty string.
1237 */
convertWriteVpdParamsToString(const types::WriteVpdParams & i_paramsToWriteData,uint16_t & o_errCode)1238 inline const std::string convertWriteVpdParamsToString(
1239 const types::WriteVpdParams& i_paramsToWriteData,
1240 uint16_t& o_errCode) noexcept
1241 {
1242 o_errCode = 0;
1243 try
1244 {
1245 if (const types::IpzData* l_ipzDataPtr =
1246 std::get_if<types::IpzData>(&i_paramsToWriteData))
1247 {
1248 return std::string{
1249 "Record: " + std::get<0>(*l_ipzDataPtr) +
1250 " Keyword: " + std::get<1>(*l_ipzDataPtr) + " Value: " +
1251 commonUtility::convertByteVectorToHex(
1252 std::get<2>(*l_ipzDataPtr))};
1253 }
1254 else if (const types::KwData* l_kwDataPtr =
1255 std::get_if<types::KwData>(&i_paramsToWriteData))
1256 {
1257 return std::string{
1258 "Keyword: " + std::get<0>(*l_kwDataPtr) + " Value: " +
1259 commonUtility::convertByteVectorToHex(
1260 std::get<1>(*l_kwDataPtr))};
1261 }
1262 else
1263 {
1264 o_errCode = error_code::UNSUPPORTED_VPD_TYPE;
1265 }
1266 }
1267 catch (const std::exception& l_ex)
1268 {
1269 o_errCode = error_code::STANDARD_EXCEPTION;
1270 }
1271 return std::string{};
1272 }
1273
1274 /**
1275 * @brief An API to read IM value from VPD.
1276 *
1277 * @param[in] i_parsedVpd - Parsed VPD.
1278 * @param[out] o_errCode - To set error code in case of error.
1279 */
getIMValue(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1280 inline std::string getIMValue(const types::IPZVpdMap& i_parsedVpd,
1281 uint16_t& o_errCode) noexcept
1282 {
1283 o_errCode = 0;
1284 std::ostringstream l_imData;
1285 try
1286 {
1287 if (i_parsedVpd.empty())
1288 {
1289 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1290 return {};
1291 }
1292
1293 const auto& l_itrToVSBP = i_parsedVpd.find("VSBP");
1294 if (l_itrToVSBP == i_parsedVpd.end())
1295 {
1296 o_errCode = error_code::RECORD_NOT_FOUND;
1297 return {};
1298 }
1299
1300 const auto& l_itrToIM = (l_itrToVSBP->second).find("IM");
1301 if (l_itrToIM == (l_itrToVSBP->second).end())
1302 {
1303 o_errCode = error_code::KEYWORD_NOT_FOUND;
1304 return {};
1305 }
1306
1307 types::BinaryVector l_imVal;
1308 std::copy(l_itrToIM->second.begin(), l_itrToIM->second.end(),
1309 back_inserter(l_imVal));
1310
1311 for (auto& l_aByte : l_imVal)
1312 {
1313 l_imData << std::setw(2) << std::setfill('0') << std::hex
1314 << static_cast<int>(l_aByte);
1315 }
1316 }
1317 catch (const std::exception& l_ex)
1318 {
1319 logging::logMessage("Failed to get IM value with exception:" +
1320 std::string(l_ex.what()));
1321 o_errCode = error_code::STANDARD_EXCEPTION;
1322 }
1323
1324 return l_imData.str();
1325 }
1326
1327 /**
1328 * @brief An API to read HW version from VPD.
1329 *
1330 * @param[in] i_parsedVpd - Parsed VPD.
1331 * @param[out] o_errCode - To set error code in case of error.
1332 */
getHWVersion(const types::IPZVpdMap & i_parsedVpd,uint16_t & o_errCode)1333 inline std::string getHWVersion(const types::IPZVpdMap& i_parsedVpd,
1334 uint16_t& o_errCode) noexcept
1335 {
1336 o_errCode = 0;
1337 std::ostringstream l_hwString;
1338 try
1339 {
1340 if (i_parsedVpd.empty())
1341 {
1342 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1343 return {};
1344 }
1345
1346 const auto& l_itrToVINI = i_parsedVpd.find("VINI");
1347 if (l_itrToVINI == i_parsedVpd.end())
1348 {
1349 o_errCode = error_code::RECORD_NOT_FOUND;
1350 return {};
1351 }
1352
1353 const auto& l_itrToHW = (l_itrToVINI->second).find("HW");
1354 if (l_itrToHW == (l_itrToVINI->second).end())
1355 {
1356 o_errCode = error_code::KEYWORD_NOT_FOUND;
1357 return {};
1358 }
1359
1360 types::BinaryVector l_hwVal;
1361 std::copy(l_itrToHW->second.begin(), l_itrToHW->second.end(),
1362 back_inserter(l_hwVal));
1363
1364 // The planar pass only comes from the LSB of the HW keyword,
1365 // where as the MSB is used for other purposes such as signifying clock
1366 // termination.
1367 l_hwVal[0] = 0x00;
1368
1369 for (auto& l_aByte : l_hwVal)
1370 {
1371 l_hwString << std::setw(2) << std::setfill('0') << std::hex
1372 << static_cast<int>(l_aByte);
1373 }
1374 }
1375 catch (const std::exception& l_ex)
1376 {
1377 logging::logMessage("Failed to get HW version with exception:" +
1378 std::string(l_ex.what()));
1379 o_errCode = error_code::STANDARD_EXCEPTION;
1380 }
1381
1382 return l_hwString.str();
1383 }
1384
1385 } // namespace vpdSpecificUtility
1386 } // namespace vpd
1387