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