Enable redundant EEPROMs

This commit adds support in the VPD manager to
automatically update redundant EEPROMs.

The redundant EEPROMs path is obtained from the
"redundantEeprom" key in the VPD JSON.

Signed-off-by: Santosh Puranik <santosh.puranik@in.ibm.com>
Change-Id: I88dd6710523dd0d46d14bec5c849950db15cba28
diff --git a/test/vpd-manager-test/editor_test.cpp b/test/vpd-manager-test/editor_test.cpp
index 7a88c59..f20f465 100644
--- a/test/vpd-manager-test/editor_test.cpp
+++ b/test/vpd-manager-test/editor_test.cpp
@@ -88,7 +88,7 @@
     {
         // Invalid kwd name
         EditorImpl edit("VINI", "SN", std::move(emptyVpdFile));
-        edit.updateKeyword(dataToUodate, true);
+        edit.updateKeyword(dataToUodate, 0, true);
     }
     catch (const std::exception& e)
     {
@@ -106,7 +106,7 @@
     {
         // the path is dummy
         EditorImpl edit("VINI", "SN", std::move(vpd));
-        edit.updateKeyword(dataToUodate, true);
+        edit.updateKeyword(dataToUodate, 0, true);
     }
     catch (const std::exception& e)
     {
@@ -125,7 +125,7 @@
     {
         // Invalid record name "VIN", path is dummy
         EditorImpl edit("VIN", "SN", std::move(vpd));
-        edit.updateKeyword(dataToUodate, true);
+        edit.updateKeyword(dataToUodate, 0, true);
     }
     catch (const std::exception& e)
     {
@@ -144,7 +144,7 @@
     {
         // All valid data
         EditorImpl edit("VINI", "Sn", std::move(vpd));
-        edit.updateKeyword(dataToUodate, true);
+        edit.updateKeyword(dataToUodate, 0, true);
     }
     catch (const std::runtime_error& e)
     {
@@ -163,7 +163,7 @@
     {
         // All valid data
         EditorImpl edit("VINI", "SN", std::move(vpd));
-        edit.updateKeyword(dataToUodate, true);
+        edit.updateKeyword(dataToUodate, 0, true);
     }
     catch (const std::runtime_error& e)
     {
@@ -177,4 +177,4 @@
     ::testing::InitGoogleTest(&argc, argv);
 
     return RUN_ALL_TESTS();
-}
+}
\ No newline at end of file
diff --git a/types.hpp b/types.hpp
index 080a976..f87b53c 100644
--- a/types.hpp
+++ b/types.hpp
@@ -35,7 +35,8 @@
 
 using VPDfilepath = std::string;
 using FruIsMotherboard = bool;
-using FrusMap = std::multimap<Path, std::pair<VPDfilepath, FruIsMotherboard>>;
+using FrusMap =
+    std::multimap<Path, std::tuple<VPDfilepath, VPDfilepath, FruIsMotherboard>>;
 using LocationCode = std::string;
 using LocationCodeMap = std::unordered_multimap<LocationCode, Path>;
 using ListOfPaths = std::vector<sdbusplus::message::object_path>;
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
index 8ae4558..30f0a41 100644
--- a/vpd-manager/editor_impl.cpp
+++ b/vpd-manager/editor_impl.cpp
@@ -497,20 +497,11 @@
     common::utility::callPIM(move(objects));
 }
 
-void EditorImpl::updateKeyword(const Binary& kwdData, const bool& updCache)
+void EditorImpl::updateKeyword(const Binary& kwdData, uint32_t offset,
+                               const bool& updCache)
 {
-    startOffset = 0;
+    startOffset = offset;
 #ifndef ManagerTest
-
-    // check if offset present?
-    for (const auto& item : jsonFile["frus"][vpdFilePath])
-    {
-        if (item.find("offset") != item.end())
-        {
-            startOffset = item["offset"];
-        }
-    }
-
     // TODO: Figure out a better way to get max possible VPD size.
     Binary completeVPDFile;
     completeVPDFile.resize(65504);
diff --git a/vpd-manager/editor_impl.hpp b/vpd-manager/editor_impl.hpp
index c03354a..7c3eb9e 100644
--- a/vpd-manager/editor_impl.hpp
+++ b/vpd-manager/editor_impl.hpp
@@ -93,9 +93,11 @@
      * 3) update the data for that keyword with the new data
      *
      * @param[in] kwdData - data to update
+     * @param[in] offset - offset at which the VPD starts
      * @param[in] updCache - Flag which tells whether to update Cache or not.
      */
-    void updateKeyword(const Binary& kwdData, const bool& updCache);
+    void updateKeyword(const Binary& kwdData, uint32_t offset,
+                       const bool& updCache);
 
     /** @brief Expands location code on DBUS
      *  @param[in] locationCodeType - "fcs" or "mts"
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index 036f843..d391a0b 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -121,15 +121,23 @@
         for (const auto& itemEEPROM : groupEEPROM)
         {
             bool isMotherboard = false;
+            std::string redundantPath;
+
             if (itemEEPROM["extraInterfaces"].find(
                     "xyz.openbmc_project.Inventory.Item.Board.Motherboard") !=
                 itemEEPROM["extraInterfaces"].end())
             {
                 isMotherboard = true;
             }
-            frus.emplace(itemEEPROM["inventoryPath"]
-                             .get_ref<const nlohmann::json::string_t&>(),
-                         std::make_pair(itemFRUS.key(), isMotherboard));
+            if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end())
+            {
+                redundantPath = itemEEPROM["redundantEeprom"]
+                                    .get_ref<const nlohmann::json::string_t&>();
+            }
+            frus.emplace(
+                itemEEPROM["inventoryPath"]
+                    .get_ref<const nlohmann::json::string_t&>(),
+                std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard));
 
             if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
                 itemEEPROM["extraInterfaces"].end())
@@ -168,14 +176,35 @@
             throw std::runtime_error("Inventory path not found");
         }
 
-        inventory::Path vpdFilePath = frus.find(objPath)->second.first;
+        inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
 
         // instantiate editor class to update the data
         EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath);
-        edit.updateKeyword(value, true);
+
+        uint32_t offset = 0;
+        // Setup offset, if any
+        for (const auto& item : jsonFile["frus"][vpdFilePath])
+        {
+            if (item.find("offset") != item.end())
+            {
+                offset = item["offset"];
+                break;
+            }
+        }
+
+        edit.updateKeyword(value, offset, true);
+
+        // If we have a redundant EEPROM to update, then update just the EEPROM,
+        // not the cache since that is already done when we updated the primary
+        if (!std::get<1>(frus.find(objPath)->second).empty())
+        {
+            EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile,
+                            recordName, keyword);
+            edit.updateKeyword(value, offset, false);
+        }
 
         // if it is a motehrboard FRU need to check for location expansion
-        if (frus.find(objPath)->second.second)
+        if (std::get<2>(frus.find(objPath)->second))
         {
             if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE"))
             {
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index 4d9f1a3..8453ce2 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -495,13 +495,20 @@
     try
     {
         auto json = nlohmann::json::parse(inventoryJson);
+        uint32_t offset = 0;
         EditorImpl edit(fruPath, json, recordName, keyword);
         if (!((isPathInJson(fruPath)) &&
               (isRecKwInDbusJson(recordName, keyword))))
         {
             updCache = false;
         }
-        edit.updateKeyword(val, updCache);
+        if (fruPath.starts_with("/sys/bus/spi"))
+        {
+            // TODO: Figure out a better way to get this, SPI eeproms
+            // start at offset 0x30000
+            offset = 0x30000;
+        }
+        edit.updateKeyword(val, offset, updCache);
     }
     catch (const json::parse_error& ex)
     {