manager: Move System VPD Restore to Manager

This commit moves the system VPD restore functionality to the VPD
manager.

This ensures there are no race conditions with doing it in the parser
process when the manager is synchronizing BIOS attributes to VPD.

Tested: I tested both the VPD restore as well as the BIOS attribute sync
paths. They tested out fine.

Signed-off-by: Santosh Puranik <santosh.puranik@in.ibm.com>
Change-Id: I4e3c274a72f86ad4b4821529ffbe0526203b7df5
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 6bfafdf..43241bc 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -40,15 +40,6 @@
 using namespace openpower::vpd::exceptions;
 using namespace phosphor::logging;
 
-// Map to hold record, kwd pair which can be re-stored at standby.
-// The list of keywords for VSYS record is as per the S0 system. Should
-// be updated for another type of systems
-static const std::unordered_map<std::string, std::vector<std::string>>
-    svpdKwdMap{{"VSYS", {"BR", "TM", "SE", "SU", "RB", "WN", "RG"}},
-               {"VCEN", {"FC", "SE"}},
-               {"LXR0", {"LX"}},
-               {"UTIL", {"D0"}}};
-
 /**
  * @brief Returns the BMC state
  */
@@ -401,31 +392,6 @@
     }
 }
 
-/*API to reset EEPROM pointer to a safe position to avoid VPD corruption.
- * Currently do reset only for DIMM VPD.*/
-static void resetEEPROMPointer(const nlohmann::json& js, const string& file,
-                               ifstream& vpdFile)
-{
-    for (const auto& item : js["frus"][file])
-    {
-        if (item.find("extraInterfaces") != item.end())
-        {
-            if (item["extraInterfaces"].find(
-                    "xyz.openbmc_project.Inventory.Item.Dimm") !=
-                item["extraInterfaces"].end())
-            {
-                // moves the EEPROM pointer to 2048 'th byte.
-                vpdFile.seekg(2047, std::ios::beg);
-                // Read that byte and discard - to affirm the move
-                // operation.
-                char ch;
-                vpdFile.read(&ch, sizeof(ch));
-            }
-            return;
-        }
-    }
-}
-
 /**
  * @brief This API checks if this FRU is pcie_devices. If yes then it further
  *        checks whether it is PASS1 planar.
@@ -504,36 +470,6 @@
     return (isThisPCIeDev && isPASS1);
 }
 
-static Binary getVpdDataInVector(const nlohmann::json& js, const string& file)
-{
-    uint32_t offset = 0;
-    // check if offset present?
-    for (const auto& item : js["frus"][file])
-    {
-        if (item.find("offset") != item.end())
-        {
-            offset = item["offset"];
-        }
-    }
-
-    // TODO: Figure out a better way to get max possible VPD size.
-    auto maxVPDSize = std::min(std::filesystem::file_size(file),
-                               static_cast<uintmax_t>(65504));
-
-    Binary vpdVector;
-    vpdVector.resize(maxVPDSize);
-    ifstream vpdFile;
-    vpdFile.open(file, ios::binary);
-
-    vpdFile.seekg(offset, ios_base::cur);
-    vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize);
-    vpdVector.resize(vpdFile.gcount());
-
-    resetEEPROMPointer(js, file, vpdFile);
-
-    return vpdVector;
-}
-
 /** Performs any pre-action needed to get the FRU setup for collection.
  *
  * @param[in] json - json object
@@ -858,95 +794,6 @@
 }
 
 /**
- * @brief API to call VPD manager to write VPD to EEPROM.
- * @param[in] Object path.
- * @param[in] record to be updated.
- * @param[in] keyword to be updated.
- * @param[in] keyword data to be updated
- */
-void updateHardware(const string& objectName, const string& recName,
-                    const string& kwdName, const Binary& data)
-{
-    try
-    {
-        auto bus = sdbusplus::bus::new_default();
-        auto properties =
-            bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
-        properties.append(
-            static_cast<sdbusplus::message::object_path>(objectName));
-        properties.append(recName);
-        properties.append(kwdName);
-        properties.append(data);
-        bus.call(properties);
-    }
-    catch (const sdbusplus::exception::exception& e)
-    {
-        std::string what =
-            "VPDManager WriteKeyword api failed for inventory path " +
-            objectName;
-        what += " record " + recName;
-        what += " keyword " + kwdName;
-        what += " with bus error = " + std::string(e.what());
-
-        // map to hold additional data in case of logging pel
-        PelAdditionalData additionalData{};
-        additionalData.emplace("CALLOUT_INVENTORY_PATH", objectName);
-        additionalData.emplace("DESCRIPTION", what);
-        createPEL(additionalData, PelSeverity::WARNING, errIntfForBusFailure);
-    }
-}
-
-/**
- * @brief An api to get list of blank system VPD properties.
- * @param[in] vpdMap - IPZ vpd map.
- * @param[in] objectPath - Object path for the FRU.
- * @param[out] blankPropertyList - Properties which are blank in System VPD and
- * needs to be updated as standby.
- */
-void getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
-                             std::vector<RestoredEeproms>& blankPropertyList)
-{
-    for (const auto& systemRecKwdPair : svpdKwdMap)
-    {
-        auto it = vpdMap.find(systemRecKwdPair.first);
-
-        // check if record is found in map we got by parser
-        if (it != vpdMap.end())
-        {
-            const auto& kwdListForRecord = systemRecKwdPair.second;
-            for (const auto& keyword : kwdListForRecord)
-            {
-                DbusPropertyMap& kwdValMap = it->second;
-                auto iterator = kwdValMap.find(keyword);
-
-                if (iterator != kwdValMap.end())
-                {
-                    string& kwdValue = iterator->second;
-
-                    // check bus data
-                    const string& recordName = systemRecKwdPair.first;
-                    const string& busValue = readBusProperty(
-                        objectPath, ipzVpdInf + recordName, keyword);
-
-                    if (busValue.find_first_not_of(' ') != string::npos)
-                    {
-                        if (kwdValue.find_first_not_of(' ') == string::npos)
-                        {
-                            // implies data is blank on EEPROM but not on cache.
-                            // So EEPROM vpd update is required.
-                            Binary busData(busValue.begin(), busValue.end());
-
-                            blankPropertyList.push_back(std::make_tuple(
-                                objectPath, recordName, keyword, busData));
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-/**
  * @brief API to check if we need to restore system VPD
  * This functionality is only applicable for IPZ VPD data.
  * @param[in] vpdMap - IPZ vpd map
@@ -1498,58 +1345,7 @@
             if ((js["frus"].find(file) != js["frus"].end()) &&
                 (file == systemVpdFilePath))
             {
-                // We need manager service active to process restoring of
-                // system VPD on hardware. So in case any system restore is
-                // required, update hardware in the second trigger of parser
-                // code for system vpd file path.
-
-                std::vector<std::string> interfaces{motherBoardInterface};
-
-                // call mapper to check for object path creation
-                MapperResponse subTree =
-                    getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
-                string mboardPath =
-                    js["frus"][file].at(0).value("inventoryPath", "");
-
-                // Attempt system VPD restore if we have a motherboard
-                // object in the inventory.
-                if ((subTree.size() != 0) &&
-                    (subTree.find(pimPath + mboardPath) != subTree.end()))
-                {
-                    vpdVector = getVpdDataInVector(js, file);
-                    ParserInterface* parser =
-                        ParserFactory::getParser(vpdVector);
-                    variant<KeywordVpdMap, Store> parseResult;
-                    parseResult = parser->parse();
-
-                    if (auto pVal = get_if<Store>(&parseResult))
-                    {
-                        // map to hold all the keywords whose value is blank and
-                        // needs to be updated at standby.
-                        vector<RestoredEeproms> blankSystemVpdProperties{};
-                        getListOfBlankSystemVpd(pVal->getVpdMap(), mboardPath,
-                                                blankSystemVpdProperties);
-
-                        // if system VPD restore is required, update the
-                        // EEPROM
-                        for (const auto& item : blankSystemVpdProperties)
-                        {
-                            updateHardware(get<0>(item), get<1>(item),
-                                           get<2>(item), get<3>(item));
-                        }
-                    }
-                    else
-                    {
-                        std::cout << "Not a valid format to restore system VPD"
-                                  << std::endl;
-                    }
-                    // release the parser object
-                    ParserFactory::freeParser(parser);
-                }
-                else
-                {
-                    log<level::ERR>("No object path found");
-                }
+                // We have already collected system VPD, skip.
                 return 0;
             }
         }
diff --git a/ibm_vpd_utils.cpp b/ibm_vpd_utils.cpp
index 554092e..11d0faf 100644
--- a/ibm_vpd_utils.cpp
+++ b/ibm_vpd_utils.cpp
@@ -934,5 +934,54 @@
     cout << "Power state is: " << powerState << endl;
     return powerState;
 }
+
+Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file)
+{
+    uint32_t offset = 0;
+    // check if offset present?
+    for (const auto& item : js["frus"][file])
+    {
+        if (item.find("offset") != item.end())
+        {
+            offset = item["offset"];
+        }
+    }
+
+    // TODO: Figure out a better way to get max possible VPD size.
+    auto maxVPDSize = std::min(std::filesystem::file_size(file),
+                               static_cast<uintmax_t>(65504));
+
+    Binary vpdVector;
+    vpdVector.resize(maxVPDSize);
+    ifstream vpdFile;
+    vpdFile.open(file, ios::binary);
+
+    vpdFile.seekg(offset, ios_base::cur);
+    vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize);
+    vpdVector.resize(vpdFile.gcount());
+
+    // Make sure we reset the EEPROM pointer to a "safe" location if it was DIMM
+    // SPD that we just read.
+    for (const auto& item : js["frus"][file])
+    {
+        if (item.find("extraInterfaces") != item.end())
+        {
+            if (item["extraInterfaces"].find(
+                    "xyz.openbmc_project.Inventory.Item.Dimm") !=
+                item["extraInterfaces"].end())
+            {
+                // moves the EEPROM pointer to 2048 'th byte.
+                vpdFile.seekg(2047, std::ios::beg);
+                // Read that byte and discard - to affirm the move
+                // operation.
+                char ch;
+                vpdFile.read(&ch, sizeof(ch));
+                break;
+            }
+        }
+    }
+
+    return vpdVector;
+}
 } // namespace vpd
 } // namespace openpower
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index 7941e0b..e1f3445 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -15,6 +15,15 @@
 namespace vpd
 {
 
+// Map to hold record, kwd pair which can be re-stored at standby.
+// The list of keywords for VSYS record is as per the S0 system. Should
+// be updated for another type of systems
+static const std::unordered_map<std::string, std::vector<std::string>>
+    svpdKwdMap{{"VSYS", {"BR", "TM", "SE", "SU", "RB", "WN", "RG"}},
+               {"VCEN", {"FC", "SE"}},
+               {"LXR0", {"LX"}},
+               {"UTIL", {"D0"}}};
+
 /** @brief Return the hex representation of the incoming byte
  *
  * @param [in] c - The input byte
@@ -366,5 +375,18 @@
  * @return The chassis power state.
  */
 std::string getPowerState();
+
+/**
+ * @brief Reads VPD from the supplied EEPROM
+ *
+ * This function reads the given VPD EEPROM file and returns its contents as a
+ * byte array. It handles any offsets into the file that need to be taken care
+ * of by looking up the VPD JSON for a possible offset key.
+ *
+ * @param js[in] - The VPD JSON Object
+ * @param file[in] - The path to the EEPROM to read
+ * @return A byte array containing the raw VPD.
+ */
+Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file);
 } // namespace vpd
 } // namespace openpower
\ No newline at end of file
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index de40edf..36e9753 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -8,6 +8,7 @@
 #include "gpioMonitor.hpp"
 #include "ibm_vpd_utils.hpp"
 #include "ipz_parser.hpp"
+#include "parser_factory.hpp"
 #include "reader_impl.hpp"
 #include "vpd_exceptions.hpp"
 
@@ -21,6 +22,8 @@
 using namespace openpower::vpd::manager::reader;
 using namespace std;
 using namespace openpower::vpd::parser;
+using namespace openpower::vpd::parser::factory;
+using namespace openpower::vpd::ipz::parser;
 using namespace openpower::vpd::exceptions;
 using namespace phosphor::logging;
 
@@ -43,6 +46,7 @@
     try
     {
         processJSON();
+        restoreSystemVpd();
         listenHostState();
         listenAssetTag();
 
@@ -62,6 +66,100 @@
     }
 }
 
+/**
+ * @brief An api to get list of blank system VPD properties.
+ * @param[in] vpdMap - IPZ vpd map.
+ * @param[in] objectPath - Object path for the FRU.
+ * @param[out] blankPropertyList - Properties which are blank in System VPD and
+ * needs to be updated as standby.
+ */
+static void
+    getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
+                            std::vector<RestoredEeproms>& blankPropertyList)
+{
+    for (const auto& systemRecKwdPair : svpdKwdMap)
+    {
+        auto it = vpdMap.find(systemRecKwdPair.first);
+
+        // check if record is found in map we got by parser
+        if (it != vpdMap.end())
+        {
+            const auto& kwdListForRecord = systemRecKwdPair.second;
+            for (const auto& keyword : kwdListForRecord)
+            {
+                DbusPropertyMap& kwdValMap = it->second;
+                auto iterator = kwdValMap.find(keyword);
+
+                if (iterator != kwdValMap.end())
+                {
+                    string& kwdValue = iterator->second;
+
+                    // check bus data
+                    const string& recordName = systemRecKwdPair.first;
+                    const string& busValue = readBusProperty(
+                        objectPath, ipzVpdInf + recordName, keyword);
+
+                    if (busValue.find_first_not_of(' ') != string::npos)
+                    {
+                        if (kwdValue.find_first_not_of(' ') == string::npos)
+                        {
+                            // implies data is blank on EEPROM but not on cache.
+                            // So EEPROM vpd update is required.
+                            Binary busData(busValue.begin(), busValue.end());
+
+                            blankPropertyList.push_back(std::make_tuple(
+                                objectPath, recordName, keyword, busData));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+void Manager::restoreSystemVpd()
+{
+    std::cout << "Attempting system VPD restore" << std::endl;
+    ParserInterface* parser = nullptr;
+    try
+    {
+        auto vpdVector = getVpdDataInVector(jsonFile, systemVpdFilePath);
+        parser = ParserFactory::getParser(vpdVector);
+        auto parseResult = parser->parse();
+
+        if (auto pVal = std::get_if<Store>(&parseResult))
+        {
+            // map to hold all the keywords whose value is blank and
+            // needs to be updated at standby.
+            std::vector<RestoredEeproms> blankSystemVpdProperties{};
+            getListOfBlankSystemVpd(pVal->getVpdMap(), SYSTEM_OBJECT,
+                                    blankSystemVpdProperties);
+
+            // if system VPD restore is required, update the
+            // EEPROM
+            for (const auto& item : blankSystemVpdProperties)
+            {
+                std::cout << "Restoring keyword: " << std::get<2>(item)
+                          << std::endl;
+                writeKeyword(std::get<0>(item), std::get<1>(item),
+                             std::get<2>(item), std::get<3>(item));
+            }
+        }
+        else
+        {
+            std::cerr << "Not a valid format to restore system VPD"
+                      << std::endl;
+        }
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Failed to restore system VPD due to exception: "
+                  << e.what() << std::endl;
+    }
+    // release the parser object
+    ParserFactory::freeParser(parser);
+}
+
 void Manager::listenHostState()
 {
     static std::shared_ptr<sdbusplus::bus::match::match> hostState =
diff --git a/vpd-manager/manager.hpp b/vpd-manager/manager.hpp
index 3beb412..e2a806b 100644
--- a/vpd-manager/manager.hpp
+++ b/vpd-manager/manager.hpp
@@ -149,6 +149,16 @@
      */
     void assetTagCallback(sdbusplus::message::message& msg);
 
+    /**
+     * @brief Restores and defaulted VPD on the system VPD EEPROM.
+     *
+     * This function will read the system VPD EEPROM and check if any of the
+     * keywords that need to be preserved across FRU replacements are defaulted
+     * in the EEPROM. If they are, this function will restore them from the
+     * value that is in the D-Bus cache.
+     */
+    void restoreSystemVpd();
+
     /** @brief Persistent sdbusplus DBus bus connection. */
     sdbusplus::bus::bus _bus;
 
diff --git a/vpd-manager/meson.build b/vpd-manager/meson.build
index 9b5c5c9..772b3c4 100644
--- a/vpd-manager/meson.build
+++ b/vpd-manager/meson.build
@@ -37,5 +37,6 @@
                                 vpd_manager_dependencies,
                                 ],
                  link_with : libvpdecc,
-                 install : true
+                 install : true,
+                 cpp_args : '-DIPZ_PARSER'
                 )