vpd-tool:Fix system VPD and its backup VPD

vpd-tool fixSystemVPD implementation to backup and restore system VPD
to and from the backup VPD if the backup VPD path is found in
vpd inventory JSON.

Test:
(Truncating the output in commit message)

===>>CASE 1: Option 6 to update new value on both backup and primary
hardware and its cache.

vpd-tool --fixSystemVPD
=============================================================
S.No  Record  Keyword  Backup Data  Primary Data    Data Mismatch
1     UTIL    D0       0x01         0x01            NO
============================================================

>> Enter the new value to update on both primary & backup. Value should be in ASCII or in HEX(prefixed with 0x) : 0x00

busctl get-property xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard/base_op_panel_blyth com.ibm.ipzvpd.VSBK D0
ay 1 0

busctl get-property xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard com.ibm.ipzvpd.UTIL D0
ay 1 0

===>>CASE 2: Option 4 to update primary with backup data

vpd-tool -w -O /system/chassis/motherboard -R LXR0 -K LX -V 0x333435363738
Data updated successfully

vpd-tool --fixSystemVPD
=================================================================================
S.No  Record  Keyword  Backup Data            Primary Data           Data Mismatch
6     LXR0    LX       0x310004010030007b     0x333435363738007b     YES
=================================================================================

Enter 4 => If you choose the data on backup as the right value : 4

busctl get-property xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard com.ibm.ipzvpd.LXR0 LX
ay 8 49 0 4 1 0 48 0 123

===>>CASE 3: Option 5 to update backup with primary value
vpd-tool -w -O /system/chassis/motherboard/base_op_panel_blyth -R VSBK -K FC -V "abcd"
Data updated successfully

vpd-tool --fixSystemVPD
=================================================================================
S.No  Record  Keyword  Backup Data            Primary Data           Data Mismatch
7     VCEN    FC       0x616263642d303031     0x324534412d303031     YES
=================================================================================

Enter 5 => If you choose the data on primary as the right value : 5

busctl get-property xyz.openbmc_project.Inventory.Manager /xyz/openbmc_project/inventory/system/chassis/motherboard/base_op_panel_blyth com.ibm.ipzvpd.VSBK FC
ay 8 50 69 52 65 45 48 48 49

===>>CASE 4: Option 2 to update all mismatching keywords with its primary data

vpd-tool -w -O /system/chassis/motherboard/base_op_panel_blyth -R VSBK -K FC -V "abcd"
Data updated successfully

vpd-tool -w -O /system/chassis/motherboard/base_op_panel_blyth -R VSBK -K D0 -V "abcd"
Data updated successfully

vpd-tool --fixSystemVPD
=================================================================================
S.No  Record  Keyword  Backup Data            Primary Data           Data Mismatch
=================================================================================
1     UTIL    D0       0x61                   0x01                   YES
-------------------------------------------------------------------------------
7     VCEN    FC       0x616263642d303031     0x324534412d303031     YES
=================================================================================

Enter 2 => If you choose the data on primary for all mismatching record-keyword pairs : 2

Data updated successfully for all mismatching record-keyword pairs by choosing their corresponding data from primary VPD.

vpd-tool -r -O /system/chassis/motherboard/base_op_panel_blyth -R VSBK -K FC
{
    "/system/chassis/motherboard/base_op_panel_blyth": {
        "FC": "2E4A-001"
    }
}
vpd-tool -r -O /system/chassis/motherboard/base_op_panel_blyth -R VSBK -K D0
{
    "/system/chassis/motherboard/base_op_panel_blyth": {
        "D0": "0x01"
    }
}

===>>CASE 5: Option 1

vpd-tool -w -O /system/chassis/motherboard -R UTIL -K D0 -V "abcd"
Data updated successfully
vpd-tool -w -O /system/chassis/motherboard -R UTIL -K D1 -V "abcd"
Data updated successfully

vpd-tool --fixSystemVPD
=================================================================================
S.No  Record  Keyword  Backup Data            Primary Data           Data Mismatch
=================================================================================
1     UTIL    D0       0x01                   0x61                   YES
-------------------------------------------------------------------------------
2     UTIL    D1       0x00                   0x61                   YES
=================================================================================

Enter 1 => If you choose the data on backup for all mismatching record-keyword pairs: 1

Data updated successfully for all mismatching record-keyword pairs by choosing their corresponding data from backup. Exit successfully.

vpd-tool -r -O /system/chassis/motherboard  -R UTIL -K D0
{
    "/system/chassis/motherboard": {
        "D0": "0x01"
    }
}
vpd-tool -r -O /system/chassis/motherboard  -R UTIL -K D1
{
    "/system/chassis/motherboard": {
        "D1": "0x00"
    }
}

Change-Id: I0cfa5c869d4c44d2dd74916b5284fa2ad9b4f398
Signed-off-by: Priyanga Ramasamy <priyanga24@in.ibm.com>
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index 8b96641..885cd5e 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -117,9 +117,9 @@
     Binary vpdVector{};
 
     uint32_t vpdStartOffset = 0;
