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 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 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 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 = ParserFactory::getParser(vpdVector, invPath, 122 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 = vpdPath + 133 " is not of type IPZ VPD. Unable to parse the VPD."; 134 throw std::runtime_error(err); 135 } 136 } 137 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 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 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 197 void VpdTool::debugger(json output) 198 { 199 cout << output.dump(4) << '\n'; 200 } 201 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 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 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 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 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 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 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 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 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 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 = bus.new_method_call(BUSNAME, OBJPATH, IFACE, 556 "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 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 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 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 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 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 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 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 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 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(make_tuple(++num, primaryRecord, primaryKeyword, 1336 backupValStr, primaryValStr, 1337 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