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/ibm_vpd_utils.cpp b/ibm_vpd_utils.cpp
index 81bd87d..29817b2 100644
--- a/ibm_vpd_utils.cpp
+++ b/ibm_vpd_utils.cpp
@@ -1180,5 +1180,46 @@
         }
     }
 }
+
+void findBackupVPDPaths(std::string& backupEepromPath,
+                        std::string& backupInvPath, const nlohmann::json& js)
+{
+    for (const auto& item : js["frus"][constants::systemVpdFilePath])
+    {
+        if (item.find("systemVpdBackupPath") != item.end())
+        {
+            backupEepromPath = item["systemVpdBackupPath"];
+            for (const auto& item : js["frus"][backupEepromPath])
+            {
+                if (item.find("inventoryPath") != item.end())
+                {
+                    backupInvPath = item["inventoryPath"];
+                    break;
+                }
+            }
+            break;
+        }
+    }
+}
+
+void getBackupRecordKeyword(std::string& record, std::string& keyword)
+{
+    for (const auto& recordKw : svpdKwdMap)
+    {
+        if (record == recordKw.first)
+        {
+            for (const auto& keywordInfo : recordKw.second)
+            {
+                if (keyword == get<0>(keywordInfo))
+                {
+                    record = get<4>(keywordInfo);
+                    keyword = get<5>(keywordInfo);
+                    break;
+                }
+            }
+            break;
+        }
+    }
+}
 } // namespace vpd
 } // namespace openpower
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index b441b76..82a0083 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -539,5 +539,24 @@
  */
 void clearVpdOnRemoval(const std::string& objPath,
                        inventory::InterfaceMap& interfacesPropMap);
+
+/**
+ * @brief Find backup VPD path if any for the system VPD
+ *
+ * @param[out] backupEepromPath - Backup VPD path
+ * @param[out] backupInvPath - Backup inventory path
+ * @param[in] js - Inventory JSON object
+ */
+void findBackupVPDPaths(std::string& backupEepromPath,
+                        std::string& backupInvPath, const nlohmann::json& js);
+
+/**
+ * @brief Get backup VPD's record and keyword for the given system VPD keyword
+ *
+ * @param[in,out] record - Record name
+ * @param[in,out] keyword - Keyword name
+ */
+void getBackupRecordKeyword(std::string& record, std::string& keyword);
+
 } // namespace vpd
 } // namespace openpower
diff --git a/vpd_tool.cpp b/vpd_tool.cpp
index 9bad631..911ce7f 100644
--- a/vpd_tool.cpp
+++ b/vpd_tool.cpp
@@ -199,8 +199,20 @@
         }
         else if (*fixSystemVPDFlag)
         {
+            std::string backupEepromPath;
+            std::string backupInvPath;
+            findBackupVPDPaths(backupEepromPath, backupInvPath, jsObject);
             VpdTool vpdToolObj;
-            rc = vpdToolObj.fixSystemVPD();
+
+            if (backupEepromPath.empty())
+            {
+                rc = vpdToolObj.fixSystemVPD();
+            }
+            else
+            {
+                rc = vpdToolObj.fixSystemBackupVPD(backupEepromPath,
+                                                   backupInvPath);
+            }
         }
         else if (*mfgClean)
         {
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;
+}
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
index 11e6ef8..25f07af 100644
--- a/vpd_tool_impl.hpp
+++ b/vpd_tool_impl.hpp
@@ -12,7 +12,7 @@
 
 // <S.no, Record, Keyword, D-Bus value, HW value, Data mismatch>
 using SystemCriticalData =
-    std::vector<std::tuple<int, std::string, std::string, std::string,
+    std::vector<std::tuple<uint8_t, std::string, std::string, std::string,
                            std::string, std::string>>;
 
 class VpdTool
@@ -141,8 +141,10 @@
      * @brief Parse through the options to fix system VPD
      *
      * @param[in] json - Inventory JSON
+     * @param[in] backupEEPROMPath - Backup VPD path
      */
-    void parseSVPDOptions(const nlohmann::json& json);
+    void parseSVPDOptions(const nlohmann::json& json,
+                          const std::string& backupEEPROMPath);
 
     /**
      * @brief List of user options that can be used to fix system VPD keywords.
@@ -279,6 +281,19 @@
     int cleanSystemVPD();
 
     /**
+     * @brief Fix system VPD and its backup VPD
+     * API is triggerred if the backup of system VPD has to be taken in a
+     * hardware VPD. User can use the --fixSystemVPD option to restore the
+     * keywords in backup VPD and/or system VPD.
+     *
+     * @param[in] backupEepromPath - Backup VPD path
+     * @param[in] backupInvPath - Backup inventory path
+     * @return returncode
+     */
+    int fixSystemBackupVPD(const std::string& backupEepromPath,
+                           const std::string& backupInvPath);
+
+    /**
      * @brief Constructor
      * Constructor is called during the
      * object instantiation for dumpInventory option and