Handle CPU Module and FRU in write VPD

Test-
1. Using vpd-tool
 ./vpd-tool --readKeyword   --object  /system/chassis/motherboard --record VINI --keyword SN
{
    "/system/chassis/motherboard": {
        "SN": "YL2E2D010000"
    }
}
 ./vpd-tool --writeKeyword    --object  /system/chassis/motherboard --record VINI --keyword SN --value 0x303030

 ./vpd-tool --readKeyword   --object  /system/chassis/motherboard --record VINI --keyword SN           {
    "/system/chassis/motherboard": {
        "SN": "000E2D010000"
    }
}

COM interface-

Change-Id: I39ee0448483be581da254f5633b0817637292dff
-------------
.SN                                                  property  ay        12 48 48 48 69 50 68 48 49 48 48 48 48   emits-change writable

Common Interface-
----------------
xyz.openbmc_project.Inventory.Decorator.Asset        interface -         -                                        -
.SerialNumber                                        property  s         "000E2D010000"                           emits-change writable

sys path updated-
--------
00000110  35 53 4e 0c 30 30 30 45  32 44 30 31 30 30 30 30  |5SN.000E2D010000|

2. Tested on simics with spi driver supported image for 1 DCM
verified CPUs and other FRUs to work as expected.

1- busctl call WriteKeyword ossay  "/system/chassis/motherboard/cpu0" "VINI" "DR" 1 65
Updating CI, so updated cpu0, cpu1 and spi2 vpd.

2- busctl call WriteKeyword ossay  "/system/chassis/motherboard/cpu1" "VINI" "DR" 1 66
Updating CI, so updated cpu0, cpu1 and spi2 vpd.

3- busctl call WriteKeyword ossay  "/system/chassis/motherboard/cpu0" "VINI" "CC" 1 67
Not a CI, so only cpu0 and spi2 vpd updated.

4- busctl call WriteKeyword ossay  "/system/chassis/motherboard/cpu1" "VINI" "CC" 1 67
Not a CI, so only cpu1 and spi6 vpd updated.

cpu1 updated 1 Byte data of DR (7)-
busctl call WriteKeyword ossay "/system/chassis/motherboard/cpu1" "VINI" "DR" 1 55

Read file info at spi2.0/spi2.00/nvmem offset 0x30000-
000001a0  04 56 49 4e 49 44 52 10  37 20 57 41 59 20 20 50  |.VINIDR.7 WAY  P|  <--DR updated 1 Byte

Change-Id: I5e99c1ac4e09b2d0e05be18a10d1d50eba22fea2
Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
index b9104f0..e5d698a 100644
--- a/vpd-manager/editor_impl.cpp
+++ b/vpd-manager/editor_impl.cpp
@@ -95,7 +95,8 @@
 #else
 
     // update data in EEPROM as well. As we will not write complete file back
-    vpdFileStream.seekg(thisRecord.kwDataOffset, std::ios::beg);
+    vpdFileStream.seekp(offset + thisRecord.kwDataOffset, std::ios::beg);
+
     iteratorToNewdata = kwdData.cbegin();
     std::copy(iteratorToNewdata, end,
               std::ostreambuf_iterator<char>(vpdFileStream));
@@ -186,7 +187,7 @@
     std::advance(end, thisRecord.recECCLength);
 
 #ifndef ManagerTest
-    vpdFileStream.seekp(thisRecord.recECCoffset, std::ios::beg);
+    vpdFileStream.seekp(offset + thisRecord.recECCoffset, std::ios::beg);
     std::copy(itrToRecordECC, end,
               std::ostreambuf_iterator<char>(vpdFileStream));
 #endif
@@ -352,30 +353,62 @@
     {
         // by default inherit property is true
         bool isInherit = true;
+        bool isInheritEI = true;
+        bool isCpuModuleOnly = false;
 
         if (singleInventory.find("inherit") != singleInventory.end())
         {
             isInherit = singleInventory["inherit"].get<bool>();
         }
 
+        if (singleInventory.find("inheritEI") != singleInventory.end())
+        {
+            isInheritEI = singleInventory["inheritEI"].get<bool>();
+        }
+
+        // "type" exists only in CPU module and FRU
+        if (singleInventory.find("type") != singleInventory.end())
+        {
+            if (singleInventory["type"] == "moduleOnly")
+            {
+                isCpuModuleOnly = true;
+            }
+        }
+
         if (isInherit)
         {
             // update com interface
-            makeDbusCall<Binary>(
-                (INVENTORY_PATH +
-                 singleInventory["inventoryPath"].get<std::string>()),
-                (IPZ_INTERFACE + (std::string) "." + thisRecord.recName),
-                thisRecord.recKWd, thisRecord.kwdUpdatedData);
+            // For CPU- update  com interface only when isCI true
+            if ((!isCpuModuleOnly) || (isCpuModuleOnly && isCI))
+            {
+                makeDbusCall<Binary>(
+                    (INVENTORY_PATH +
+                     singleInventory["inventoryPath"].get<std::string>()),
+                    (IPZ_INTERFACE + (std::string) "." + thisRecord.recName),
+                    thisRecord.recKWd, thisRecord.kwdUpdatedData);
+            }
 
             // process Common interface
             processAndUpdateCI(singleInventory["inventoryPath"]
                                    .get_ref<const nlohmann::json::string_t&>());
         }
 
-        // process extra interfaces
-        processAndUpdateEI(singleInventory,
-                           singleInventory["inventoryPath"]
-                               .get_ref<const nlohmann::json::string_t&>());
+        if (isInheritEI)
+        {
+            if (isCpuModuleOnly)
+            {
+                makeDbusCall<Binary>(
+                    (INVENTORY_PATH +
+                     singleInventory["inventoryPath"].get<std::string>()),
+                    (IPZ_INTERFACE + (std::string) "." + thisRecord.recName),
+                    thisRecord.recKWd, thisRecord.kwdUpdatedData);
+            }
+
+            // process extra interfaces
+            processAndUpdateEI(singleInventory,
+                               singleInventory["inventoryPath"]
+                                   .get_ref<const nlohmann::json::string_t&>());
+        }
     }
 }
 
