xref: /openbmc/openpower-vpd-parser/vpd-tool/src/vpd_tool.cpp (revision 963e842d76719f5ca740ba03afabdb7aacea542e)
1 #include "config.h"
2 
3 #include "vpd_tool.hpp"
4 
5 #include "tool_constants.hpp"
6 #include "tool_types.hpp"
7 #include "tool_utils.hpp"
8 
9 #include <cstdlib>
10 #include <iostream>
11 #include <regex>
12 #include <tuple>
13 namespace vpd
14 {
15 // {Record, Keyword} -> {attribute name, number of bits in keyword, starting bit
16 // position, enabled value, disabled value}
17 // Note: we do not care about min/max value for the BIOS attribute here.
18 const types::BiosAttributeKeywordMap VpdTool::m_biosAttributeVpdKeywordMap = {
19     {{"UTIL", "D0"},
20      {{"hb_memory_mirror_mode", constants::VALUE_8, std::nullopt,
21        constants::VALUE_2, constants::VALUE_1}}},
22     {{"UTIL", "D1"},
23      {{"pvm_keep_and_clear", constants::VALUE_1, constants::VALUE_0,
24        constants::VALUE_1, constants::VALUE_0},
25       {"pvm_create_default_lpar", constants::VALUE_1, constants::VALUE_1,
26        constants::VALUE_1, constants::VALUE_0},
27       {"pvm_clear_nvram", constants::VALUE_1, constants::VALUE_2,
28        constants::VALUE_1, constants::VALUE_0}}},
29     {{"VSYS", "RG"},
30      {{"hb_field_core_override", constants::VALUE_32, std::nullopt,
31        std::nullopt, std::nullopt}}}};
32 
readKeyword(const std::string & i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const bool i_onHardware,const std::string & i_fileToSave)33 int VpdTool::readKeyword(
34     const std::string& i_vpdPath, const std::string& i_recordName,
35     const std::string& i_keywordName, const bool i_onHardware,
36     const std::string& i_fileToSave)
37 {
38     int l_rc = constants::FAILURE;
39     try
40     {
41         types::DbusVariantType l_keywordValue;
42         if (i_onHardware)
43         {
44             l_keywordValue = utils::readKeywordFromHardware(
45                 i_vpdPath, std::make_tuple(i_recordName, i_keywordName));
46         }
47         else
48         {
49             std::string l_inventoryObjectPath(
50                 constants::baseInventoryPath + i_vpdPath);
51 
52             l_keywordValue = utils::readDbusProperty(
53                 constants::inventoryManagerService, l_inventoryObjectPath,
54                 constants::ipzVpdInfPrefix + i_recordName, i_keywordName);
55         }
56 
57         if (const auto l_value =
58                 std::get_if<types::BinaryVector>(&l_keywordValue);
59             l_value && !l_value->empty())
60         {
61             // ToDo: Print value in both ASCII and hex formats
62             const std::string& l_keywordStrValue =
63                 utils::getPrintableValue(*l_value);
64 
65             if (i_fileToSave.empty())
66             {
67                 utils::displayOnConsole(i_vpdPath, i_keywordName,
68                                         l_keywordStrValue);
69                 l_rc = constants::SUCCESS;
70             }
71             else
72             {
73                 if (utils::saveToFile(i_fileToSave, l_keywordStrValue))
74                 {
75                     std::cout
76                         << "Value read is saved on the file: " << i_fileToSave
77                         << std::endl;
78                     l_rc = constants::SUCCESS;
79                 }
80                 else
81                 {
82                     std::cerr
83                         << "Error while saving the read value on the file: "
84                         << i_fileToSave
85                         << "\nDisplaying the read value on console"
86                         << std::endl;
87                     utils::displayOnConsole(i_vpdPath, i_keywordName,
88                                             l_keywordStrValue);
89                 }
90             }
91         }
92         else
93         {
94             // TODO: Enable logging when verbose is enabled.
95             std::cout << "Invalid data type or empty data received."
96                       << std::endl;
97         }
98     }
99     catch (const std::exception& l_ex)
100     {
101         // TODO: Enable logging when verbose is enabled.
102         std::cerr << "Read keyword's value failed for path: " << i_vpdPath
103                   << ", Record: " << i_recordName << ", Keyword: "
104                   << i_keywordName << ", error: " << l_ex.what() << std::endl;
105     }
106     return l_rc;
107 }
108 
dumpObject(std::string i_fruPath) const109 int VpdTool::dumpObject(std::string i_fruPath) const noexcept
110 {
111     int l_rc{constants::FAILURE};
112     try
113     {
114         // ToDo: For PFuture system take only full path from the user.
115         i_fruPath = constants::baseInventoryPath + i_fruPath;
116 
117         nlohmann::json l_resultJsonArray = nlohmann::json::array({});
118         const nlohmann::json l_fruJson = getFruProperties(i_fruPath);
119         if (!l_fruJson.empty())
120         {
121             l_resultJsonArray += l_fruJson;
122 
123             utils::printJson(l_resultJsonArray);
124         }
125         else
126         {
127             std::cout << "FRU [" << i_fruPath
128                       << "] is not present in the system" << std::endl;
129         }
130         l_rc = constants::SUCCESS;
131     }
132     catch (std::exception& l_ex)
133     {
134         // TODO: Enable logging when verbose is enabled.
135         std::cerr << "Dump Object failed for FRU [" << i_fruPath
136                   << "], Error: " << l_ex.what() << std::endl;
137     }
138     return l_rc;
139 }
140 
141 template <typename PropertyType>
populateInterfaceJson(const std::string & i_inventoryObjPath,const std::string & i_infName,const std::vector<std::string> & i_propList,nlohmann::json & io_fruJsonObject) const142 void VpdTool::populateInterfaceJson(const std::string& i_inventoryObjPath,
143                                     const std::string& i_infName,
144                                     const std::vector<std::string>& i_propList,
145                                     nlohmann::json& io_fruJsonObject) const
146 {
147     nlohmann::json l_interfaceJsonObj = nlohmann::json::object({});
148 
149     auto l_readProperties = [i_inventoryObjPath, &l_interfaceJsonObj, i_infName,
150                              this](const std::string& i_property) {
151         const nlohmann::json l_propertyJsonObj =
152             getInventoryPropertyJson<PropertyType>(i_inventoryObjPath,
153                                                    i_infName, i_property);
154         l_interfaceJsonObj.insert(l_propertyJsonObj.cbegin(),
155                                   l_propertyJsonObj.cend());
156     };
157 
158     std::for_each(i_propList.cbegin(), i_propList.cend(), l_readProperties);
159 
160     if (!l_interfaceJsonObj.empty())
161     {
162         io_fruJsonObject.insert(l_interfaceJsonObj.cbegin(),
163                                 l_interfaceJsonObj.cend());
164     }
165 }
166 
populateFruJson(const std::string & i_inventoryObjPath,nlohmann::json & io_fruJsonObject,const std::vector<std::string> & i_interfaceList) const167 void VpdTool::populateFruJson(
168     const std::string& i_inventoryObjPath, nlohmann::json& io_fruJsonObject,
169     const std::vector<std::string>& i_interfaceList) const
170 {
171     for (const auto& l_interface : i_interfaceList)
172     {
173         if (l_interface == constants::inventoryItemInf)
174         {
175             const std::vector<std::string> l_properties = {"PrettyName"};
176             populateInterfaceJson<std::string>(i_inventoryObjPath,
177                                                constants::inventoryItemInf,
178                                                l_properties, io_fruJsonObject);
179             continue;
180         }
181 
182         if (l_interface == constants::locationCodeInf)
183         {
184             const std::vector<std::string> l_properties = {"LocationCode"};
185             populateInterfaceJson<std::string>(i_inventoryObjPath,
186                                                constants::locationCodeInf,
187                                                l_properties, io_fruJsonObject);
188             continue;
189         }
190 
191         if (l_interface == constants::viniInf)
192         {
193             const std::vector<std::string> l_properties = {"SN", "PN", "CC",
194                                                            "FN", "DR"};
195             populateInterfaceJson<vpd::types::BinaryVector>(
196                 i_inventoryObjPath, constants::viniInf, l_properties,
197                 io_fruJsonObject);
198             continue;
199         }
200 
201         if (l_interface == constants::assetInf)
202         {
203             if (std::find(i_interfaceList.begin(), i_interfaceList.end(),
204                           constants::viniInf) != i_interfaceList.end())
205             {
206                 // The value will be filled from VINI interface. Don't
207                 // process asset interface.
208                 continue;
209             }
210 
211             const std::vector<std::string> l_properties = {
212                 "Model", "SerialNumber", "SubModel"};
213 
214             populateInterfaceJson<std::string>(i_inventoryObjPath,
215                                                constants::assetInf,
216                                                l_properties, io_fruJsonObject);
217             continue;
218         }
219 
220         if (l_interface == constants::networkInf)
221         {
222             const std::vector<std::string> l_properties = {"MACAddress"};
223             populateInterfaceJson<std::string>(i_inventoryObjPath,
224                                                constants::networkInf,
225                                                l_properties, io_fruJsonObject);
226             continue;
227         }
228 
229         if (l_interface == constants::pcieSlotInf)
230         {
231             const std::vector<std::string> l_properties = {"SlotType"};
232             populateInterfaceJson<std::string>(i_inventoryObjPath,
233                                                constants::pcieSlotInf,
234                                                l_properties, io_fruJsonObject);
235             continue;
236         }
237 
238         if (l_interface == constants::slotNumInf)
239         {
240             const std::vector<std::string> l_properties = {"SlotNumber"};
241             populateInterfaceJson<uint32_t>(i_inventoryObjPath,
242                                             constants::slotNumInf, l_properties,
243                                             io_fruJsonObject);
244             continue;
245         }
246 
247         if (l_interface == constants::i2cDeviceInf)
248         {
249             const std::vector<std::string> l_properties = {"Address", "Bus"};
250             populateInterfaceJson<uint32_t>(i_inventoryObjPath,
251                                             constants::i2cDeviceInf,
252                                             l_properties, io_fruJsonObject);
253             continue;
254         }
255     }
256 }
257 
getFruProperties(const std::string & i_objectPath) const258 nlohmann::json VpdTool::getFruProperties(const std::string& i_objectPath) const
259 {
260     // check if FRU is present in the system
261     if (!isFruPresent(i_objectPath))
262     {
263         return nlohmann::json::object_t();
264     }
265 
266     nlohmann::json l_fruJson = nlohmann::json::object_t({});
267 
268     // need to trim out the base inventory path in the FRU JSON.
269     const std::string l_displayObjectPath =
270         (i_objectPath.find(constants::baseInventoryPath) == std::string::npos)
271             ? i_objectPath
272             : i_objectPath.substr(strlen(constants::baseInventoryPath));
273 
274     l_fruJson.emplace(l_displayObjectPath, nlohmann::json::object_t({}));
275 
276     auto& l_fruObject = l_fruJson[l_displayObjectPath];
277 
278     types::MapperGetObject l_mapperResp = utils::GetServiceInterfacesForObject(
279         i_objectPath, std::vector<std::string>{});
280 
281     for (const auto& [l_service, l_interfaceList] : l_mapperResp)
282     {
283         if (l_service != constants::inventoryManagerService)
284         {
285             continue;
286         }
287         populateFruJson(i_objectPath, l_fruObject, l_interfaceList);
288     }
289 
290     const auto l_typePropertyJson = getFruTypeProperty(i_objectPath);
291     if (!l_typePropertyJson.empty())
292     {
293         l_fruObject.insert(l_typePropertyJson.cbegin(),
294                            l_typePropertyJson.cend());
295     }
296 
297     // insert FRU "TYPE"
298     l_fruObject.emplace("TYPE", "FRU");
299 
300     return l_fruJson;
301 }
302 
303 template <typename PropertyType>
getInventoryPropertyJson(const std::string & i_objectPath,const std::string & i_interface,const std::string & i_propertyName) const304 nlohmann::json VpdTool::getInventoryPropertyJson(
305     const std::string& i_objectPath, const std::string& i_interface,
306     const std::string& i_propertyName) const noexcept
307 {
308     nlohmann::json l_resultInJson = nlohmann::json::object({});
309     try
310     {
311         types::DbusVariantType l_keyWordValue;
312 
313         l_keyWordValue =
314             utils::readDbusProperty(constants::inventoryManagerService,
315                                     i_objectPath, i_interface, i_propertyName);
316 
317         if (const auto l_value = std::get_if<PropertyType>(&l_keyWordValue))
318         {
319             if constexpr (std::is_same<PropertyType, std::string>::value)
320             {
321                 l_resultInJson.emplace(i_propertyName, *l_value);
322             }
323             else if constexpr (std::is_same<PropertyType, bool>::value)
324             {
325                 l_resultInJson.emplace(i_propertyName,
326                                        *l_value ? "true" : "false");
327             }
328             else if constexpr (std::is_same<PropertyType,
329                                             types::BinaryVector>::value)
330             {
331                 const std::string& l_keywordStrValue =
332                     vpd::utils::getPrintableValue(*l_value);
333 
334                 l_resultInJson.emplace(i_propertyName, l_keywordStrValue);
335             }
336             else if constexpr (std::is_same<PropertyType, uint32_t>::value)
337             {
338                 l_resultInJson.emplace(i_propertyName,
339                                        std::to_string(*l_value));
340             }
341         }
342         else
343         {
344             // TODO: Enable logging when verbose is enabled.
345             std::cout << "Invalid data type received." << std::endl;
346         }
347     }
348     catch (const std::exception& l_ex)
349     {
350         // TODO: Enable logging when verbose is enabled.
351         std::cerr << "Read " << i_propertyName
352                   << " value for FRU path: " << i_objectPath
353                   << ", failed with exception: " << l_ex.what() << std::endl;
354     }
355     return l_resultInJson;
356 }
357 
fixSystemVpd() const358 int VpdTool::fixSystemVpd() const noexcept
359 {
360     int l_rc = constants::FAILURE;
361 
362     nlohmann::json l_backupRestoreCfgJsonObj = getBackupRestoreCfgJsonObj();
363     if (!fetchKeywordInfo(l_backupRestoreCfgJsonObj))
364     {
365         return l_rc;
366     }
367 
368     printSystemVpd(l_backupRestoreCfgJsonObj);
369 
370     do
371     {
372         printFixSystemVpdOption(types::UserOption::UseBackupDataForAll);
373         printFixSystemVpdOption(
374             types::UserOption::UseSystemBackplaneDataForAll);
375         printFixSystemVpdOption(types::UserOption::MoreOptions);
376         printFixSystemVpdOption(types::UserOption::Exit);
377 
378         int l_userSelectedOption = types::UserOption::Exit;
379         std::cin >> l_userSelectedOption;
380 
381         std::cout << std::endl << std::string(191, '=') << std::endl;
382 
383         if (types::UserOption::UseBackupDataForAll == l_userSelectedOption)
384         {
385             l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, true);
386             break;
387         }
388         else if (types::UserOption::UseSystemBackplaneDataForAll ==
389                  l_userSelectedOption)
390         {
391             l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, false);
392             break;
393         }
394         else if (types::UserOption::MoreOptions == l_userSelectedOption)
395         {
396             l_rc = handleMoreOption(l_backupRestoreCfgJsonObj);
397             break;
398         }
399         else if (types::UserOption::Exit == l_userSelectedOption)
400         {
401             std::cout << "Exit successfully" << std::endl;
402             break;
403         }
404         else
405         {
406             std::cout << "Provide a valid option. Retry." << std::endl;
407         }
408     } while (true);
409 
410     return l_rc;
411 }
412 
writeKeyword(std::string i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const std::string & i_keywordValue,const bool i_onHardware)413 int VpdTool::writeKeyword(
414     std::string i_vpdPath, const std::string& i_recordName,
415     const std::string& i_keywordName, const std::string& i_keywordValue,
416     const bool i_onHardware) noexcept
417 {
418     int l_rc = constants::FAILURE;
419     try
420     {
421         if (i_vpdPath.empty() || i_recordName.empty() ||
422             i_keywordName.empty() || i_keywordValue.empty())
423         {
424             throw std::runtime_error("Received input is empty.");
425         }
426 
427         auto l_paramsToWrite =
428             std::make_tuple(i_recordName, i_keywordName,
429                             utils::convertToBinary(i_keywordValue));
430 
431         if (i_onHardware)
432         {
433             l_rc = utils::writeKeywordOnHardware(i_vpdPath, l_paramsToWrite);
434         }
435         else
436         {
437             i_vpdPath = constants::baseInventoryPath + i_vpdPath;
438             l_rc = utils::writeKeyword(i_vpdPath, l_paramsToWrite);
439         }
440 
441         if (l_rc > 0)
442         {
443             std::cout << "Data updated successfully " << std::endl;
444             l_rc = constants::SUCCESS;
445         }
446     }
447     catch (const std::exception& l_ex)
448     {
449         // TODO: Enable log when verbose is enabled.
450         std::cerr << "Write keyword's value for path: " << i_vpdPath
451                   << ", Record: " << i_recordName
452                   << ", Keyword: " << i_keywordName
453                   << " is failed. Exception: " << l_ex.what() << std::endl;
454     }
455     return l_rc;
456 }
457 
getBackupRestoreCfgJsonObj() const458 nlohmann::json VpdTool::getBackupRestoreCfgJsonObj() const noexcept
459 {
460     nlohmann::json l_parsedBackupRestoreJson{};
461     try
462     {
463         nlohmann::json l_parsedSystemJson =
464             utils::getParsedJson(INVENTORY_JSON_SYM_LINK);
465 
466         // check for mandatory fields at this point itself.
467         if (!l_parsedSystemJson.contains("backupRestoreConfigPath"))
468         {
469             throw std::runtime_error(
470                 "backupRestoreConfigPath tag is missing from system config JSON : " +
471                 std::string(INVENTORY_JSON_SYM_LINK));
472         }
473 
474         l_parsedBackupRestoreJson =
475             utils::getParsedJson(l_parsedSystemJson["backupRestoreConfigPath"]);
476     }
477     catch (const std::exception& l_ex)
478     {
479         // TODO: Enable logging when verbose is enabled.
480         std::cerr << l_ex.what() << std::endl;
481     }
482 
483     return l_parsedBackupRestoreJson;
484 }
485 
cleanSystemVpd(bool i_syncBiosAttributesRequired) const486 int VpdTool::cleanSystemVpd(bool i_syncBiosAttributesRequired) const noexcept
487 {
488     try
489     {
490         // In order to do syncBiosAttributes, we need BIOS Config Manager
491         // service up and running
492         if (i_syncBiosAttributesRequired &&
493             !utils::isServiceRunning(constants::biosConfigMgrService))
494         {
495             std::cerr
496                 << "Cannot sync BIOS attributes as BIOS Config Manager service is not running."
497                 << std::endl;
498             return constants::FAILURE;
499         }
500 
501         // get the keyword map from backup_restore json
502         // iterate through the keyword map get default value of
503         // l_keywordName.
504         // use writeKeyword API to update default value on hardware,
505         // backup and D - Bus.
506         const nlohmann::json l_parsedBackupRestoreJson =
507             getBackupRestoreCfgJsonObj();
508 
509         // check for mandatory tags
510         if (l_parsedBackupRestoreJson.contains("source") &&
511             l_parsedBackupRestoreJson.contains("backupMap") &&
512             l_parsedBackupRestoreJson["source"].contains("hardwarePath") &&
513             l_parsedBackupRestoreJson["backupMap"].is_array())
514         {
515             // get the source hardware path
516             const auto& l_hardwarePath =
517                 l_parsedBackupRestoreJson["source"]["hardwarePath"];
518 
519             // iterate through the backup map
520             for (const auto& l_aRecordKwInfo :
521                  l_parsedBackupRestoreJson["backupMap"])
522             {
523                 // check if Manufacturing Reset is required for this entry
524                 const bool l_isMfgCleanRequired =
525                     l_aRecordKwInfo.value("isManufactureResetRequired", false);
526 
527                 if (l_isMfgCleanRequired)
528                 {
529                     // get the Record name and Keyword name
530                     const std::string& l_srcRecordName =
531                         l_aRecordKwInfo.value("sourceRecord", "");
532                     const std::string& l_srcKeywordName =
533                         l_aRecordKwInfo.value("sourceKeyword", "");
534 
535                     // validate the Record name, Keyword name and the
536                     // defaultValue
537                     if (!l_srcRecordName.empty() && !l_srcKeywordName.empty() &&
538                         l_aRecordKwInfo.contains("defaultValue") &&
539                         l_aRecordKwInfo["defaultValue"].is_array())
540                     {
541                         // check if this keyword is used for backing up BIOS
542                         // attribute
543                         const bool l_isUsedForBiosAttributeBackup =
544                             l_aRecordKwInfo.value("isBiosSyncRequired", false);
545 
546                         const types::BinaryVector l_keywordValueToUpdate =
547                             (i_syncBiosAttributesRequired &&
548                              l_isUsedForBiosAttributeBackup)
549                                 ? getVpdValueInBiosConfigManager(
550                                       l_srcRecordName, l_srcKeywordName)
551                                 : l_aRecordKwInfo["defaultValue"]
552                                       .get<types::BinaryVector>();
553 
554                         if (l_keywordValueToUpdate.empty())
555                         {
556                             std::cerr << "Failed to update " << l_srcRecordName
557                                       << ":" << l_srcKeywordName
558                                       << " . Keyword value to update is empty"
559                                       << std::endl;
560                             continue;
561                         }
562 
563                         // update the Keyword with default value, use D-Bus
564                         // method UpdateKeyword exposed by vpd-manager.
565                         // Note: writing to all paths (Primary EEPROM path,
566                         // Secondary EEPROM path, D-Bus cache and Backup path)
567                         // is the responsibility of vpd-manager's UpdateKeyword
568                         // API
569                         if (constants::FAILURE ==
570                             utils::writeKeyword(
571                                 l_hardwarePath,
572                                 std::make_tuple(l_srcRecordName,
573                                                 l_srcKeywordName,
574                                                 l_keywordValueToUpdate)))
575                         {
576                             // TODO: Enable logging when verbose
577                             // is enabled.
578                             std::cerr << "Failed to update " << l_srcRecordName
579                                       << ":" << l_srcKeywordName << std::endl;
580                         }
581                     }
582                     else
583                     {
584                         std::cerr
585                             << "Unrecognized Entry Record [" << l_srcRecordName
586                             << "] Keyword [" << l_srcKeywordName
587                             << "] in Backup Restore JSON backup map"
588                             << std::endl;
589                     }
590                 } // mfgClean required check
591             } // keyword list loop
592         }
593         else // backupRestoreJson is not valid
594         {
595             std::cerr << "Backup Restore JSON is not valid" << std::endl;
596         }
597 
598         // success/failure message
599         std::cout << "The critical keywords from system backplane VPD has "
600                      "been reset successfully."
601                   << std::endl;
602 
603     } // try block end
604     catch (const std::exception& l_ex)
605     {
606         // TODO: Enable logging when verbose is enabled.
607         std::cerr
608             << "Manufacturing reset on system vpd keywords is unsuccessful. Error : "
609             << l_ex.what() << std::endl;
610     }
611     return constants::SUCCESS;
612 }
613 
fetchKeywordInfo(nlohmann::json & io_parsedJsonObj) const614 bool VpdTool::fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept
615 {
616     bool l_returnValue = false;
617     try
618     {
619         if (io_parsedJsonObj.empty() || !io_parsedJsonObj.contains("source") ||
620             !io_parsedJsonObj.contains("destination") ||
621             !io_parsedJsonObj.contains("backupMap"))
622         {
623             throw std::runtime_error("Invalid JSON");
624         }
625 
626         std::string l_srcVpdPath;
627         std::string l_dstVpdPath;
628 
629         bool l_isSourceOnHardware = false;
630         if (l_srcVpdPath = io_parsedJsonObj["source"].value("hardwarePath", "");
631             !l_srcVpdPath.empty())
632         {
633             l_isSourceOnHardware = true;
634         }
635         else if (l_srcVpdPath =
636                      io_parsedJsonObj["source"].value("inventoryPath", "");
637                  l_srcVpdPath.empty())
638         {
639             throw std::runtime_error("Source path is empty in JSON");
640         }
641 
642         bool l_isDestinationOnHardware = false;
643         if (l_dstVpdPath =
644                 io_parsedJsonObj["destination"].value("hardwarePath", "");
645             !l_dstVpdPath.empty())
646         {
647             l_isDestinationOnHardware = true;
648         }
649         else if (l_dstVpdPath =
650                      io_parsedJsonObj["destination"].value("inventoryPath", "");
651                  l_dstVpdPath.empty())
652         {
653             throw std::runtime_error("Destination path is empty in JSON");
654         }
655 
656         for (auto& l_aRecordKwInfo : io_parsedJsonObj["backupMap"])
657         {
658             const std::string& l_srcRecordName =
659                 l_aRecordKwInfo.value("sourceRecord", "");
660             const std::string& l_srcKeywordName =
661                 l_aRecordKwInfo.value("sourceKeyword", "");
662             const std::string& l_dstRecordName =
663                 l_aRecordKwInfo.value("destinationRecord", "");
664             const std::string& l_dstKeywordName =
665                 l_aRecordKwInfo.value("destinationKeyword", "");
666 
667             if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
668                 l_srcKeywordName.empty() || l_dstKeywordName.empty())
669             {
670                 // TODO: Enable logging when verbose is enabled.
671                 std::cout << "Record or keyword not found in the JSON."
672                           << std::endl;
673                 continue;
674             }
675 
676             types::DbusVariantType l_srcKeywordVariant;
677             if (l_isSourceOnHardware)
678             {
679                 l_srcKeywordVariant = utils::readKeywordFromHardware(
680                     l_srcVpdPath,
681                     std::make_tuple(l_srcRecordName, l_srcKeywordName));
682             }
683             else
684             {
685                 l_srcKeywordVariant = utils::readDbusProperty(
686                     constants::inventoryManagerService, l_srcVpdPath,
687                     constants::ipzVpdInfPrefix + l_srcRecordName,
688                     l_srcKeywordName);
689             }
690 
691             if (auto l_srcKeywordValue =
692                     std::get_if<types::BinaryVector>(&l_srcKeywordVariant);
693                 l_srcKeywordValue && !l_srcKeywordValue->empty())
694             {
695                 l_aRecordKwInfo["sourcekeywordValue"] = *l_srcKeywordValue;
696             }
697             else
698             {
699                 // TODO: Enable logging when verbose is enabled.
700                 std::cout
701                     << "Invalid data type or empty data received, for source record: "
702                     << l_srcRecordName << ", keyword: " << l_srcKeywordName
703                     << std::endl;
704                 continue;
705             }
706 
707             types::DbusVariantType l_dstKeywordVariant;
708             if (l_isDestinationOnHardware)
709             {
710                 l_dstKeywordVariant = utils::readKeywordFromHardware(
711                     l_dstVpdPath,
712                     std::make_tuple(l_dstRecordName, l_dstKeywordName));
713             }
714             else
715             {
716                 l_dstKeywordVariant = utils::readDbusProperty(
717                     constants::inventoryManagerService, l_dstVpdPath,
718                     constants::ipzVpdInfPrefix + l_dstRecordName,
719                     l_dstKeywordName);
720             }
721 
722             if (auto l_dstKeywordValue =
723                     std::get_if<types::BinaryVector>(&l_dstKeywordVariant);
724                 l_dstKeywordValue && !l_dstKeywordValue->empty())
725             {
726                 l_aRecordKwInfo["destinationkeywordValue"] = *l_dstKeywordValue;
727             }
728             else
729             {
730                 // TODO: Enable logging when verbose is enabled.
731                 std::cout
732                     << "Invalid data type or empty data received, for destination record: "
733                     << l_dstRecordName << ", keyword: " << l_dstKeywordName
734                     << std::endl;
735                 continue;
736             }
737         }
738 
739         l_returnValue = true;
740     }
741     catch (const std::exception& l_ex)
742     {
743         // TODO: Enable logging when verbose is enabled.
744         std::cerr << l_ex.what() << std::endl;
745     }
746 
747     return l_returnValue;
748 }
749 
getFruTypeProperty(const std::string & i_objectPath) const750 nlohmann::json VpdTool::getFruTypeProperty(
751     const std::string& i_objectPath) const noexcept
752 {
753     nlohmann::json l_resultInJson = nlohmann::json::object({});
754     std::vector<std::string> l_pimInfList;
755 
756     auto l_serviceInfMap = utils::GetServiceInterfacesForObject(
757         i_objectPath, std::vector<std::string>{constants::inventoryItemInf});
758     if (l_serviceInfMap.contains(constants::inventoryManagerService))
759     {
760         l_pimInfList = l_serviceInfMap[constants::inventoryManagerService];
761 
762         // iterate through the list and find
763         // "xyz.openbmc_project.Inventory.Item.*"
764         for (const auto& l_interface : l_pimInfList)
765         {
766             if (l_interface.find(constants::inventoryItemInf) !=
767                     std::string::npos &&
768                 l_interface.length() >
769                     std::string(constants::inventoryItemInf).length())
770             {
771                 l_resultInJson.emplace("type", l_interface);
772             }
773         }
774     }
775     return l_resultInJson;
776 }
777 
isFruPresent(const std::string & i_objectPath) const778 bool VpdTool::isFruPresent(const std::string& i_objectPath) const noexcept
779 {
780     bool l_returnValue{false};
781     try
782     {
783         types::DbusVariantType l_keyWordValue;
784 
785         l_keyWordValue = utils::readDbusProperty(
786             constants::inventoryManagerService, i_objectPath,
787             constants::inventoryItemInf, "Present");
788 
789         if (const auto l_value = std::get_if<bool>(&l_keyWordValue))
790         {
791             l_returnValue = *l_value;
792         }
793     }
794     catch (const std::runtime_error& l_ex)
795     {
796         // TODO: Enable logging when verbose is enabled.
797         // std::cerr << "Failed to check \"Present\" property for FRU "
798         //           << i_objectPath << " Error: " << l_ex.what() <<
799         //           std::endl;
800     }
801     return l_returnValue;
802 }
803 
printFixSystemVpdOption(const types::UserOption & i_option) const804 void VpdTool::printFixSystemVpdOption(
805     const types::UserOption& i_option) const noexcept
806 {
807     switch (i_option)
808     {
809         case types::UserOption::Exit:
810             std::cout << "Enter 0 => To exit successfully : ";
811             break;
812         case types::UserOption::UseBackupDataForAll:
813             std::cout << "Enter 1 => If you choose the data on backup for all "
814                          "mismatching record-keyword pairs"
815                       << std::endl;
816             break;
817         case types::UserOption::UseSystemBackplaneDataForAll:
818             std::cout << "Enter 2 => If you choose the data on primary for all "
819                          "mismatching record-keyword pairs"
820                       << std::endl;
821             break;
822         case types::UserOption::MoreOptions:
823             std::cout << "Enter 3 => If you wish to explore more options"
824                       << std::endl;
825             break;
826         case types::UserOption::UseBackupDataForCurrent:
827             std::cout << "Enter 4 => If you choose the data on backup as the "
828                          "right value"
829                       << std::endl;
830             break;
831         case types::UserOption::UseSystemBackplaneDataForCurrent:
832             std::cout << "Enter 5 => If you choose the data on primary as the "
833                          "right value"
834                       << std::endl;
835             break;
836         case types::UserOption::NewValueOnBoth:
837             std::cout
838                 << "Enter 6 => If you wish to enter a new value to update "
839                    "both on backup and primary"
840                 << std::endl;
841             break;
842         case types::UserOption::SkipCurrent:
843             std::cout << "Enter 7 => If you wish to skip the above "
844                          "record-keyword pair"
845                       << std::endl;
846             break;
847     }
848 }
849 
dumpInventory(bool i_dumpTable) const850 int VpdTool::dumpInventory(bool i_dumpTable) const noexcept
851 {
852     int l_rc{constants::FAILURE};
853 
854     try
855     {
856         // get all object paths under PIM
857         const auto l_objectPaths = utils::GetSubTreePaths(
858             constants::baseInventoryPath, 0,
859             std::vector<std::string>{constants::inventoryItemInf});
860 
861         if (!l_objectPaths.empty())
862         {
863             nlohmann::json l_resultInJson = nlohmann::json::array({});
864 
865             std::for_each(l_objectPaths.begin(), l_objectPaths.end(),
866                           [&](const auto& l_objectPath) {
867                               const auto l_fruJson =
868                                   getFruProperties(l_objectPath);
869                               if (!l_fruJson.empty())
870                               {
871                                   if (l_resultInJson.empty())
872                                   {
873                                       l_resultInJson += l_fruJson;
874                                   }
875                                   else
876                                   {
877                                       l_resultInJson.at(0).insert(
878                                           l_fruJson.cbegin(), l_fruJson.cend());
879                                   }
880                               }
881                           });
882 
883             if (i_dumpTable)
884             {
885                 // create Table object
886                 utils::Table l_inventoryTable{};
887 
888                 // columns to be populated in the Inventory table
889                 const std::vector<types::TableColumnNameSizePair>
890                     l_tableColumns = {
891                         {"FRU", 100},         {"CC", 6},  {"DR", 20},
892                         {"LocationCode", 32}, {"PN", 8},  {"PrettyName", 80},
893                         {"SubModel", 10},     {"SN", 15}, {"type", 60}};
894 
895                 types::TableInputData l_tableData;
896 
897                 // First prepare the Table Columns
898                 for (const auto& l_column : l_tableColumns)
899                 {
900                     if (constants::FAILURE ==
901                         l_inventoryTable.AddColumn(l_column.first,
902                                                    l_column.second))
903                     {
904                         // TODO: Enable logging when verbose is enabled.
905                         std::cerr << "Failed to add column " << l_column.first
906                                   << " in Inventory Table." << std::endl;
907                     }
908                 }
909 
910                 // iterate through the json array
911                 for (const auto& l_fruEntry : l_resultInJson[0].items())
912                 {
913                     // if object path ends in "unit([0-9][0-9]?)", skip adding
914                     // the object path in the table
915                     if (std::regex_search(l_fruEntry.key(),
916                                           std::regex("unit([0-9][0-9]?)")))
917                     {
918                         continue;
919                     }
920 
921                     std::vector<std::string> l_row;
922                     for (const auto& l_column : l_tableColumns)
923                     {
924                         const auto& l_fruJson = l_fruEntry.value();
925 
926                         if (l_column.first == "FRU")
927                         {
928                             l_row.push_back(l_fruEntry.key());
929                         }
930                         else
931                         {
932                             if (l_fruJson.contains(l_column.first))
933                             {
934                                 l_row.push_back(l_fruJson[l_column.first]);
935                             }
936                             else
937                             {
938                                 l_row.push_back("");
939                             }
940                         }
941                     }
942 
943                     l_tableData.push_back(l_row);
944                 }
945 
946                 l_rc = l_inventoryTable.Print(l_tableData);
947             }
948             else
949             {
950                 // print JSON to console
951                 utils::printJson(l_resultInJson);
952                 l_rc = constants::SUCCESS;
953             }
954         }
955     }
956     catch (const std::exception& l_ex)
957     {
958         // TODO: Enable logging when verbose is enabled.
959         std::cerr << "Dump inventory failed. Error: " << l_ex.what()
960                   << std::endl;
961     }
962     return l_rc;
963 }
964 
printSystemVpd(const nlohmann::json & i_parsedJsonObj) const965 void VpdTool::printSystemVpd(
966     const nlohmann::json& i_parsedJsonObj) const noexcept
967 {
968     if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
969     {
970         // TODO: Enable logging when verbose is enabled.
971         std::cerr << "Invalid JSON to print system VPD" << std::endl;
972     }
973 
974     std::string l_outline(191, '=');
975     std::cout << "\nRestorable record-keyword pairs and their data on backup & "
976                  "primary.\n\n"
977               << l_outline << std::endl;
978 
979     std::cout << std::left << std::setw(6) << "S.No" << std::left
980               << std::setw(8) << "Record" << std::left << std::setw(9)
981               << "Keyword" << std::left << std::setw(75) << "Data On Backup"
982               << std::left << std::setw(75) << "Data On Primary" << std::left
983               << std::setw(14) << "Data Mismatch\n"
984               << l_outline << std::endl;
985 
986     uint8_t l_slNum = 0;
987 
988     for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
989     {
990         if (l_aRecordKwInfo.contains("sourceRecord") ||
991             l_aRecordKwInfo.contains("sourceKeyword") ||
992             l_aRecordKwInfo.contains("destinationkeywordValue") ||
993             l_aRecordKwInfo.contains("sourcekeywordValue"))
994         {
995             std::string l_mismatchFound{
996                 (l_aRecordKwInfo["destinationkeywordValue"] !=
997                  l_aRecordKwInfo["sourcekeywordValue"])
998                     ? "YES"
999                     : "NO"};
1000 
1001             std::string l_splitLine(191, '-');
1002 
1003             try
1004             {
1005                 std::cout << std::left << std::setw(6)
1006                           << static_cast<int>(++l_slNum) << std::left
1007                           << std::setw(8)
1008                           << l_aRecordKwInfo.value("sourceRecord", "")
1009                           << std::left << std::setw(9)
1010                           << l_aRecordKwInfo.value("sourceKeyword", "")
1011                           << std::left << std::setw(75) << std::setfill(' ')
1012                           << utils::getPrintableValue(
1013                                  l_aRecordKwInfo["destinationkeywordValue"])
1014                           << std::left << std::setw(75) << std::setfill(' ')
1015                           << utils::getPrintableValue(
1016                                  l_aRecordKwInfo["sourcekeywordValue"])
1017                           << std::left << std::setw(14) << l_mismatchFound
1018                           << '\n'
1019                           << l_splitLine << std::endl;
1020             }
1021             catch (const std::exception& l_ex)
1022             {
1023                 // TODO: Enable logging when verbose is enabled.
1024                 std::cerr << l_ex.what() << std::endl;
1025             }
1026         }
1027     }
1028 }
1029 
updateAllKeywords(const nlohmann::json & i_parsedJsonObj,bool i_useBackupData) const1030 int VpdTool::updateAllKeywords(const nlohmann::json& i_parsedJsonObj,
1031                                bool i_useBackupData) const noexcept
1032 {
1033     int l_rc = constants::FAILURE;
1034 
1035     if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("source") ||
1036         !i_parsedJsonObj.contains("backupMap"))
1037     {
1038         // TODO: Enable logging when verbose is enabled.
1039         std::cerr << "Invalid JSON" << std::endl;
1040         return l_rc;
1041     }
1042 
1043     std::string l_srcVpdPath;
1044     if (auto l_vpdPath = i_parsedJsonObj["source"].value("hardwarePath", "");
1045         !l_vpdPath.empty())
1046     {
1047         l_srcVpdPath = l_vpdPath;
1048     }
1049     else if (auto l_vpdPath =
1050                  i_parsedJsonObj["source"].value("inventoryPath", "");
1051              !l_vpdPath.empty())
1052     {
1053         l_srcVpdPath = l_vpdPath;
1054     }
1055     else
1056     {
1057         // TODO: Enable logging when verbose is enabled.
1058         std::cerr << "source path information is missing in JSON" << std::endl;
1059         return l_rc;
1060     }
1061 
1062     bool l_anyMismatchFound = false;
1063     for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
1064     {
1065         if (!l_aRecordKwInfo.contains("sourceRecord") ||
1066             !l_aRecordKwInfo.contains("sourceKeyword") ||
1067             !l_aRecordKwInfo.contains("destinationkeywordValue") ||
1068             !l_aRecordKwInfo.contains("sourcekeywordValue"))
1069         {
1070             // TODO: Enable logging when verbose is enabled.
1071             std::cerr << "Missing required information in the JSON"
1072                       << std::endl;
1073             continue;
1074         }
1075 
1076         if (l_aRecordKwInfo["sourcekeywordValue"] !=
1077             l_aRecordKwInfo["destinationkeywordValue"])
1078         {
1079             l_anyMismatchFound = true;
1080 
1081             auto l_keywordValue =
1082                 i_useBackupData ? l_aRecordKwInfo["destinationkeywordValue"]
1083                                 : l_aRecordKwInfo["sourcekeywordValue"];
1084 
1085             auto l_paramsToWrite = std::make_tuple(
1086                 l_aRecordKwInfo["sourceRecord"],
1087                 l_aRecordKwInfo["sourceKeyword"], l_keywordValue);
1088 
1089             try
1090             {
1091                 l_rc = utils::writeKeyword(l_srcVpdPath, l_paramsToWrite);
1092                 if (l_rc > 0)
1093                 {
1094                     l_rc = constants::SUCCESS;
1095                 }
1096             }
1097             catch (const std::exception& l_ex)
1098             {
1099                 // TODO: Enable logging when verbose is enabled.
1100                 std::cerr << "write keyword failed for record: "
1101                           << l_aRecordKwInfo["sourceRecord"]
1102                           << ", keyword: " << l_aRecordKwInfo["sourceKeyword"]
1103                           << ", error: " << l_ex.what() << std::ends;
1104             }
1105         }
1106     }
1107 
1108     std::string l_dataUsed =
1109         (i_useBackupData ? "data from backup" : "data from primary VPD");
1110     if (l_anyMismatchFound)
1111     {
1112         std::cout << "Data updated successfully for all mismatching "
1113                      "record-keyword pairs by choosing their corresponding "
1114                   << l_dataUsed << ". Exit successfully." << std::endl;
1115     }
1116     else
1117     {
1118         std::cout << "No mismatch found for any of the above mentioned "
1119                      "record-keyword pair. Exit successfully."
1120                   << std::endl;
1121     }
1122 
1123     return l_rc;
1124 }
1125 
handleMoreOption(const nlohmann::json & i_parsedJsonObj) const1126 int VpdTool::handleMoreOption(
1127     const nlohmann::json& i_parsedJsonObj) const noexcept
1128 {
1129     int l_rc = constants::FAILURE;
1130 
1131     try
1132     {
1133         if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
1134         {
1135             throw std::runtime_error("Invalid JSON");
1136         }
1137 
1138         std::string l_srcVpdPath;
1139 
1140         if (auto l_vpdPath =
1141                 i_parsedJsonObj["source"].value("hardwarePath", "");
1142             !l_vpdPath.empty())
1143         {
1144             l_srcVpdPath = l_vpdPath;
1145         }
1146         else if (auto l_vpdPath =
1147                      i_parsedJsonObj["source"].value("inventoryPath", "");
1148                  !l_vpdPath.empty())
1149         {
1150             l_srcVpdPath = l_vpdPath;
1151         }
1152         else
1153         {
1154             throw std::runtime_error(
1155                 "source path information is missing in JSON");
1156         }
1157 
1158         auto updateKeywordValue =
1159             [](std::string io_vpdPath, const std::string& i_recordName,
1160                const std::string& i_keywordName,
1161                const types::BinaryVector& i_keywordValue) -> int {
1162             int l_rc = constants::FAILURE;
1163 
1164             try
1165             {
1166                 auto l_paramsToWrite = std::make_tuple(
1167                     i_recordName, i_keywordName, i_keywordValue);
1168                 l_rc = utils::writeKeyword(io_vpdPath, l_paramsToWrite);
1169 
1170                 if (l_rc > 0)
1171                 {
1172                     std::cout << std::endl
1173                               << "Data updated successfully." << std::endl;
1174                 }
1175             }
1176             catch (const std::exception& l_ex)
1177             {
1178                 // TODO: Enable log when verbose is enabled.
1179                 std::cerr << l_ex.what() << std::endl;
1180             }
1181             return l_rc;
1182         };
1183 
1184         do
1185         {
1186             int l_slNum = 0;
1187             bool l_exit = false;
1188 
1189             for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
1190             {
1191                 if (!l_aRecordKwInfo.contains("sourceRecord") ||
1192                     !l_aRecordKwInfo.contains("sourceKeyword") ||
1193                     !l_aRecordKwInfo.contains("destinationkeywordValue") ||
1194                     !l_aRecordKwInfo.contains("sourcekeywordValue"))
1195                 {
1196                     // TODO: Enable logging when verbose is enabled.
1197                     std::cerr
1198                         << "Source or destination information is missing in the JSON."
1199                         << std::endl;
1200                     continue;
1201                 }
1202 
1203                 const std::string l_mismatchFound{
1204                     (l_aRecordKwInfo["sourcekeywordValue"] !=
1205                      l_aRecordKwInfo["destinationkeywordValue"])
1206                         ? "YES"
1207                         : "NO"};
1208 
1209                 std::cout << std::endl
1210                           << std::left << std::setw(6) << "S.No" << std::left
1211                           << std::setw(8) << "Record" << std::left
1212                           << std::setw(9) << "Keyword" << std::left
1213                           << std::setw(75) << std::setfill(' ') << "Backup Data"
1214                           << std::left << std::setw(75) << std::setfill(' ')
1215                           << "Primary Data" << std::left << std::setw(14)
1216                           << "Data Mismatch" << std::endl;
1217 
1218                 std::cout << std::left << std::setw(6)
1219                           << static_cast<int>(++l_slNum) << std::left
1220                           << std::setw(8)
1221                           << l_aRecordKwInfo.value("sourceRecord", "")
1222                           << std::left << std::setw(9)
1223                           << l_aRecordKwInfo.value("sourceKeyword", "")
1224                           << std::left << std::setw(75) << std::setfill(' ')
1225                           << utils::getPrintableValue(
1226                                  l_aRecordKwInfo["destinationkeywordValue"])
1227                           << std::left << std::setw(75) << std::setfill(' ')
1228                           << utils::getPrintableValue(
1229                                  l_aRecordKwInfo["sourcekeywordValue"])
1230                           << std::left << std::setw(14) << l_mismatchFound
1231                           << std::endl;
1232 
1233                 std::cout << std::string(191, '=') << std::endl;
1234 
1235                 if (constants::STR_CMP_SUCCESS ==
1236                     l_mismatchFound.compare("YES"))
1237                 {
1238                     printFixSystemVpdOption(
1239                         types::UserOption::UseBackupDataForCurrent);
1240                     printFixSystemVpdOption(
1241                         types::UserOption::UseSystemBackplaneDataForCurrent);
1242                     printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
1243                     printFixSystemVpdOption(types::UserOption::SkipCurrent);
1244                     printFixSystemVpdOption(types::UserOption::Exit);
1245                 }
1246                 else
1247                 {
1248                     std::cout << "No mismatch found." << std::endl << std::endl;
1249                     printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
1250                     printFixSystemVpdOption(types::UserOption::SkipCurrent);
1251                     printFixSystemVpdOption(types::UserOption::Exit);
1252                 }
1253 
1254                 int l_userSelectedOption = types::UserOption::Exit;
1255                 std::cin >> l_userSelectedOption;
1256 
1257                 if (types::UserOption::UseBackupDataForCurrent ==
1258                     l_userSelectedOption)
1259                 {
1260                     l_rc = updateKeywordValue(
1261                         l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1262                         l_aRecordKwInfo["sourceKeyword"],
1263                         l_aRecordKwInfo["destinationkeywordValue"]);
1264                 }
1265                 else if (types::UserOption::UseSystemBackplaneDataForCurrent ==
1266                          l_userSelectedOption)
1267                 {
1268                     l_rc = updateKeywordValue(
1269                         l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1270                         l_aRecordKwInfo["sourceKeyword"],
1271                         l_aRecordKwInfo["sourcekeywordValue"]);
1272                 }
1273                 else if (types::UserOption::NewValueOnBoth ==
1274                          l_userSelectedOption)
1275                 {
1276                     std::string l_newValue;
1277                     std::cout
1278                         << std::endl
1279                         << "Enter the new value to update on both "
1280                            "primary & backup. Value should be in ASCII or "
1281                            "in HEX(prefixed with 0x) : ";
1282                     std::cin >> l_newValue;
1283                     std::cout << std::endl
1284                               << std::string(191, '=') << std::endl;
1285 
1286                     try
1287                     {
1288                         l_rc = updateKeywordValue(
1289                             l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
1290                             l_aRecordKwInfo["sourceKeyword"],
1291                             utils::convertToBinary(l_newValue));
1292                     }
1293                     catch (const std::exception& l_ex)
1294                     {
1295                         // TODO: Enable logging when verbose is enabled.
1296                         std::cerr << l_ex.what() << std::endl;
1297                     }
1298                 }
1299                 else if (types::UserOption::SkipCurrent == l_userSelectedOption)
1300                 {
1301                     std::cout << std::endl
1302                               << "Skipped the above record-keyword pair. "
1303                                  "Continue to the next available pair."
1304                               << std::endl;
1305                 }
1306                 else if (types::UserOption::Exit == l_userSelectedOption)
1307                 {
1308                     std::cout << "Exit successfully" << std::endl;
1309                     l_exit = true;
1310                     break;
1311                 }
1312                 else
1313                 {
1314                     std::cout << "Provide a valid option. Retrying for the "
1315                                  "current record-keyword pair"
1316                               << std::endl;
1317                 }
1318             }
1319             if (l_exit)
1320             {
1321                 l_rc = constants::SUCCESS;
1322                 break;
1323             }
1324         } while (true);
1325     }
1326     catch (const std::exception& l_ex)
1327     {
1328         // TODO: Enable logging when verbose is enabled.
1329         std::cerr << l_ex.what() << std::endl;
1330     }
1331 
1332     return l_rc;
1333 }
1334 
resetVpdOnDbus()1335 int VpdTool::resetVpdOnDbus()
1336 {
1337     // ToDo: Limit this function to lab mode only.
1338 
1339     int l_rc = constants::FAILURE;
1340     try
1341     {
1342         std::string l_vpdManagerStopCmd(
1343             "systemctl stop " + std::string(constants::vpdManagerProcessName));
1344 
1345         std::cout << std::flush;
1346         if (auto l_rc = std::system(l_vpdManagerStopCmd.c_str()); l_rc != 0)
1347         {
1348             std::cerr << "Failed to stop " << constants::vpdManagerProcessName
1349                       << " service. Return code [" << l_rc << "]. Exiting."
1350                       << std::endl;
1351             return l_rc;
1352         }
1353 
1354         std::string l_vpdServiceIsActiveCmd(
1355             "systemctl is-active --quiet " +
1356             std::string(constants::vpdManagerProcessName));
1357 
1358         std::cout << std::flush;
1359         if (auto l_rc = std::system(l_vpdServiceIsActiveCmd.c_str()); l_rc == 0)
1360         {
1361             std::cerr
1362                 << constants::vpdManagerProcessName
1363                 << " service is still active, can't proceed further. Return code ["
1364                 << l_rc << "]. Exiting." << std::endl;
1365             return l_rc;
1366         }
1367 
1368         std::error_code l_ec;
1369         if (static_cast<std::uintmax_t>(-1) ==
1370             std::filesystem::remove_all(constants::pimPersistPath, l_ec))
1371         {
1372             std::cerr
1373                 << "Error occured while removing the persisted VPD under path ["
1374                 << constants::pimPersistPath << "]." << std::endl;
1375 
1376             if (l_ec)
1377             {
1378                 std::cerr << "Reason: " << l_ec.message() << std::endl;
1379             }
1380 
1381             std::cerr << "Reboot BMC to recover the system." << std::endl;
1382             return l_rc;
1383         }
1384 
1385         std::string l_pimServiceRestartCmd(
1386             "systemctl restart " +
1387             std::string(constants::inventoryManagerService));
1388 
1389         std::cout << std::flush;
1390         if (auto l_rc = std::system(l_pimServiceRestartCmd.c_str()); l_rc != 0)
1391         {
1392             std::cerr << "Failed to restart "
1393                       << constants::inventoryManagerService
1394                       << " service. Return code [" << l_rc << "]. Exiting."
1395                       << std::endl
1396                       << "Reboot BMC to recover the system." << std::endl;
1397             return l_rc;
1398         }
1399 
1400         std::string l_pimServiceIsActiveCmd(
1401             "systemctl is-active --quiet " +
1402             std::string(constants::inventoryManagerService));
1403 
1404         std::cout << std::flush;
1405         if (auto l_rc = std::system(l_pimServiceIsActiveCmd.c_str()); l_rc != 0)
1406         {
1407             std::cerr << constants::inventoryManagerService
1408                       << " service is not active. Return code [" << l_rc
1409                       << "]. Exiting." << std::endl
1410                       << "Reboot BMC to recover the system." << std::endl;
1411             return l_rc;
1412         }
1413 
1414         std::string l_vpdManagerStartCmd(
1415             "systemctl start " + std::string(constants::vpdManagerProcessName));
1416 
1417         std::cout << std::flush;
1418         if (auto l_rc = std::system(l_vpdManagerStartCmd.c_str()); l_rc != 0)
1419         {
1420             std::cerr << "Failed to start " << constants::vpdManagerProcessName
1421                       << " service. Return code [" << l_rc << "]. Exiting."
1422                       << std::endl
1423                       << "Reboot BMC to recover the system." << std::endl;
1424             return l_rc;
1425         }
1426 
1427         std::cout << std::flush;
1428         if (auto l_rc = std::system(l_vpdServiceIsActiveCmd.c_str()); l_rc != 0)
1429         {
1430             std::cerr << constants::vpdManagerProcessName
1431                       << " service is not active. Return code [" << l_rc
1432                       << "]. Exiting." << std::endl
1433                       << "Reboot BMC to recover the system." << std::endl;
1434             return l_rc;
1435         }
1436 
1437         l_rc = constants::SUCCESS;
1438     }
1439     catch (const std::exception& l_ex)
1440     {
1441         // TODO: Enable logging when verbose is enabled.
1442         std::cerr << l_ex.what() << std::endl;
1443     }
1444 
1445     return l_rc;
1446 }
1447 
getVpdValueInBiosConfigManager(const std::string & i_recordName,const std::string & i_keywordName) const1448 types::BinaryVector VpdTool::getVpdValueInBiosConfigManager(
1449     const std::string& i_recordName, const std::string& i_keywordName) const
1450 {
1451     types::BinaryVector l_result;
1452     const auto l_itrToBiosAttributeKeywordMap =
1453         m_biosAttributeVpdKeywordMap.find(
1454             types::IpzType(i_recordName, i_keywordName));
1455 
1456     if (l_itrToBiosAttributeKeywordMap != m_biosAttributeVpdKeywordMap.end())
1457     {
1458         const auto& l_biosAttributeList =
1459             l_itrToBiosAttributeKeywordMap->second;
1460         for (const auto& l_biosAttributeEntry : l_biosAttributeList)
1461         {
1462             // get the attribute name
1463             const std::string l_attributeName =
1464                 std::get<0>(l_biosAttributeEntry);
1465 
1466             // get the number of bits used to store the value in VPD
1467             const size_t l_numBitsKeyword = std::get<1>(l_biosAttributeEntry);
1468 
1469             auto l_attrValueVariant =
1470                 utils::biosGetAttributeMethodCall(l_attributeName);
1471 
1472             if (auto l_attrVal = std::get_if<int64_t>(&l_attrValueVariant))
1473             {
1474                 // multiple bytes update
1475 
1476                 size_t l_numBytesKeyword =
1477                     l_numBitsKeyword / constants::VALUE_8;
1478 
1479                 // convert to VPD format
1480                 l_result = utils::convertIntegralTypeToBytes(*l_attrVal,
1481                                                              l_numBytesKeyword);
1482             }
1483             else if (auto l_attrVal =
1484                          std::get_if<std::string>(&l_attrValueVariant))
1485             {
1486                 utils::toLower(*l_attrVal);
1487 
1488                 // Since we are doing mfgClean, we do not
1489                 // care about reading the current VPD keyword value before
1490                 // writing to it.
1491                 if (l_numBitsKeyword == constants::VALUE_1)
1492                 {
1493                     // single bit update.
1494 
1495                     // get the bit position
1496                     const uint8_t l_bitPosition =
1497                         std::get<2>(l_biosAttributeEntry).has_value()
1498                             ? std::get<2>(l_biosAttributeEntry).value()
1499                             : constants::VALUE_0;
1500 
1501                     l_result.resize(constants::VALUE_1, constants::VALUE_0);
1502 
1503                     if (l_attrVal->compare("enabled") ==
1504                         constants::STR_CMP_SUCCESS)
1505                     {
1506                         l_result.at(constants::VALUE_0) |=
1507                             (constants::VALUE_1 << l_bitPosition);
1508                     }
1509                     else
1510                     {
1511                         l_result.at(constants::VALUE_0) &=
1512                             ~(constants::VALUE_1 << l_bitPosition);
1513                     }
1514                 }
1515                 else
1516                 {
1517                     // single byte update
1518                     const auto l_enabledValue =
1519                         std::get<3>(l_biosAttributeEntry).has_value()
1520                             ? std::get<3>(l_biosAttributeEntry).value()
1521                             : constants::VALUE_1;
1522 
1523                     const auto l_disabledValue =
1524                         std::get<4>(l_biosAttributeEntry).has_value()
1525                             ? std::get<4>(l_biosAttributeEntry).value()
1526                             : constants::VALUE_0;
1527 
1528                     l_result.emplace_back((l_attrVal->compare("enabled") ==
1529                                            constants::STR_CMP_SUCCESS)
1530                                               ? l_enabledValue
1531                                               : l_disabledValue);
1532                 }
1533             }
1534             else
1535             {
1536                 std::cerr << "Invalid value received for attribute [" +
1537                                  l_attributeName + "] from BIOS Config Manager";
1538             }
1539         } // BIOS attribute loop end
1540     }
1541     return l_result;
1542 }
1543 
clearVpdDumpDir() const1544 void VpdTool::clearVpdDumpDir() const noexcept
1545 {
1546     try
1547     {
1548         if (std::filesystem::exists(constants::badVpdPath))
1549         {
1550             std::filesystem::remove_all(constants::badVpdPath);
1551         }
1552     }
1553     catch (const std::exception& l_ex)
1554     {
1555         std::cerr << "Failed to clear VPD dump path:[" +
1556                          std::string(constants::badVpdPath) + "]. Error: "
1557                   << l_ex.what() << std::endl;
1558     }
1559 }
1560 
1561 } // namespace vpd
1562