1 #include "vpd_tool_impl.hpp"
2 
3 #include "impl.hpp"
4 #include "parser_factory.hpp"
5 #include "vpd_exceptions.hpp"
6 
7 #include <sdbusplus/bus.hpp>
8 
9 #include <cstdlib>
10 #include <filesystem>
11 #include <iostream>
12 #include <variant>
13 #include <vector>
14 
15 using namespace std;
16 using namespace openpower::vpd;
17 using namespace inventory;
18 using namespace openpower::vpd::manager::editor;
19 namespace fs = std::filesystem;
20 using json = nlohmann::json;
21 using namespace openpower::vpd::exceptions;
22 using namespace openpower::vpd::parser;
23 using namespace openpower::vpd::parser::factory;
24 using namespace openpower::vpd::parser::interface;
25 
fileToVector(Binary & data)26 bool VpdTool::fileToVector(Binary& data)
27 {
28     try
29     {
30         std::ifstream file(value, std::ifstream::in);
31 
32         if (file)
33         {
34             std::string line;
35             while (std::getline(file, line))
36             {
37                 std::istringstream iss(line);
38                 std::string byteStr;
39                 while (iss >> std::setw(2) >> std::hex >> byteStr)
40                 {
41                     uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
42                     data.emplace(data.end(), byte);
43                 }
44             }
45             return true;
46         }
47         else
48         {
49             std::cerr << "Unable to open the given file " << value << std::endl;
50         }
51     }
52     catch (std::exception& e)
53     {
54         std::cerr << e.what();
55     }
56     return false;
57 }
58 
copyStringToFile(const std::string & input)59 bool VpdTool::copyStringToFile(const std::string& input)
60 {
61     try
62     {
63         std::ofstream outFile(value, std::ofstream::out);
64 
65         if (outFile.is_open())
66         {
67             std::string hexString = input;
68             if (input.substr(0, 2) == "0x")
69             {
70                 // truncating prefix 0x
71                 hexString = input.substr(2);
72             }
73             outFile.write(hexString.c_str(), hexString.length());
74         }
75         else
76         {
77             std::cerr << "Error opening output file " << value << std::endl;
78             return false;
79         }
80 
81         outFile.close();
82     }
83     catch (std::exception& e)
84     {
85         std::cerr << e.what();
86         return false;
87     }
88     return true;
89 }
90 
91 static void
getVPDInMap(const std::string & vpdPath,std::unordered_map<std::string,DbusPropertyMap> & vpdMap,json & js,const std::string & invPath)92     getVPDInMap(const std::string& vpdPath,
93                 std::unordered_map<std::string, DbusPropertyMap>& vpdMap,
94                 json& js, const std::string& invPath)
95 {
96     auto jsonToParse = INVENTORY_JSON_DEFAULT;
97     if (fs::exists(INVENTORY_JSON_SYM_LINK))
98     {
99         jsonToParse = INVENTORY_JSON_SYM_LINK;
100     }
101 
102     std::ifstream inventoryJson(jsonToParse);
103     if (!inventoryJson)
104     {
105         throw std::runtime_error("VPD JSON file not found");
106     }
107 
108     try
109     {
110         js = json::parse(inventoryJson);
111     }
112     catch (const json::parse_error& ex)
113     {
114         throw std::runtime_error("VPD JSON parsing failed");
115     }
116 
117     Binary vpdVector{};
118 
119     uint32_t vpdStartOffset = 0;
120     vpdVector = getVpdDataInVector(js, vpdPath);
121     ParserInterface* parser =
122         ParserFactory::getParser(vpdVector, invPath, vpdPath, vpdStartOffset);
123     auto parseResult = parser->parse();
124     ParserFactory::freeParser(parser);
125 
126     if (auto pVal = std::get_if<Store>(&parseResult))
127     {
128         vpdMap = pVal->getVpdMap();
129     }
130     else
131     {
132         std::string err =
133             vpdPath + " is not of type IPZ VPD. Unable to parse the VPD.";
134         throw std::runtime_error(err);
135     }
136 }
137 
toBinary(const std::string & value)138 Binary VpdTool::toBinary(const std::string& value)
139 {
140     Binary val{};
141     if (value.find("0x") == string::npos)
142     {
143         val.assign(value.begin(), value.end());
144     }
145     else if (value.find("0x") != string::npos)
146     {
147         stringstream ss;
148         ss.str(value.substr(2));
149         string byteStr{};
150 
151         if (value.length() % 2 != 0)
152         {
153             throw runtime_error(
154                 "VPD-TOOL write option accepts 2 digit hex numbers. (Eg. 0x1 "
155                 "should be given as 0x01). Aborting the write operation.");
156         }
157 
158         if (value.find_first_not_of("0123456789abcdefABCDEF", 2) !=
159             std::string::npos)
160         {
161             throw runtime_error("Provide a valid hexadecimal input.");
162         }
163 
164         while (ss >> setw(2) >> byteStr)
165         {
166             uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
167 
168             val.push_back(byte);
169         }
170     }
171 
172     else
173     {
174         throw runtime_error("The value to be updated should be either in ascii "
175                             "or in hex. Refer --help option");
176     }
177     return val;
178 }
179 
printReturnCode(int returnCode)180 void VpdTool::printReturnCode(int returnCode)
181 {
182     if (returnCode)
183     {
184         cout << "\n Command failed with the return code " << returnCode
185              << ". Continuing the execution. " << endl;
186     }
187 }
188 
eraseInventoryPath(string & fru)189 void VpdTool::eraseInventoryPath(string& fru)
190 {
191     // Power supply frupath comes with INVENTORY_PATH appended in prefix.
192     // Stripping it off inorder to avoid INVENTORY_PATH duplication
193     // during getVINIProperties() execution.
194     fru.erase(0, sizeof(INVENTORY_PATH) - 1);
195 }
196 
debugger(json output)197 void VpdTool::debugger(json output)
198 {
199     cout << output.dump(4) << '\n';
200 }
201 
makeDBusCall(const string & objectName,const string & interface,const string & kw)202 auto VpdTool::makeDBusCall(const string& objectName, const string& interface,
203                            const string& kw)
204 {
205     auto bus = sdbusplus::bus::new_default();
206     auto properties =
207         bus.new_method_call(INVENTORY_MANAGER_SERVICE, objectName.c_str(),
208                             "org.freedesktop.DBus.Properties", "Get");
209     properties.append(interface);
210     properties.append(kw);
211     auto result = bus.call(properties);
212 
213     if (result.is_method_error())
214     {
215         throw runtime_error("Get api failed");
216     }
217     return result;
218 }
219 
getVINIProperties(string invPath)220 json VpdTool::getVINIProperties(string invPath)
221 {
222     variant<Binary> response;
223     json kwVal = json::object({});
224 
225     vector<string> keyword{"CC", "SN", "PN", "FN", "DR"};
226     string interface = "com.ibm.ipzvpd.VINI";
227     string objectName = {};
228 
229     if (invPath.find(INVENTORY_PATH) != string::npos)
230     {
231         objectName = invPath;
232         eraseInventoryPath(invPath);
233     }
234     else
235     {
236         objectName = INVENTORY_PATH + invPath;
237     }
238     for (string kw : keyword)
239     {
240         try
241         {
242             makeDBusCall(objectName, interface, kw).read(response);
243 
244             if (auto vec = get_if<Binary>(&response))
245             {
246                 string printableVal = getPrintableValue(*vec);
247                 kwVal.emplace(kw, printableVal);
248             }
249         }
250         catch (const sdbusplus::exception_t& e)
251         {
252             if (string(e.name()) ==
253                 string("org.freedesktop.DBus.Error.UnknownObject"))
254             {
255                 kwVal.emplace(invPath, json::object({}));
256                 objFound = false;
257                 break;
258             }
259         }
260     }
261 
262     return kwVal;
263 }
264 
getExtraInterfaceProperties(const string & invPath,const string & extraInterface,const json & prop,json & output)265 void VpdTool::getExtraInterfaceProperties(const string& invPath,
266                                           const string& extraInterface,
267                                           const json& prop, json& output)
268 {
269     variant<string> response;
270 
271     string objectName = INVENTORY_PATH + invPath;
272 
273     for (const auto& itProp : prop.items())
274     {
275         string kw = itProp.key();
276         try
277         {
278             makeDBusCall(objectName, extraInterface, kw).read(response);
279 
280             if (auto str = get_if<string>(&response))
281             {
282                 output.emplace(kw, *str);
283             }
284         }
285         catch (const sdbusplus::exception_t& e)
286         {
287             if (std::string(e.name()) ==
288                 std::string("org.freedesktop.DBus.Error.UnknownObject"))
289             {
290                 objFound = false;
291                 break;
292             }
293             else if (std::string(e.name()) ==
294                      std::string("org.freedesktop.DBus.Error.UnknownProperty"))
295             {
296                 output.emplace(kw, "");
297             }
298         }
299     }
300 }
301 
interfaceDecider(json & itemEEPROM)302 json VpdTool::interfaceDecider(json& itemEEPROM)
303 {
304     if (itemEEPROM.find("inventoryPath") == itemEEPROM.end())
305     {
306         throw runtime_error("Inventory Path not found");
307     }
308 
309     if (itemEEPROM.find("extraInterfaces") == itemEEPROM.end())
310     {
311         throw runtime_error("Extra Interfaces not found");
312     }
313 
314     json subOutput = json::object({});
315     fruType = "FRU";
316 
317     json j;
318     objFound = true;
319     string invPath = itemEEPROM.at("inventoryPath");
320 
321     j = getVINIProperties(invPath);
322 
323     if (objFound)
324     {
325         subOutput.insert(j.begin(), j.end());
326         json js;
327         if (itemEEPROM.find("type") != itemEEPROM.end())
328         {
329             fruType = itemEEPROM.at("type");
330         }
331         js.emplace("TYPE", fruType);
332 
333         if (invPath.find("powersupply") != string::npos)
334         {
335             js.emplace("type", POWER_SUPPLY_TYPE_INTERFACE);
336         }
337         else if (invPath.find("fan") != string::npos)
338         {
339             js.emplace("type", FAN_INTERFACE);
340         }
341 
342         for (const auto& ex : itemEEPROM["extraInterfaces"].items())
343         {
344             // Properties under Decorator.Asset interface are derived from VINI
345             // keywords. Displaying VINI keywords and skipping Decorator.Asset
346             // interface's properties will avoid duplicate entries in vpd-tool
347             // output.
348             if (ex.key() == "xyz.openbmc_project.Inventory.Decorator.Asset" &&
349                 itemEEPROM["extraInterfaces"].find(constants::kwdVpdInf) !=
350                     itemEEPROM["extraInterfaces"].end())
351             {
352                 continue;
353             }
354 
355             if (!(ex.value().is_null()))
356             {
357                 // TODO: Remove this if condition check once inventory json is
358                 // updated with xyz location code interface.
359                 if (ex.key() == "com.ibm.ipzvpd.Location")
360                 {
361                     getExtraInterfaceProperties(
362                         invPath,
363                         "xyz.openbmc_project.Inventory.Decorator.LocationCode",
364                         ex.value(), js);
365                 }
366                 else
367                 {
368                     getExtraInterfaceProperties(invPath, ex.key(), ex.value(),
369                                                 js);
370                 }
371             }
372             if ((ex.key().find("Item") != string::npos) &&
373                 (ex.value().is_null()))
374             {
375                 js.emplace("type", ex.key());
376             }
377             subOutput.insert(js.begin(), js.end());
378         }
379     }
380     return subOutput;
381 }
382 
getPresentPropJson(const std::string & invPath)383 json VpdTool::getPresentPropJson(const std::string& invPath)
384 {
385     std::variant<bool> response;
386     std::string presence = "Unknown";
387 
388     try
389     {
390         makeDBusCall(invPath, "xyz.openbmc_project.Inventory.Item", "Present")
391             .read(response);
392 
393         if (auto pVal = get_if<bool>(&response))
394         {
395             presence = *pVal ? "true" : "false";
396         }
397     }
398     catch (const sdbusplus::exception::SdBusError& e)
399     {
400         presence = "Unknown";
401     }
402 
403     json js;
404     js.emplace("Present", presence);
405     return js;
406 }
407 
parseInvJson(const json & jsObject,char flag,string fruPath)408 json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
409 {
410     json output = json::object({});
411     bool validObject = false;
412 
413     if (jsObject.find("frus") == jsObject.end())
414     {
415         throw runtime_error("Frus missing in Inventory json");
416     }
417     else
418     {
419         for (const auto& itemFRUS : jsObject["frus"].items())
420         {
421             for (auto itemEEPROM : itemFRUS.value())
422             {
423                 json subOutput = json::object({});
424                 try
425                 {
426                     if (flag == 'O')
427                     {
428                         if (itemEEPROM.find("inventoryPath") ==
429                             itemEEPROM.end())
430                         {
431                             throw runtime_error("Inventory Path not found");
432                         }
433                         else if (itemEEPROM.at("inventoryPath") == fruPath)
434                         {
435                             validObject = true;
436                             subOutput = interfaceDecider(itemEEPROM);
437                             json presentJs = getPresentPropJson(
438                                 "/xyz/openbmc_project/inventory" + fruPath);
439                             subOutput.insert(presentJs.begin(),
440                                              presentJs.end());
441                             output.emplace(fruPath, subOutput);
442                             return output;
443                         }
444                     }
445                     else
446                     {
447                         subOutput = interfaceDecider(itemEEPROM);
448                         json presentJs = getPresentPropJson(
449                             "/xyz/openbmc_project/inventory" +
450                             string(itemEEPROM.at("inventoryPath")));
451                         subOutput.insert(presentJs.begin(), presentJs.end());
452                         output.emplace(string(itemEEPROM.at("inventoryPath")),
453                                        subOutput);
454                     }
455                 }
456                 catch (const exception& e)
457                 {
458                     cerr << e.what();
459                 }
460             }
461         }
462         if ((flag == 'O') && (!validObject))
463         {
464             throw runtime_error(
465                 "Invalid object path. Refer --dumpInventory/-i option.");
466         }
467     }
468     return output;
469 }
470 
dumpInventory(const nlohmann::basic_json<> & jsObject)471 void VpdTool::dumpInventory(const nlohmann::basic_json<>& jsObject)
472 {
473     char flag = 'I';
474     json output = json::array({});
475     output.emplace_back(parseInvJson(jsObject, flag, ""));
476     debugger(output);
477 }
478 
dumpObject(const nlohmann::basic_json<> & jsObject)479 void VpdTool::dumpObject(const nlohmann::basic_json<>& jsObject)
480 {
481     char flag = 'O';
482     json output = json::array({});
483     output.emplace_back(parseInvJson(jsObject, flag, fruPath));
484     debugger(output);
485 }
486 
readKeyword()487 void VpdTool::readKeyword()
488 {
489     const std::string& kw = getDbusNameForThisKw(keyword);
490 
491     string interface = "com.ibm.ipzvpd.";
492     variant<Binary> response;
493 
494     try
495     {
496         makeDBusCall(INVENTORY_PATH + fruPath, interface + recordName, kw)
497             .read(response);
498 
499         string printableVal{};
500         if (auto vec = get_if<Binary>(&response))
501         {
502             printableVal = getPrintableValue(*vec);
503         }
504 
505         if (!value.empty())
506         {
507             if (copyStringToFile(printableVal))
508             {
509                 std::cout << "Value read is saved in the file " << value
510                           << std::endl;
511                 return;
512             }
513             else
514             {
515                 std::cerr << "Error while saving the read value in file. "
516                              "Displaying the read value on console"
517                           << std::endl;
518             }
519         }
520 
521         json output = json::object({});
522         json kwVal = json::object({});
523         kwVal.emplace(keyword, printableVal);
524 
525         output.emplace(fruPath, kwVal);
526 
527         debugger(output);
528     }
529     catch (const json::exception& e)
530     {
531         std::cout << "Keyword Value: " << keyword << std::endl;
532         std::cout << e.what() << std::endl;
533     }
534 }
535 
updateKeyword()536 int VpdTool::updateKeyword()
537 {
538     Binary val;
539 
540     if (std::filesystem::exists(value))
541     {
542         if (!fileToVector(val))
543         {
544             std::cout << "Keyword " << keyword << " update failed."
545                       << std::endl;
546             return 1;
547         }
548     }
549     else
550     {
551         val = toBinary(value);
552     }
553 
554     auto bus = sdbusplus::bus::new_default();
555     auto properties =
556         bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
557     properties.append(static_cast<sdbusplus::message::object_path>(fruPath));
558     properties.append(recordName);
559     properties.append(keyword);
560     properties.append(val);
561 
562     // When there is a request to write 10K bytes, there occurs a delay in dbus
563     // call which leads to dbus timeout exception. To avoid such exceptions
564     // increase the timeout period from default 25 seconds to 60 seconds.
565     auto timeoutInMicroSeconds = 60 * 1000000L;
566     auto result = bus.call(properties, timeoutInMicroSeconds);
567 
568     if (result.is_method_error())
569     {
570         throw runtime_error("Get api failed");
571     }
572     std::cout << "Data updated successfully " << std::endl;
573     return 0;
574 }
575 
forceReset(const nlohmann::basic_json<> & jsObject)576 void VpdTool::forceReset(const nlohmann::basic_json<>& jsObject)
577 {
578     for (const auto& itemFRUS : jsObject["frus"].items())
579     {
580         for (const auto& itemEEPROM : itemFRUS.value().items())
581         {
582             string fru = itemEEPROM.value().at("inventoryPath");
583 
584             fs::path fruCachePath = INVENTORY_MANAGER_CACHE;
585             fruCachePath += INVENTORY_PATH;
586             fruCachePath += fru;
587 
588             try
589             {
590                 for (const auto& it : fs::directory_iterator(fruCachePath))
591                 {
592                     if (fs::is_regular_file(it.status()))
593                     {
594                         fs::remove(it);
595                     }
596                 }
597             }
598             catch (const fs::filesystem_error& e)
599             {}
600         }
601     }
602 
603     cout.flush();
604     string udevRemove = "udevadm trigger -c remove -s \"*nvmem*\" -v";
605     int returnCode = system(udevRemove.c_str());
606     printReturnCode(returnCode);
607 
608     string invManagerRestart =
609         "systemctl restart xyz.openbmc_project.Inventory.Manager.service";
610     returnCode = system(invManagerRestart.c_str());
611     printReturnCode(returnCode);
612 
613     string sysVpdRestart = "systemctl restart system-vpd.service";
614     returnCode = system(sysVpdRestart.c_str());
615     printReturnCode(returnCode);
616 
617     string udevAdd = "udevadm trigger -c add -s \"*nvmem*\" -v";
618     returnCode = system(udevAdd.c_str());
619     printReturnCode(returnCode);
620 }
621 
updateHardware(const uint32_t offset)622 int VpdTool::updateHardware(const uint32_t offset)
623 {
624     int rc = 0;
625     Binary val;
626     if (std::filesystem::exists(value))
627     {
628         if (!fileToVector(val))
629         {
630             std::cout << "Keyword " << keyword << " update failed."
631                       << std::endl;
632             return 1;
633         }
634     }
635     else
636     {
637         val = toBinary(value);
638     }
639 
640     ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
641     try
642     {
643         auto json = nlohmann::json::parse(inventoryJson);
644         EditorImpl edit(fruPath, json, recordName, keyword);
645 
646         edit.updateKeyword(val, offset, false);
647     }
648     catch (const json::parse_error& ex)
649     {
650         throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
651     }
652     std::cout << "Data updated successfully " << std::endl;
653     return rc;
654 }
655 
readKwFromHw(const uint32_t & startOffset)656 void VpdTool::readKwFromHw(const uint32_t& startOffset)
657 {
658     ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
659     auto jsonFile = nlohmann::json::parse(inventoryJson);
660     std::string inventoryPath;
661 
662     if (jsonFile["frus"].contains(fruPath))
663     {
664         uint32_t vpdStartOffset = 0;
665 
666         for (const auto& item : jsonFile["frus"][fruPath])
667         {
668             if (item.find("offset") != item.end())
669             {
670                 vpdStartOffset = item["offset"];
671                 break;
672             }
673         }
674 
675         if ((startOffset != vpdStartOffset))
676         {
677             std::cerr << "Invalid offset, please correct the offset" << endl;
678             std::cerr << "Recommended Offset is: " << vpdStartOffset << endl;
679             return;
680         }
681         inventoryPath = jsonFile["frus"][fruPath][0]["inventoryPath"];
682     }
683 
684     Binary completeVPDFile;
685     fstream vpdFileStream;
686 
687     vpdFileStream.exceptions(std::ifstream::badbit | std::ifstream::failbit);
688     try
689     {
690         vpdFileStream.open(fruPath,
691                            std::ios::in | std::ios::out | std::ios::binary);
692 
693         auto vpdFileSize = std::min(std::filesystem::file_size(fruPath),
694                                     constants::MAX_VPD_SIZE);
695         if (vpdFileSize == 0)
696         {
697             std::cerr << "File size is 0 for " << fruPath << std::endl;
698             throw std::runtime_error("File size is 0.");
699         }
700 
701         completeVPDFile.resize(vpdFileSize);
702         vpdFileStream.seekg(startOffset, ios_base::cur);
703         vpdFileStream.read(reinterpret_cast<char*>(&completeVPDFile[0]),
704                            vpdFileSize);
705         vpdFileStream.clear(std::ios_base::eofbit);
706     }
707     catch (const std::system_error& fail)
708     {
709         std::cerr << "Exception in file handling [" << fruPath
710                   << "] error : " << fail.what();
711         std::cerr << "Stream file size = " << vpdFileStream.gcount()
712                   << std::endl;
713         throw;
714     }
715 
716     if (completeVPDFile.empty())
717     {
718         throw std::runtime_error("Invalid File");
719     }
720 
721     Impl obj(completeVPDFile, (constants::pimPath + inventoryPath), fruPath,
722              startOffset);
723     std::string keywordVal = obj.readKwFromHw(recordName, keyword);
724 
725     keywordVal = getPrintableValue(keywordVal);
726 
727     if (keywordVal.empty())
728     {
729         std::cerr << "The given keyword " << keyword << " or record "
730                   << recordName
731                   << " or both are not present in the given FRU path "
732                   << fruPath << std::endl;
733         return;
734     }
735 
736     if (!value.empty())
737     {
738         if (copyStringToFile(keywordVal))
739         {
740             std::cout << "Value read is saved in the file " << value
741                       << std::endl;
742             return;
743         }
744         else
745         {
746             std::cerr
747                 << "Error while saving the read value in file. Displaying "
748                    "the read value on console"
749                 << std::endl;
750         }
751     }
752 
753     json output = json::object({});
754     json kwVal = json::object({});
755     kwVal.emplace(keyword, keywordVal);
756     output.emplace(fruPath, kwVal);
757     debugger(output);
758 }
759 
printFixSystemVPDOption(UserOption option)760 void VpdTool::printFixSystemVPDOption(UserOption option)
761 {
762     switch (option)
763     {
764         case VpdTool::EXIT:
765             cout << "\nEnter 0 => To exit successfully : ";
766             break;
767         case VpdTool::BACKUP_DATA_FOR_ALL:
768             cout << "\n\nEnter 1 => If you choose the data on backup for all "
769                     "mismatching record-keyword pairs";
770             break;
771         case VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL:
772             cout << "\nEnter 2 => If you choose the data on primary for all "
773                     "mismatching record-keyword pairs";
774             break;
775         case VpdTool::MORE_OPTIONS:
776             cout << "\nEnter 3 => If you wish to explore more options";
777             break;
778         case VpdTool::BACKUP_DATA_FOR_CURRENT:
779             cout << "\nEnter 4 => If you choose the data on backup as the "
780                     "right value";
781             break;
782         case VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT:
783             cout << "\nEnter 5 => If you choose the data on primary as the "
784                     "right value";
785             break;
786         case VpdTool::NEW_VALUE_ON_BOTH:
787             cout << "\nEnter 6 => If you wish to enter a new value to update "
788                     "both on backup and primary";
789             break;
790         case VpdTool::SKIP_CURRENT:
791             cout << "\nEnter 7 => If you wish to skip the above "
792                     "record-keyword pair";
793             break;
794     }
795 }
796 
getSystemDataFromCache(IntfPropMap & svpdBusData)797 void VpdTool::getSystemDataFromCache(IntfPropMap& svpdBusData)
798 {
799     const auto vsys = getAllDBusProperty<GetAllResultType>(
800         constants::pimIntf,
801         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
802         "com.ibm.ipzvpd.VSYS");
803     svpdBusData.emplace("VSYS", vsys);
804 
805     const auto vcen = getAllDBusProperty<GetAllResultType>(
806         constants::pimIntf,
807         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
808         "com.ibm.ipzvpd.VCEN");
809     svpdBusData.emplace("VCEN", vcen);
810 
811     const auto lxr0 = getAllDBusProperty<GetAllResultType>(
812         constants::pimIntf,
813         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
814         "com.ibm.ipzvpd.LXR0");
815     svpdBusData.emplace("LXR0", lxr0);
816 
817     const auto util = getAllDBusProperty<GetAllResultType>(
818         constants::pimIntf,
819         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
820         "com.ibm.ipzvpd.UTIL");
821     svpdBusData.emplace("UTIL", util);
822 }
823 
fixSystemVPD()824 int VpdTool::fixSystemVPD()
825 {
826     std::string outline(191, '=');
827     cout << "\nRestorable record-keyword pairs and their data on backup & "
828             "primary.\n\n"
829          << outline << std::endl;
830 
831     cout << left << setw(6) << "S.No" << left << setw(8) << "Record" << left
832          << setw(9) << "Keyword" << left << setw(75) << "Data On Backup" << left
833          << setw(75) << "Data On Primary" << left << setw(14)
834          << "Data Mismatch\n"
835          << outline << std::endl;
836 
837     uint8_t num = 0;
838 
839     // Get system VPD data in map
840     unordered_map<string, DbusPropertyMap> vpdMap;
841     json js;
842     getVPDInMap(constants::systemVpdFilePath, vpdMap, js,
843                 constants::pimPath +
844                     static_cast<std::string>(constants::SYSTEM_OBJECT));
845 
846     // Get system VPD D-Bus Data in a map
847     IntfPropMap svpdBusData;
848     getSystemDataFromCache(svpdBusData);
849 
850     for (const auto& recordKw : svpdKwdMap)
851     {
852         string record = recordKw.first;
853 
854         // Extract specific record data from the svpdBusData map.
855         const auto& rec = svpdBusData.find(record);
856 
857         if (rec == svpdBusData.end())
858         {
859             std::cerr << record << " not a part of critical system VPD records."
860                       << std::endl;
861             continue;
862         }
863 
864         const auto& recData = svpdBusData.find(record)->second;
865 
866         string busStr{}, hwValStr{};
867 
868         for (const auto& keywordInfo : recordKw.second)
869         {
870             const auto& keyword = get<0>(keywordInfo);
871             string mismatch = "NO"; // no mismatch
872             string hardwareValue{};
873             auto recItr = vpdMap.find(record);
874 
875             if (recItr != vpdMap.end())
876             {
877                 DbusPropertyMap& kwValMap = recItr->second;
878                 auto kwItr = kwValMap.find(keyword);
879                 if (kwItr != kwValMap.end())
880                 {
881                     hardwareValue = kwItr->second;
882                 }
883             }
884 
885             inventory::Value kwValue;
886             for (auto& kwData : recData)
887             {
888                 if (kwData.first == keyword)
889                 {
890                     kwValue = kwData.second;
891                     break;
892                 }
893             }
894 
895             if (keyword != "SE") // SE to display in Hex string only
896             {
897                 ostringstream hwValStream;
898                 hwValStream << "0x";
899                 hwValStr = hwValStream.str();
900 
901                 for (uint16_t byte : hardwareValue)
902                 {
903                     hwValStream << setfill('0') << setw(2) << hex << byte;
904                     hwValStr = hwValStream.str();
905                 }
906 
907                 if (const auto value = get_if<Binary>(&kwValue))
908                 {
909                     busStr = hexString(*value);
910                 }
911                 if (busStr != hwValStr)
912                 {
913                     mismatch = "YES";
914                 }
915             }
916             else
917             {
918                 if (const auto value = get_if<Binary>(&kwValue))
919                 {
920                     busStr = getPrintableValue(*value);
921                 }
922                 if (busStr != hardwareValue)
923                 {
924                     mismatch = "YES";
925                 }
926                 hwValStr = hardwareValue;
927             }
928             recKwData.push_back(
929                 make_tuple(++num, record, keyword, busStr, hwValStr, mismatch));
930 
931             std::string splitLine(191, '-');
932             cout << left << setw(6) << static_cast<int>(num) << left << setw(8)
933                  << record << left << setw(9) << keyword << left << setw(75)
934                  << setfill(' ') << busStr << left << setw(75) << setfill(' ')
935                  << hwValStr << left << setw(14) << mismatch << '\n'
936                  << splitLine << endl;
937         }
938     }
939     parseSVPDOptions(js, std::string());
940     return 0;
941 }
942 
parseSVPDOptions(const nlohmann::json & json,const std::string & backupEEPROMPath)943 void VpdTool::parseSVPDOptions(const nlohmann::json& json,
944                                const std::string& backupEEPROMPath)
945 {
946     do
947     {
948         printFixSystemVPDOption(VpdTool::BACKUP_DATA_FOR_ALL);
949         printFixSystemVPDOption(VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL);
950         printFixSystemVPDOption(VpdTool::MORE_OPTIONS);
951         printFixSystemVPDOption(VpdTool::EXIT);
952 
953         int option = 0;
954         cin >> option;
955 
956         std::string outline(191, '=');
957         cout << '\n' << outline << endl;
958 
959         if (json.find("frus") == json.end())
960         {
961             throw runtime_error("Frus not found in json");
962         }
963 
964         bool mismatchFound = false;
965 
966         if (option == VpdTool::BACKUP_DATA_FOR_ALL)
967         {
968             for (const auto& data : recKwData)
969             {
970                 if (get<5>(data) == "YES")
971                 {
972                     EditorImpl edit(constants::systemVpdFilePath, json,
973                                     get<1>(data), get<2>(data));
974                     edit.updateKeyword(toBinary(get<3>(data)), 0, true);
975                     mismatchFound = true;
976                 }
977             }
978 
979             if (mismatchFound)
980             {
981                 cout << "\nData updated successfully for all mismatching "
982                         "record-keyword pairs by choosing their corresponding "
983                         "data from backup. Exit successfully.\n"
984                      << endl;
985             }
986             else
987             {
988                 cout << "\nNo mismatch found for any of the above mentioned "
989                         "record-keyword pair. Exit successfully.\n";
990             }
991 
992             exit(0);
993         }
994         else if (option == VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL)
995         {
996             std::string hardwarePath = constants::systemVpdFilePath;
997             if (!backupEEPROMPath.empty())
998             {
999                 hardwarePath = backupEEPROMPath;
1000             }
1001 
1002             for (const auto& data : recKwData)
1003             {
1004                 if (get<5>(data) == "YES")
1005                 {
1006                     std::string record = get<1>(data), keyword = get<2>(data);
1007 
1008                     if (!backupEEPROMPath.empty())
1009                     {
1010                         getBackupRecordKeyword(record, keyword);
1011                     }
1012 
1013                     EditorImpl edit(hardwarePath, json, record, keyword);
1014                     edit.updateKeyword(toBinary(get<4>(data)), 0, true);
1015                     mismatchFound = true;
1016                 }
1017             }
1018 
1019             if (mismatchFound)
1020             {
1021                 cout << "\nData updated successfully for all mismatching "
1022                         "record-keyword pairs by choosing their corresponding "
1023                         "data from primary VPD.\n"
1024                      << endl;
1025             }
1026             else
1027             {
1028                 cout << "\nNo mismatch found for any of the above mentioned "
1029                         "record-keyword pair. Exit successfully.\n";
1030             }
1031 
1032             exit(0);
1033         }
1034         else if (option == VpdTool::MORE_OPTIONS)
1035         {
1036             cout << "\nIterate through all restorable record-keyword pairs\n";
1037 
1038             for (const auto& data : recKwData)
1039             {
1040                 do
1041                 {
1042                     cout << '\n' << outline << endl;
1043 
1044                     cout << left << setw(6) << "S.No" << left << setw(8)
1045                          << "Record" << left << setw(9) << "Keyword" << left
1046                          << setw(75) << setfill(' ') << "Backup Data" << left
1047                          << setw(75) << setfill(' ') << "Primary Data" << left
1048                          << setw(14) << "Data Mismatch" << endl;
1049 
1050                     cout << left << setw(6) << static_cast<int>(get<0>(data))
1051                          << left << setw(8) << get<1>(data) << left << setw(9)
1052                          << get<2>(data) << left << setw(75) << setfill(' ')
1053                          << get<3>(data) << left << setw(75) << setfill(' ')
1054                          << get<4>(data) << left << setw(14) << get<5>(data);
1055 
1056                     cout << '\n' << outline << endl;
1057 
1058                     if (get<5>(data) == "NO")
1059                     {
1060                         cout << "\nNo mismatch found.\n";
1061                         printFixSystemVPDOption(VpdTool::NEW_VALUE_ON_BOTH);
1062                         printFixSystemVPDOption(VpdTool::SKIP_CURRENT);
1063                         printFixSystemVPDOption(VpdTool::EXIT);
1064                     }
1065                     else
1066                     {
1067                         printFixSystemVPDOption(
1068                             VpdTool::BACKUP_DATA_FOR_CURRENT);
1069                         printFixSystemVPDOption(
1070                             VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT);
1071                         printFixSystemVPDOption(VpdTool::NEW_VALUE_ON_BOTH);
1072                         printFixSystemVPDOption(VpdTool::SKIP_CURRENT);
1073                         printFixSystemVPDOption(VpdTool::EXIT);
1074                     }
1075 
1076                     cin >> option;
1077                     cout << '\n' << outline << endl;
1078 
1079                     if (option == VpdTool::BACKUP_DATA_FOR_CURRENT)
1080                     {
1081                         EditorImpl edit(constants::systemVpdFilePath, json,
1082                                         get<1>(data), get<2>(data));
1083                         edit.updateKeyword(toBinary(get<3>(data)), 0, true);
1084                         cout << "\nData updated successfully.\n";
1085                         break;
1086                     }
1087                     else if (option ==
1088                              VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT)
1089                     {
1090                         std::string hardwarePath = constants::systemVpdFilePath;
1091                         std::string record = get<1>(data);
1092                         std::string keyword = get<2>(data);
1093 
1094                         if (!backupEEPROMPath.empty())
1095                         {
1096                             hardwarePath = backupEEPROMPath;
1097                             getBackupRecordKeyword(record, keyword);
1098                         }
1099 
1100                         EditorImpl edit(hardwarePath, json, record, keyword);
1101                         edit.updateKeyword(toBinary(get<4>(data)), 0, true);
1102                         cout << "\nData updated successfully.\n";
1103                         break;
1104                     }
1105                     else if (option == VpdTool::NEW_VALUE_ON_BOTH)
1106                     {
1107                         string value;
1108                         cout << "\nEnter the new value to update on both "
1109                                 "primary & backup. Value should be in ASCII or "
1110                                 "in HEX(prefixed with 0x) : ";
1111                         cin >> value;
1112                         cout << '\n' << outline << endl;
1113 
1114                         EditorImpl edit(constants::systemVpdFilePath, json,
1115                                         get<1>(data), get<2>(data));
1116                         edit.updateKeyword(toBinary(value), 0, true);
1117 
1118                         if (!backupEEPROMPath.empty())
1119                         {
1120                             std::string record = get<1>(data);
1121                             std::string keyword = get<2>(data);
1122 
1123                             getBackupRecordKeyword(record, keyword);
1124                             EditorImpl edit(backupEEPROMPath, json, record,
1125                                             keyword);
1126                             edit.updateKeyword(toBinary(value), 0, true);
1127                         }
1128 
1129                         cout << "\nData updated successfully.\n";
1130                         break;
1131                     }
1132                     else if (option == VpdTool::SKIP_CURRENT)
1133                     {
1134                         cout << "\nSkipped the above record-keyword pair. "
1135                                 "Continue to the next available pair.\n";
1136                         break;
1137                     }
1138                     else if (option == VpdTool::EXIT)
1139                     {
1140                         cout << "\nExit successfully\n";
1141                         exit(0);
1142                     }
1143                     else
1144                     {
1145                         cout << "\nProvide a valid option. Retrying for the "
1146                                 "current record-keyword pair\n";
1147                     }
1148                 } while (1);
1149             }
1150             exit(0);
1151         }
1152         else if (option == VpdTool::EXIT)
1153         {
1154             cout << "\nExit successfully";
1155             exit(0);
1156         }
1157         else
1158         {
1159             cout << "\nProvide a valid option. Retry.";
1160             continue;
1161         }
1162 
1163     } while (true);
1164 }
1165 
cleanSystemVPD()1166 int VpdTool::cleanSystemVPD()
1167 {
1168     try
1169     {
1170         // Get system VPD hardware data in map
1171         unordered_map<string, DbusPropertyMap> vpdMap;
1172         json js;
1173         getVPDInMap(constants::systemVpdFilePath, vpdMap, js,
1174                     constants::pimPath +
1175                         static_cast<std::string>(constants::SYSTEM_OBJECT));
1176 
1177         RecKwValMap kwdsToBeUpdated;
1178 
1179         for (auto recordMap : svpdKwdMap)
1180         {
1181             const auto& record = recordMap.first;
1182             std::unordered_map<std::string, Binary> kwDefault;
1183             for (auto keywordMap : recordMap.second)
1184             {
1185                 // Skip those keywords which cannot be reset at manufacturing
1186                 if (!std::get<3>(keywordMap))
1187                 {
1188                     continue;
1189                 }
1190                 const auto& keyword = std::get<0>(keywordMap);
1191 
1192                 // Get hardware value for this keyword from vpdMap
1193                 Binary hardwareValue;
1194 
1195                 auto recItr = vpdMap.find(record);
1196 
1197                 if (recItr != vpdMap.end())
1198                 {
1199                     DbusPropertyMap& kwValMap = recItr->second;
1200                     auto kwItr = kwValMap.find(keyword);
1201                     if (kwItr != kwValMap.end())
1202                     {
1203                         hardwareValue = toBinary(kwItr->second);
1204                     }
1205                 }
1206 
1207                 // compare hardware value with the keyword's default value
1208                 auto defaultValue = std::get<1>(keywordMap);
1209                 if (hardwareValue != defaultValue)
1210                 {
1211                     EditorImpl edit(constants::systemVpdFilePath, js, record,
1212                                     keyword);
1213                     edit.updateKeyword(defaultValue, 0, true);
1214                 }
1215             }
1216         }
1217 
1218         std::cout << "\n The critical keywords from system backplane VPD has "
1219                      "been reset successfully."
1220                   << std::endl;
1221     }
1222     catch (const std::exception& e)
1223     {
1224         std::cerr << e.what();
1225         std::cerr
1226             << "\nManufacturing reset on system vpd keywords is unsuccessful";
1227     }
1228     return 0;
1229 }
1230 
fixSystemBackupVPD(const std::string & backupEepromPath,const std::string & backupInvPath)1231 int VpdTool::fixSystemBackupVPD(const std::string& backupEepromPath,
1232                                 const std::string& backupInvPath)
1233 {
1234     std::string outline(191, '=');
1235     cout << "\nRestorable record-keyword pairs and their data on backup & "
1236             "primary.\n\n"
1237          << outline << std::endl;
1238 
1239     cout << left << setw(6) << "S.No" << left << setw(8) << "Record" << left
1240          << setw(9) << "Keyword" << left << setw(75) << "Data On Backup" << left
1241          << setw(75) << "Data On Primary" << left << setw(14)
1242          << "Data Mismatch\n"
1243          << outline << std::endl;
1244 
1245     uint8_t num = 0;
1246     // Get system VPD data in map
1247     unordered_map<string, DbusPropertyMap> systemVPDMap;
1248     json js;
1249     getVPDInMap(constants::systemVpdFilePath, systemVPDMap, js,
1250                 constants::pimPath +
1251                     static_cast<std::string>(constants::SYSTEM_OBJECT));
1252 
1253     // Get backup VPD data in map
1254     unordered_map<string, DbusPropertyMap> backupVPDMap;
1255     getVPDInMap(backupEepromPath, backupVPDMap, js,
1256                 constants::pimPath + backupInvPath);
1257 
1258     for (const auto& recordKw : svpdKwdMap)
1259     {
1260         const std::string& primaryRecord = recordKw.first;
1261 
1262         std::string primaryValStr{}, backupValStr{};
1263 
1264         for (const auto& keywordInfo : recordKw.second)
1265         {
1266             const auto& primaryKeyword = get<0>(keywordInfo);
1267             const auto& bkRecord = get<4>(keywordInfo);
1268             const auto& bkKeyword = get<5>(keywordInfo);
1269             string mismatch = "NO";
1270             string primaryValue{};
1271             string backupValue{};
1272 
1273             // Find keyword value for system VPD (primary VPD)
1274             auto primaryRecItr = systemVPDMap.find(primaryRecord);
1275             if (primaryRecItr != systemVPDMap.end())
1276             {
1277                 DbusPropertyMap& primaryKwValMap = primaryRecItr->second;
1278                 auto kwItr = primaryKwValMap.find(primaryKeyword);
1279                 if (kwItr != primaryKwValMap.end())
1280                 {
1281                     primaryValue = kwItr->second;
1282                 }
1283             }
1284 
1285             // Find keyword value for backup VPD
1286             auto bkRecItr = backupVPDMap.find(bkRecord);
1287             if (bkRecItr != backupVPDMap.end())
1288             {
1289                 DbusPropertyMap& bkKwValMap = bkRecItr->second;
1290                 auto kwItr = bkKwValMap.find(bkKeyword);
1291                 if (kwItr != bkKwValMap.end())
1292                 {
1293                     backupValue = kwItr->second;
1294                 }
1295             }
1296 
1297             // SE to display in hex string only
1298             if (primaryKeyword != "SE")
1299             {
1300                 ostringstream hwValStream;
1301                 hwValStream << "0x";
1302                 primaryValStr = hwValStream.str();
1303 
1304                 for (uint16_t byte : primaryValue)
1305                 {
1306                     hwValStream << setfill('0') << setw(2) << hex << byte;
1307                     primaryValStr = hwValStream.str();
1308                 }
1309 
1310                 hwValStream.str(std::string());
1311                 hwValStream << "0x";
1312                 backupValStr = hwValStream.str();
1313 
1314                 for (uint16_t byte : backupValue)
1315                 {
1316                     hwValStream << setfill('0') << setw(2) << hex << byte;
1317                     backupValStr = hwValStream.str();
1318                 }
1319                 if (primaryValStr != backupValStr)
1320                 {
1321                     mismatch = "YES";
1322                 }
1323             }
1324             else
1325             {
1326                 if (primaryValue != backupValue)
1327                 {
1328                     mismatch = "YES";
1329                 }
1330 
1331                 primaryValStr = primaryValue;
1332                 backupValStr = backupValue;
1333             }
1334 
1335             recKwData.push_back(
1336                 make_tuple(++num, primaryRecord, primaryKeyword, backupValStr,
1337                            primaryValStr, mismatch));
1338 
1339             std::string splitLine(191, '-');
1340             cout << left << setw(6) << static_cast<int>(num) << left << setw(8)
1341                  << primaryRecord << left << setw(9) << primaryKeyword << left
1342                  << setw(75) << setfill(' ') << backupValStr << left << setw(75)
1343                  << setfill(' ') << primaryValStr << left << setw(14)
1344                  << mismatch << '\n'
1345                  << splitLine << endl;
1346         }
1347     }
1348 
1349     parseSVPDOptions(js, backupEepromPath);
1350     return 0;
1351 }
1352