@@ -446,29 +479,139 @@
     }
 }
 
+string EditorImpl::getSysPathForThisFruType(const string& moduleObjPath,
+                                            const string& fruType)
+{
+    string fruVpdPath;
+
+    // get all FRUs list
+    for (const auto& eachFru : jsonFile["frus"].items())
+    {
+        bool moduleObjPathMatched = false;
+        bool expectedFruFound = false;
+
+        for (const auto& eachInventory : eachFru.value())
+        {
+            const auto& thisObjectPath = eachInventory["inventoryPath"];
+
+            // "type" exists only in CPU module and FRU
+            if (eachInventory.find("type") != eachInventory.end())
+            {
+                // If inventory type is fruAndModule then set flag
+                if (eachInventory["type"] == fruType)
+                {
+                    expectedFruFound = true;
+                }
+            }
+
+            if (thisObjectPath == moduleObjPath)
+            {
+                moduleObjPathMatched = true;
+            }
+        }
+
+        // If condition satisfies then collect this sys path and exit
+        if (expectedFruFound && moduleObjPathMatched)
+        {
+            fruVpdPath = eachFru.key();
+            break;
+        }
+    }
+
+    return fruVpdPath;
+}
+
+void EditorImpl::getVpdPathForCpu()
+{
+    isCI = false;
+    // keep a backup In case we need it later
+    inventory::Path vpdFilePathBackup = vpdFilePath;
+
+    // TODO 1:Temp hardcoded list. create it dynamically.
+    std::vector<std::string> commonIntVINIKwds = {"PN", "SN", "DR"};
+    std::vector<std::string> commonIntVR10Kwds = {"DC"};
+    std::unordered_map<std::string, std::vector<std::string>>
+        commonIntRecordsList = {{"VINI", commonIntVINIKwds},
+                                {"VR10", commonIntVR10Kwds}};
+
+    // If requested Record&Kw is one among CI, then update 'FRU' type sys
+    // path, SPI2
+    unordered_map<std::string, vector<string>>::const_iterator isCommonInt =
+        commonIntRecordsList.find(thisRecord.recName);
+
+    if ((isCommonInt != commonIntRecordsList.end()) &&
+        (find(isCommonInt->second.begin(), isCommonInt->second.end(),
+              thisRecord.recKWd) != isCommonInt->second.end()))
+    {
+        isCI = true;
+        vpdFilePath = getSysPathForThisFruType(objPath, "fruAndModule");
+    }
+    else
+    {
+        for (const auto& eachFru : jsonFile["frus"].items())
+        {
+            for (const auto& eachInventory : eachFru.value())
+            {
+                if (eachInventory.find("type") != eachInventory.end())
+                {
+                    const auto& thisObjectPath = eachInventory["inventoryPath"];
+                    if ((eachInventory["type"] == "moduleOnly") &&
+                        (eachInventory.value("inheritEI", true)) &&
+                        (thisObjectPath == static_cast<string>(objPath)))
+                    {
+                        vpdFilePath = eachFru.key();
+                    }
+                }
+            }
+        }
+    }
+    // If it is not a CPU fru then go ahead with default vpdFilePath from
+    // fruMap
+    if (vpdFilePath.empty())
+    {
+        vpdFilePath = vpdFilePathBackup;
+    }
+}
+
 void EditorImpl::updateKeyword(const Binary& kwdData)
 {
-
+    offset = 0;
 #ifndef ManagerTest
+
+    getVpdPathForCpu();
+
+    uint32_t offset = 0;
+    // check if offset present?
+    for (const auto& item : jsonFile["frus"][vpdFilePath])
+    {
+        if (item.find("offset") != item.end())
+        {
+            offset = item["offset"];
+        }
+    }
+
+    // TODO: Figure out a better way to get max possible VPD size.
+    Binary completeVPDFile;
+    completeVPDFile.resize(65504);
     vpdFileStream.open(vpdFilePath,
                        std::ios::in | std::ios::out | std::ios::binary);
 
-    if (!vpdFileStream)
-    {
-        throw std::runtime_error("unable to open vpd file to edit");
-    }
+    vpdFileStream.seekg(offset, ios_base::cur);
+    vpdFileStream.read(reinterpret_cast<char*>(&completeVPDFile[0]), 65504);
+    completeVPDFile.resize(vpdFileStream.gcount());
+    vpdFileStream.clear(std::ios_base::eofbit);
 
-    Binary completeVPDFile((std::istreambuf_iterator<char>(vpdFileStream)),
-                           std::istreambuf_iterator<char>());
     vpdFile = completeVPDFile;
+
 #else
+
     Binary completeVPDFile = vpdFile;
+
 #endif
     if (vpdFile.empty())
     {
         throw std::runtime_error("Invalid File");
     }
-
     auto iterator = vpdFile.cbegin();
     std::advance(iterator, IPZ_DATA_START);