-    vpdVector = getVpdDataInVector(js, constants::systemVpdFilePath);
-    ParserInterface* parser = ParserFactory::getParser(
-        vpdVector, invPath, constants::systemVpdFilePath, vpdStartOffset);
+    vpdVector = getVpdDataInVector(js, vpdPath);
+    ParserInterface* parser = ParserFactory::getParser(vpdVector, invPath,
+                                                       vpdPath, vpdStartOffset);
     auto parseResult = parser->parse();
     ParserFactory::freeParser(parser);
 
@@ -834,7 +834,7 @@
          << "Data Mismatch\n"
          << outline << std::endl;
 
-    int num = 0;
+    uint8_t num = 0;
 
     // Get system VPD data in map
     unordered_map<string, DbusPropertyMap> vpdMap;
@@ -929,18 +929,19 @@
                 make_tuple(++num, record, keyword, busStr, hwValStr, mismatch));
 
             std::string splitLine(191, '-');
-            cout << left << setw(6) << num << left << setw(8) << record << left
-                 << setw(9) << keyword << left << setw(75) << setfill(' ')
-                 << busStr << left << setw(75) << setfill(' ') << hwValStr
-                 << left << setw(14) << mismatch << '\n'
+            cout << left << setw(6) << static_cast<int>(num) << left << setw(8)
+                 << record << left << setw(9) << keyword << left << setw(75)
+                 << setfill(' ') << busStr << left << setw(75) << setfill(' ')
+                 << hwValStr << left << setw(14) << mismatch << '\n'
                  << splitLine << endl;
         }
     }
-    parseSVPDOptions(js);
+    parseSVPDOptions(js, std::string());
     return 0;
 }
 
-void VpdTool::parseSVPDOptions(const nlohmann::json& json)
+void VpdTool::parseSVPDOptions(const nlohmann::json& json,
+                               const std::string& backupEEPROMPath)
 {
     do
     {
@@ -992,12 +993,24 @@
         }
         else if (option == VpdTool::SYSTEM_BACKPLANE_DATA_FOR_ALL)
         {
+            std::string hardwarePath = constants::systemVpdFilePath;
+            if (!backupEEPROMPath.empty())
+            {
+                hardwarePath = backupEEPROMPath;
+            }
+
             for (const auto& data : recKwData)
             {
                 if (get<5>(data) == "YES")
                 {
-                    EditorImpl edit(constants::systemVpdFilePath, json,
-                                    get<1>(data), get<2>(data));
+                    std::string record = get<1>(data), keyword = get<2>(data);
+
+                    if (!backupEEPROMPath.empty())
+                    {
+                        getBackupRecordKeyword(record, keyword);
+                    }
+
+                    EditorImpl edit(hardwarePath, json, record, keyword);
                     edit.updateKeyword(toBinary(get<4>(data)), 0, true);
                     mismatchFound = true;
                 }
@@ -1034,11 +1047,11 @@
                          << setw(75) << setfill(' ') << "Primary Data" << left
                          << setw(14) << "Data Mismatch" << endl;
 
-                    cout << left << setw(6) << get<0>(data) << left << setw(8)
-                         << get<1>(data) << left << setw(9) << get<2>(data)
-                         << left << setw(75) << setfill(' ') << get<3>(data)
-                         << left << setw(75) << setfill(' ') << get<4>(data)
-                         << left << setw(14) << get<5>(data);
+                    cout << left << setw(6) << static_cast<int>(get<0>(data))
+                         << left << setw(8) << get<1>(data) << left << setw(9)
+                         << get<2>(data) << left << setw(75) << setfill(' ')
+                         << get<3>(data) << left << setw(75) << setfill(' ')
+                         << get<4>(data) << left << setw(14) << get<5>(data);
 
                     cout << '\n' << outline << endl;
 
@@ -1063,11 +1076,10 @@
                     cin >> option;
                     cout << '\n' << outline << endl;
 
-                    EditorImpl edit(constants::systemVpdFilePath, json,
-                                    get<1>(data), get<2>(data));
-
                     if (option == VpdTool::BACKUP_DATA_FOR_CURRENT)
                     {
+                        EditorImpl edit(constants::systemVpdFilePath, json,
+                                        get<1>(data), get<2>(data));
                         edit.updateKeyword(toBinary(get<3>(data)), 0, true);
                         cout << "\nData updated successfully.\n";
                         break;
@@ -1075,6 +1087,17 @@
                     else if (option ==
                              VpdTool::SYSTEM_BACKPLANE_DATA_FOR_CURRENT)
                     {
+                        std::string hardwarePath = constants::systemVpdFilePath;
+                        std::string record = get<1>(data);
+                        std::string keyword = get<2>(data);
+
+                        if (!backupEEPROMPath.empty())
+                        {
+                            hardwarePath = backupEEPROMPath;
+                            getBackupRecordKeyword(record, keyword);
+                        }
+
+                        EditorImpl edit(hardwarePath, json, record, keyword);
                         edit.updateKeyword(toBinary(get<4>(data)), 0, true);
                         cout << "\nData updated successfully.\n";
                         break;
@@ -1088,7 +1111,21 @@
                         cin >> value;
                         cout << '\n' << outline << endl;
 
+                        EditorImpl edit(constants::systemVpdFilePath, json,
+                                        get<1>(data), get<2>(data));
                         edit.updateKeyword(toBinary(value), 0, true);
+
+                        if (!backupEEPROMPath.empty())
+                        {
+                            std::string record = get<1>(data);
+                            std::string keyword = get<2>(data);
+
+                            getBackupRecordKeyword(record, keyword);
+                            EditorImpl edit(backupEEPROMPath, json, record,
+                                            keyword);
+                            edit.updateKeyword(toBinary(value), 0, true);
+                        }
+
                         cout << "\nData updated successfully.\n";
                         break;
                     }
@@ -1190,3 +1227,125 @@
     }
     return 0;
 }
+
+int VpdTool::fixSystemBackupVPD(const std::string& backupEepromPath,
+                                const std::string& backupInvPath)
+{
+    std::string outline(191, '=');
+    cout << "\nRestorable record-keyword pairs and their data on backup & "
+            "primary.\n\n"
+         << outline << std::endl;
+
+    cout << left << setw(6) << "S.No" << left << setw(8) << "Record" << left
+         << setw(9) << "Keyword" << left << setw(75) << "Data On Backup" << left
+         << setw(75) << "Data On Primary" << left << setw(14)
+         << "Data Mismatch\n"
+         << outline << std::endl;
+
+    uint8_t num = 0;
+    // Get system VPD data in map
+    unordered_map<string, DbusPropertyMap> systemVPDMap;
+    json js;
+    getVPDInMap(constants::systemVpdFilePath, systemVPDMap, js,
+                constants::pimPath +
+                    static_cast<std::string>(constants::SYSTEM_OBJECT));
+
+    // Get backup VPD data in map
+    unordered_map<string, DbusPropertyMap> backupVPDMap;
+    getVPDInMap(backupEepromPath, backupVPDMap, js,
+                constants::pimPath + backupInvPath);
+
+    for (const auto& recordKw : svpdKwdMap)
+    {
+        const std::string& primaryRecord = recordKw.first;
+
+        std::string primaryValStr{}, backupValStr{};
+
+        for (const auto& keywordInfo : recordKw.second)
+        {
+            const auto& primaryKeyword = get<0>(keywordInfo);
+            const auto& bkRecord = get<4>(keywordInfo);
+            const auto& bkKeyword = get<5>(keywordInfo);
+            string mismatch = "NO";
+            string primaryValue{};
+            string backupValue{};
+
+            // Find keyword value for system VPD (primary VPD)
+            auto primaryRecItr = systemVPDMap.find(primaryRecord);
+            if (primaryRecItr != systemVPDMap.end())
+            {
+                DbusPropertyMap& primaryKwValMap = primaryRecItr->second;
+                auto kwItr = primaryKwValMap.find(primaryKeyword);
+                if (kwItr != primaryKwValMap.end())
+                {
+                    primaryValue = kwItr->second;
+                }
+            }
+
+            // Find keyword value for backup VPD
+            auto bkRecItr = backupVPDMap.find(bkRecord);
+            if (bkRecItr != backupVPDMap.end())
+            {
+                DbusPropertyMap& bkKwValMap = bkRecItr->second;
+                auto kwItr = bkKwValMap.find(bkKeyword);
+                if (kwItr != bkKwValMap.end())
+                {
+                    backupValue = kwItr->second;
+                }
+            }
+
+            // SE to display in hex string only
+            if (primaryKeyword != "SE")
+            {
+                ostringstream hwValStream;
+                hwValStream << "0x";
+                primaryValStr = hwValStream.str();
+
+                for (uint16_t byte : primaryValue)
+                {
+                    hwValStream << setfill('0') << setw(2) << hex << byte;
+                    primaryValStr = hwValStream.str();
+                }
+
+                hwValStream.str(std::string());
+                hwValStream << "0x";
+                backupValStr = hwValStream.str();
+
+                for (uint16_t byte : backupValue)
+                {
+                    hwValStream << setfill('0') << setw(2) << hex << byte;
+                    backupValStr = hwValStream.str();
+                }
+                if (primaryValStr != backupValStr)
+                {
+                    mismatch = "YES";
+                }
+            }
+            else
+            {
+                if (primaryValue != backupValue)
+                {
+                    mismatch = "YES";
+                }
+
+                primaryValStr = primaryValue;
+                backupValStr = backupValue;
+            }
+
+            recKwData.push_back(make_tuple(++num, primaryRecord, primaryKeyword,
+                                           backupValStr, primaryValStr,
+                                           mismatch));
+
+            std::string splitLine(191, '-');
+            cout << left << setw(6) << static_cast<int>(num) << left << setw(8)
+                 << primaryRecord << left << setw(9) << primaryKeyword << left
+                 << setw(75) << setfill(' ') << backupValStr << left << setw(75)
+                 << setfill(' ') << primaryValStr << left << setw(14)
+                 << mismatch << '\n'
+                 << splitLine << endl;
+        }
+    }
+
+    parseSVPDOptions(js, backupEepromPath);
+    return 0;
+}