Revamped code for VPD parser

The commit removes all the pre-existing code from the branch
and pushes the revamped code.

Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.

Note: This code supports all the existing functionalities as it is.

Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/src/backup_restore.cpp b/vpd-manager/src/backup_restore.cpp
new file mode 100644
index 0000000..294efc4
--- /dev/null
+++ b/vpd-manager/src/backup_restore.cpp
@@ -0,0 +1,384 @@
+#include "backup_restore.hpp"
+
+#include "constants.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "types.hpp"
+
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+namespace vpd
+{
+BackupAndRestoreStatus BackupAndRestore::m_backupAndRestoreStatus =
+    BackupAndRestoreStatus::NotStarted;
+
+BackupAndRestore::BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj) :
+    m_sysCfgJsonObj(i_sysCfgJsonObj)
+{
+    std::string l_backupAndRestoreCfgFilePath =
+        i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
+    try
+    {
+        m_backupAndRestoreCfgJsonObj =
+            jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
+    }
+    catch (const std::exception& ex)
+    {
+        logging::logMessage(
+            "Failed to intialize backup and restore object for file = " +
+            l_backupAndRestoreCfgFilePath);
+        throw(ex);
+    }
+}
+
+std::tuple<types::VPDMapVariant, types::VPDMapVariant>
+    BackupAndRestore::backupAndRestore()
+{
+    auto l_emptyVariantPair =
+        std::make_tuple(std::monostate{}, std::monostate{});
+
+    if (m_backupAndRestoreStatus >= BackupAndRestoreStatus::Invoked)
+    {
+        logging::logMessage("Backup and restore invoked already.");
+        return l_emptyVariantPair;
+    }
+
+    m_backupAndRestoreStatus = BackupAndRestoreStatus::Invoked;
+    try
+    {
+        if (m_backupAndRestoreCfgJsonObj.empty() ||
+            !m_backupAndRestoreCfgJsonObj.contains("source") ||
+            !m_backupAndRestoreCfgJsonObj.contains("destination") ||
+            !m_backupAndRestoreCfgJsonObj.contains("type") ||
+            !m_backupAndRestoreCfgJsonObj.contains("backupMap"))
+        {
+            logging::logMessage(
+                "Backup restore config JSON is missing necessary tag(s), can't initiate backup and restore.");
+            return l_emptyVariantPair;
+        }
+
+        std::string l_srcVpdPath;
+        types::VPDMapVariant l_srcVpdVariant;
+        if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value(
+                "hardwarePath", "");
+            !l_srcVpdPath.empty() && std::filesystem::exists(l_srcVpdPath))
+        {
+            std::shared_ptr<Parser> l_vpdParser =
+                std::make_shared<Parser>(l_srcVpdPath, m_sysCfgJsonObj);
+            l_srcVpdVariant = l_vpdParser->parse();
+        }
+        else if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value(
+                     "inventoryPath", "");
+                 l_srcVpdPath.empty())
+        {
+            logging::logMessage(
+                "Couldn't extract source path, can't initiate backup and restore.");
+            return l_emptyVariantPair;
+        }
+
+        std::string l_dstVpdPath;
+        types::VPDMapVariant l_dstVpdVariant;
+        if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"].value(
+                "hardwarePath", "");
+            !l_dstVpdPath.empty() && std::filesystem::exists(l_dstVpdPath))
+        {
+            std::shared_ptr<Parser> l_vpdParser =
+                std::make_shared<Parser>(l_dstVpdPath, m_sysCfgJsonObj);
+            l_dstVpdVariant = l_vpdParser->parse();
+        }
+        else if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"]
+                                    .value("inventoryPath", "");
+                 l_dstVpdPath.empty())
+        {
+            logging::logMessage(
+                "Couldn't extract destination path, can't initiate backup and restore.");
+            return l_emptyVariantPair;
+        }
+
+        // Implement backup and restore for IPZ type VPD
+        auto l_backupAndRestoreType =
+            m_backupAndRestoreCfgJsonObj.value("type", "");
+        if (l_backupAndRestoreType.compare("IPZ") == constants::STR_CMP_SUCCESS)
+        {
+            types::IPZVpdMap l_srcVpdMap;
+            if (auto l_srcVpdPtr =
+                    std::get_if<types::IPZVpdMap>(&l_srcVpdVariant))
+            {
+                l_srcVpdMap = *l_srcVpdPtr;
+            }
+            else if (!std::holds_alternative<std::monostate>(l_srcVpdVariant))
+            {
+                logging::logMessage("Source VPD is not of IPZ type.");
+                return l_emptyVariantPair;
+            }
+
+            types::IPZVpdMap l_dstVpdMap;
+            if (auto l_dstVpdPtr =
+                    std::get_if<types::IPZVpdMap>(&l_dstVpdVariant))
+            {
+                l_dstVpdMap = *l_dstVpdPtr;
+            }
+            else if (!std::holds_alternative<std::monostate>(l_dstVpdVariant))
+            {
+                logging::logMessage("Destination VPD is not of IPZ type.");
+                return l_emptyVariantPair;
+            }
+
+            backupAndRestoreIpzVpd(l_srcVpdMap, l_dstVpdMap, l_srcVpdPath,
+                                   l_dstVpdPath);
+            m_backupAndRestoreStatus = BackupAndRestoreStatus::Completed;
+
+            return std::make_tuple(l_srcVpdMap, l_dstVpdMap);
+        }
+        // Note: add implementation here to support any other VPD type.
+    }
+    catch (const std::exception& ex)
+    {
+        logging::logMessage("Back up and restore failed with exception: " +
+                            std::string(ex.what()));
+    }
+    return l_emptyVariantPair;
+}
+
+void BackupAndRestore::backupAndRestoreIpzVpd(
+    types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap,
+    const std::string& i_srcPath, const std::string& i_dstPath)
+{
+    if (!m_backupAndRestoreCfgJsonObj["backupMap"].is_array())
+    {
+        logging::logMessage(
+            "Invalid value found for tag backupMap, in backup and restore config JSON.");
+        return;
+    }
+
+    const std::string l_srcFruPath =
+        jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath);
+    const std::string l_dstFruPath =
+        jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath);
+    if (l_srcFruPath.empty() || l_dstFruPath.empty())
+    {
+        logging::logMessage(
+            "Couldn't find either source or destination FRU path.");
+        return;
+    }
+
+    const std::string l_srcInvPath =
+        jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_srcPath);
+    const std::string l_dstInvPath =
+        jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_dstPath);
+    if (l_srcInvPath.empty() || l_dstInvPath.empty())
+    {
+        logging::logMessage(
+            "Couldn't find either source or destination inventory path.");
+        return;
+    }
+
+    const std::string l_srcServiceName =
+        jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath);
+    const std::string l_dstServiceName =
+        jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath);
+    if (l_srcServiceName.empty() || l_dstServiceName.empty())
+    {
+        logging::logMessage(
+            "Couldn't find either source or destination DBus service name.");
+        return;
+    }
+
+    for (const auto& l_aRecordKwInfo :
+         m_backupAndRestoreCfgJsonObj["backupMap"])
+    {
+        const std::string& l_srcRecordName =
+            l_aRecordKwInfo.value("sourceRecord", "");
+        const std::string& l_srcKeywordName =
+            l_aRecordKwInfo.value("sourceKeyword", "");
+        const std::string& l_dstRecordName =
+            l_aRecordKwInfo.value("destinationRecord", "");
+        const std::string& l_dstKeywordName =
+            l_aRecordKwInfo.value("destinationKeyword", "");
+
+        if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
+            l_srcKeywordName.empty() || l_dstKeywordName.empty())
+        {
+            logging::logMessage(
+                "Record or keyword not found in the backup and restore config JSON.");
+            continue;
+        }
+
+        if (!io_srcVpdMap.empty() &&
+            io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end())
+        {
+            logging::logMessage(
+                "Record: " + l_srcRecordName +
+                ", is not found in the source path: " + i_srcPath);
+            continue;
+        }
+
+        if (!io_dstVpdMap.empty() &&
+            io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end())
+        {
+            logging::logMessage(
+                "Record: " + l_dstRecordName +
+                ", is not found in the destination path: " + i_dstPath);
+            continue;
+        }
+
+        types::BinaryVector l_defaultBinaryValue;
+        if (l_aRecordKwInfo.contains("defaultValue") &&
+            l_aRecordKwInfo["defaultValue"].is_array())
+        {
+            l_defaultBinaryValue =
+                l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>();
+        }
+        else
+        {
+            logging::logMessage(
+                "Couldn't read default value for record name: " +
+                l_srcRecordName + ", keyword name: " + l_srcKeywordName +
+                " from backup and restore config JSON file.");
+            continue;
+        }
+
+        bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false);
+
+        types::BinaryVector l_srcBinaryValue;
+        std::string l_srcStrValue;
+        if (!io_srcVpdMap.empty())
+        {
+            vpdSpecificUtility::getKwVal(io_srcVpdMap.at(l_srcRecordName),
+                                         l_srcKeywordName, l_srcStrValue);
+            l_srcBinaryValue =
+                types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end());
+        }
+        else
+        {
+            // Read keyword value from DBus
+            const auto l_value = dbusUtility::readDbusProperty(
+                l_srcServiceName, l_srcInvPath,
+                constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName);
+            if (const auto l_binaryValue =
+                    std::get_if<types::BinaryVector>(&l_value))
+            {
+                l_srcBinaryValue = *l_binaryValue;
+                l_srcStrValue = std::string(l_srcBinaryValue.begin(),
+                                            l_srcBinaryValue.end());
+            }
+        }
+
+        types::BinaryVector l_dstBinaryValue;
+        std::string l_dstStrValue;
+        if (!io_dstVpdMap.empty())
+        {
+            vpdSpecificUtility::getKwVal(io_dstVpdMap.at(l_dstRecordName),
+                                         l_dstKeywordName, l_dstStrValue);
+            l_dstBinaryValue =
+                types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end());
+        }
+        else
+        {
+            // Read keyword value from DBus
+            const auto l_value = dbusUtility::readDbusProperty(
+                l_dstServiceName, l_dstInvPath,
+                constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName);
+            if (const auto l_binaryValue =
+                    std::get_if<types::BinaryVector>(&l_value))
+            {
+                l_dstBinaryValue = *l_binaryValue;
+                l_dstStrValue = std::string(l_dstBinaryValue.begin(),
+                                            l_dstBinaryValue.end());
+            }
+        }
+
+        if (l_srcBinaryValue != l_dstBinaryValue)
+        {
+            // ToDo: Handle if there is no valid default value in the backup and
+            // restore config JSON.
+            if (l_dstBinaryValue == l_defaultBinaryValue)
+            {
+                // Update keyword's value on hardware
+                auto l_vpdParser =
+                    std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj);
+
+                auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword(
+                    types::IpzData(l_dstRecordName, l_dstKeywordName,
+                                   l_srcBinaryValue));
+
+                /* To keep the data in sync between hardware and parsed map
+                 updating the io_dstVpdMap. This should only be done if write
+                 on hardware returns success.*/
+                if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0)
+                {
+                    io_dstVpdMap[l_dstRecordName][l_dstKeywordName] =
+                        l_srcStrValue;
+                }
+                continue;
+            }
+
+            if (l_srcBinaryValue == l_defaultBinaryValue)
+            {
+                // Update keyword's value on hardware
+                auto l_vpdParser =
+                    std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj);
+
+                auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword(
+                    types::IpzData(l_srcRecordName, l_srcKeywordName,
+                                   l_dstBinaryValue));
+
+                /* To keep the data in sync between hardware and parsed map
+                 updating the io_srcVpdMap. This should only be done if write
+                 on hardware returns success.*/
+                if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0)
+                {
+                    io_srcVpdMap[l_srcRecordName][l_srcKeywordName] =
+                        l_dstStrValue;
+                }
+            }
+            else
+            {
+                /**
+                 * Update io_srcVpdMap to publish the same data on DBus, which
+                 * is already present on the DBus. Because after calling
+                 * backupAndRestore API the map value will get published to DBus
+                 * in the worker flow.
+                 */
+                if (!io_srcVpdMap.empty() && io_dstVpdMap.empty())
+                {
+                    io_srcVpdMap[l_srcRecordName][l_srcKeywordName] =
+                        l_dstStrValue;
+                }
+
+                std::string l_errorMsg(
+                    "Mismatch found between source and destination VPD for record : " +
+                    l_srcRecordName + " and keyword : " + l_srcKeywordName +
+                    " . Value read from source : " + l_srcStrValue +
+                    " . Value read from destination : " + l_dstStrValue);
+
+                EventLogger::createSyncPel(
+                    types::ErrorType::VpdMismatch, types::SeverityType::Warning,
+                    __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt,
+                    std::nullopt, std::nullopt, std::nullopt);
+            }
+        }
+        else if (l_srcBinaryValue == l_defaultBinaryValue &&
+                 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired)
+        {
+            std::string l_errorMsg(
+                "Default value found on both source and destination VPD, for record: " +
+                l_srcRecordName + " and keyword: " + l_srcKeywordName);
+
+            EventLogger::createSyncPel(
+                types::ErrorType::DefaultValue, types::SeverityType::Error,
+                __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt,
+                std::nullopt, std::nullopt, std::nullopt);
+        }
+    }
+}
+
+void BackupAndRestore::setBackupAndRestoreStatus(
+    const BackupAndRestoreStatus& i_status)
+{
+    m_backupAndRestoreStatus = i_status;
+}
+} // namespace vpd
diff --git a/vpd-manager/src/bios_handler.cpp b/vpd-manager/src/bios_handler.cpp
new file mode 100644
index 0000000..44afff3
--- /dev/null
+++ b/vpd-manager/src/bios_handler.cpp
@@ -0,0 +1,764 @@
+#include "config.h"
+
+#include "bios_handler.hpp"
+
+#include "constants.hpp"
+#include "logger.hpp"
+
+#include <sdbusplus/bus/match.hpp>
+#include <utility/common_utility.hpp>
+#include <utility/dbus_utility.hpp>
+
+#include <string>
+
+namespace vpd
+{
+// Template declaration to define APIs.
+template class BiosHandler<IbmBiosHandler>;
+
+template <typename T>
+void BiosHandler<T>::checkAndListenPldmService()
+{
+    // Setup a call back match on NameOwnerChanged to determine when PLDM is up.
+    static std::shared_ptr<sdbusplus::bus::match_t> l_nameOwnerMatch =
+        std::make_shared<sdbusplus::bus::match_t>(
+            *m_asioConn,
+            sdbusplus::bus::match::rules::nameOwnerChanged(
+                constants::pldmServiceName),
+            [this](sdbusplus::message_t& l_msg) {
+                if (l_msg.is_method_error())
+                {
+                    logging::logMessage(
+                        "Error in reading PLDM name owner changed signal.");
+                    return;
+                }
+
+                std::string l_name;
+                std::string l_newOwner;
+                std::string l_oldOwner;
+
+                l_msg.read(l_name, l_oldOwner, l_newOwner);
+
+                if (!l_newOwner.empty() &&
+                    (l_name.compare(constants::pldmServiceName) ==
+                     constants::STR_CMP_SUCCESS))
+                {
+                    m_specificBiosHandler->backUpOrRestoreBiosAttributes();
+
+                    // Start listener now that we have done the restore.
+                    listenBiosAttributes();
+
+                    //  We don't need the match anymore
+                    l_nameOwnerMatch.reset();
+                }
+            });
+
+    // Based on PLDM service status reset owner match registered above and
+    // trigger BIOS attribute sync.
+    if (dbusUtility::isServiceRunning(constants::pldmServiceName))
+    {
+        l_nameOwnerMatch.reset();
+        m_specificBiosHandler->backUpOrRestoreBiosAttributes();
+
+        // Start listener now that we have done the restore.
+        listenBiosAttributes();
+    }
+}
+
+template <typename T>
+void BiosHandler<T>::listenBiosAttributes()
+{
+    static std::shared_ptr<sdbusplus::bus::match_t> l_biosMatch =
+        std::make_shared<sdbusplus::bus::match_t>(
+            *m_asioConn,
+            sdbusplus::bus::match::rules::propertiesChanged(
+                constants::biosConfigMgrObjPath,
+                constants::biosConfigMgrInterface),
+            [this](sdbusplus::message_t& l_msg) {
+                m_specificBiosHandler->biosAttributesCallback(l_msg);
+            });
+}
+
+void IbmBiosHandler::biosAttributesCallback(sdbusplus::message_t& i_msg)
+{
+    if (i_msg.is_method_error())
+    {
+        logging::logMessage("Error in reading BIOS attribute signal. ");
+        return;
+    }
+
+    std::string l_objPath;
+    types::BiosBaseTableType l_propMap;
+    i_msg.read(l_objPath, l_propMap);
+
+    for (auto l_property : l_propMap)
+    {
+        if (l_property.first != "BaseBIOSTable")
+        {
+            // Looking for change in Base BIOS table only.
+            continue;
+        }
+
+        if (auto l_attributeList =
+                std::get_if<std::map<std::string, types::BiosProperty>>(
+                    &(l_property.second)))
+        {
+            for (const auto& l_attribute : *l_attributeList)
+            {
+                if (auto l_val = std::get_if<std::string>(
+                        &(std::get<5>(std::get<1>(l_attribute)))))
+                {
+                    std::string l_attributeName = std::get<0>(l_attribute);
+                    if (l_attributeName == "hb_memory_mirror_mode")
+                    {
+                        saveAmmToVpd(*l_val);
+                    }
+
+                    if (l_attributeName == "pvm_keep_and_clear")
+                    {
+                        saveKeepAndClearToVpd(*l_val);
+                    }
+
+                    if (l_attributeName == "pvm_create_default_lpar")
+                    {
+                        saveCreateDefaultLparToVpd(*l_val);
+                    }
+
+                    if (l_attributeName == "pvm_clear_nvram")
+                    {
+                        saveClearNvramToVpd(*l_val);
+                    }
+
+                    continue;
+                }
+
+                if (auto l_val = std::get_if<int64_t>(
+                        &(std::get<5>(std::get<1>(l_attribute)))))
+                {
+                    std::string l_attributeName = std::get<0>(l_attribute);
+                    if (l_attributeName == "hb_field_core_override")
+                    {
+                        saveFcoToVpd(*l_val);
+                    }
+                }
+            }
+        }
+        else
+        {
+            // TODO: log a predicitive PEL.
+            logging::logMessage("Invalid typre received from BIOS table.");
+            break;
+        }
+    }
+}
+
+void IbmBiosHandler::backUpOrRestoreBiosAttributes()
+{
+    // process FCO
+    processFieldCoreOverride();
+
+    // process AMM
+    processActiveMemoryMirror();
+
+    // process LPAR
+    processCreateDefaultLpar();
+
+    // process clear NVRAM
+    processClearNvram();
+
+    // process keep and clear
+    processKeepAndClear();
+}
+
+types::BiosAttributeCurrentValue
+    IbmBiosHandler::readBiosAttribute(const std::string& i_attributeName)
+{
+    types::BiosAttributeCurrentValue l_attrValueVariant =
+        dbusUtility::biosGetAttributeMethodCall(i_attributeName);
+
+    return l_attrValueVariant;
+}
+
+void IbmBiosHandler::processFieldCoreOverride()
+{
+    // TODO: Should we avoid doing this at runtime?
+
+    // Read required keyword from Dbus.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::vsysInf, constants::kwdRG);
+
+    if (auto l_fcoInVpd = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        // default length of the keyword is 4 bytes.
+        if (l_fcoInVpd->size() != constants::VALUE_4)
+        {
+            logging::logMessage(
+                "Invalid value read for FCO from D-Bus. Skipping.");
+        }
+
+        //  If FCO in VPD contains anything other that ASCII Space, restore to
+        //  BIOS
+        if (std::any_of(l_fcoInVpd->cbegin(), l_fcoInVpd->cend(),
+                        [](uint8_t l_val) {
+                            return l_val != constants::ASCII_OF_SPACE;
+                        }))
+        {
+            // Restore the data to BIOS.
+            saveFcoToBios(*l_fcoInVpd);
+        }
+        else
+        {
+            types::BiosAttributeCurrentValue l_attrValueVariant =
+                readBiosAttribute("hb_field_core_override");
+
+            if (auto l_fcoInBios = std::get_if<int64_t>(&l_attrValueVariant))
+            {
+                // save the BIOS data to VPD
+                saveFcoToVpd(*l_fcoInBios);
+
+                return;
+            }
+            logging::logMessage("Invalid type recieved for FCO from BIOS.");
+        }
+        return;
+    }
+    logging::logMessage("Invalid type recieved for FCO from VPD.");
+}
+
+void IbmBiosHandler::saveFcoToVpd(int64_t i_fcoInBios)
+{
+    if (i_fcoInBios < 0)
+    {
+        logging::logMessage("Invalid FCO value in BIOS. Skip updating to VPD");
+        return;
+    }
+
+    // Read required keyword from Dbus.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::vsysInf, constants::kwdRG);
+
+    if (auto l_fcoInVpd = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        // default length of the keyword is 4 bytes.
+        if (l_fcoInVpd->size() != constants::VALUE_4)
+        {
+            logging::logMessage(
+                "Invalid value read for FCO from D-Bus. Skipping.");
+            return;
+        }
+
+        // convert to VPD value type
+        types::BinaryVector l_biosValInVpdFormat = {
+            0, 0, 0, static_cast<uint8_t>(i_fcoInBios)};
+
+        // Update only when the data are different.
+        if (std::memcmp(l_biosValInVpdFormat.data(), l_fcoInVpd->data(),
+                        constants::VALUE_4) != constants::SUCCESS)
+        {
+            if (constants::FAILURE ==
+                m_manager->updateKeyword(
+                    SYSTEM_VPD_FILE_PATH,
+                    types::IpzData("VSYS", constants::kwdRG,
+                                   l_biosValInVpdFormat)))
+            {
+                logging::logMessage(
+                    "Failed to update " + std::string(constants::kwdRG) +
+                    " keyword to VPD.");
+            }
+        }
+    }
+    else
+    {
+        logging::logMessage("Invalid type read for FCO from DBus.");
+    }
+}
+
+void IbmBiosHandler::saveFcoToBios(const types::BinaryVector& i_fcoVal)
+{
+    if (i_fcoVal.size() != constants::VALUE_4)
+    {
+        logging::logMessage("Bad size for FCO received. Skip writing to BIOS");
+        return;
+    }
+
+    types::PendingBIOSAttrs l_pendingBiosAttribute;
+    l_pendingBiosAttribute.push_back(std::make_pair(
+        "hb_field_core_override",
+        std::make_tuple(
+            "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer",
+            i_fcoVal.at(constants::VALUE_3))));
+
+    try
+    {
+        dbusUtility::writeDbusProperty(
+            constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+            constants::biosConfigMgrInterface, "PendingAttributes",
+            l_pendingBiosAttribute);
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO: Should we log informational PEL here as well?
+        logging::logMessage(
+            "DBus call to update FCO value in pending attribute failed. " +
+            std::string(l_ex.what()));
+    }
+}
+
+void IbmBiosHandler::saveAmmToVpd(const std::string& i_memoryMirrorMode)
+{
+    if (i_memoryMirrorMode.empty())
+    {
+        logging::logMessage(
+            "Empty memory mirror mode value from BIOS. Skip writing to VPD");
+        return;
+    }
+
+    // Read existing value.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdAMM);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        auto l_ammValInVpd = *l_pVal;
+
+        types::BinaryVector l_valToUpdateInVpd{
+            (i_memoryMirrorMode == "Enabled" ? constants::AMM_ENABLED_IN_VPD
+                                             : constants::AMM_DISABLED_IN_VPD)};
+
+        // Check if value is already updated on VPD.
+        if (l_ammValInVpd.at(0) == l_valToUpdateInVpd.at(0))
+        {
+            return;
+        }
+
+        if (constants::FAILURE ==
+            m_manager->updateKeyword(
+                SYSTEM_VPD_FILE_PATH,
+                types::IpzData("UTIL", constants::kwdAMM, l_valToUpdateInVpd)))
+        {
+            logging::logMessage(
+                "Failed to update " + std::string(constants::kwdAMM) +
+                " keyword to VPD");
+        }
+    }
+    else
+    {
+        // TODO: Add PEL
+        logging::logMessage(
+            "Invalid type read for memory mirror mode value from DBus. Skip writing to VPD");
+    }
+}
+
+void IbmBiosHandler::saveAmmToBios(const std::string& i_ammVal)
+{
+    if (i_ammVal.size() != constants::VALUE_1)
+    {
+        logging::logMessage("Bad size for AMM received, Skip writing to BIOS");
+        return;
+    }
+
+    const std::string l_valtoUpdate =
+        (i_ammVal.at(0) == constants::VALUE_2) ? "Enabled" : "Disabled";
+
+    types::PendingBIOSAttrs l_pendingBiosAttribute;
+    l_pendingBiosAttribute.push_back(std::make_pair(
+        "hb_memory_mirror_mode",
+        std::make_tuple(
+            "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+            l_valtoUpdate)));
+
+    try
+    {
+        dbusUtility::writeDbusProperty(
+            constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+            constants::biosConfigMgrInterface, "PendingAttributes",
+            l_pendingBiosAttribute);
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO: Should we log informational PEL here as well?
+        logging::logMessage(
+            "DBus call to update AMM value in pending attribute failed. " +
+            std::string(l_ex.what()));
+    }
+}
+
+void IbmBiosHandler::processActiveMemoryMirror()
+{
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdAMM);
+
+    if (auto pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        auto l_ammValInVpd = *pVal;
+
+        // Check if active memory mirror value is default in VPD.
+        if (l_ammValInVpd.at(0) == constants::VALUE_0)
+        {
+            types::BiosAttributeCurrentValue l_attrValueVariant =
+                readBiosAttribute("hb_memory_mirror_mode");
+
+            if (auto pVal = std::get_if<std::string>(&l_attrValueVariant))
+            {
+                saveAmmToVpd(*pVal);
+                return;
+            }
+            logging::logMessage(
+                "Invalid type recieved for auto memory mirror mode from BIOS.");
+            return;
+        }
+        else
+        {
+            saveAmmToBios(std::to_string(l_ammValInVpd.at(0)));
+        }
+        return;
+    }
+    logging::logMessage(
+        "Invalid type recieved for auto memory mirror mode from VPD.");
+}
+
+void IbmBiosHandler::saveCreateDefaultLparToVpd(
+    const std::string& i_createDefaultLparVal)
+{
+    if (i_createDefaultLparVal.empty())
+    {
+        logging::logMessage(
+            "Empty value received for Lpar from BIOS. Skip writing in VPD.");
+        return;
+    }
+
+    // Read required keyword from DBus as we need to set only a Bit.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        commonUtility::toLower(
+            const_cast<std::string&>(i_createDefaultLparVal));
+
+        // Check for second bit. Bit set for enabled else disabled.
+        if (((((*l_pVal).at(0) & 0x02) == 0x02) &&
+             (i_createDefaultLparVal.compare("enabled") ==
+              constants::STR_CMP_SUCCESS)) ||
+            ((((*l_pVal).at(0) & 0x02) == 0x00) &&
+             (i_createDefaultLparVal.compare("disabled") ==
+              constants::STR_CMP_SUCCESS)))
+        {
+            // Values are same, Don;t update.
+            return;
+        }
+
+        types::BinaryVector l_valToUpdateInVpd;
+        if (i_createDefaultLparVal.compare("enabled") ==
+            constants::STR_CMP_SUCCESS)
+        {
+            // 2nd Bit is used to store the value.
+            l_valToUpdateInVpd.emplace_back((*l_pVal).at(0) | 0x02);
+        }
+        else
+        {
+            // 2nd Bit is used to store the value.
+            l_valToUpdateInVpd.emplace_back((*l_pVal).at(0) & ~(0x02));
+        }
+
+        if (-1 ==
+            m_manager->updateKeyword(
+                SYSTEM_VPD_FILE_PATH,
+                types::IpzData("UTIL", constants::kwdClearNVRAM_CreateLPAR,
+                               l_valToUpdateInVpd)))
+        {
+            logging::logMessage(
+                "Failed to update " +
+                std::string(constants::kwdClearNVRAM_CreateLPAR) +
+                " keyword to VPD");
+        }
+
+        return;
+    }
+    logging::logMessage(
+        "Invalid type recieved for create default Lpar from VPD.");
+}
+
+void IbmBiosHandler::saveCreateDefaultLparToBios(
+    const std::string& i_createDefaultLparVal)
+{
+    // checking for exact length as it is a string and can have garbage value.
+    if (i_createDefaultLparVal.size() != constants::VALUE_1)
+    {
+        logging::logMessage(
+            "Bad size for Create default LPAR in VPD. Skip writing to BIOS.");
+        return;
+    }
+
+    std::string l_valtoUpdate =
+        (i_createDefaultLparVal.at(0) & 0x02) ? "Enabled" : "Disabled";
+
+    types::PendingBIOSAttrs l_pendingBiosAttribute;
+    l_pendingBiosAttribute.push_back(std::make_pair(
+        "pvm_create_default_lpar",
+        std::make_tuple(
+            "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+            l_valtoUpdate)));
+
+    try
+    {
+        dbusUtility::writeDbusProperty(
+            constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+            constants::biosConfigMgrInterface, "PendingAttributes",
+            l_pendingBiosAttribute);
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(
+            "DBus call to update lpar value in pending attribute failed. " +
+            std::string(l_ex.what()));
+    }
+
+    return;
+}
+
+void IbmBiosHandler::processCreateDefaultLpar()
+{
+    // Read required keyword from DBus.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        saveCreateDefaultLparToBios(std::to_string(l_pVal->at(0)));
+        return;
+    }
+    logging::logMessage(
+        "Invalid type recieved for create default Lpar from VPD.");
+}
+
+void IbmBiosHandler::saveClearNvramToVpd(const std::string& i_clearNvramVal)
+{
+    if (i_clearNvramVal.empty())
+    {
+        logging::logMessage(
+            "Empty value received for clear NVRAM from BIOS. Skip updating to VPD.");
+        return;
+    }
+
+    // Read required keyword from DBus as we need to set only a Bit.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        commonUtility::toLower(const_cast<std::string&>(i_clearNvramVal));
+
+        // Check for third bit. Bit set for enabled else disabled.
+        if (((((*l_pVal).at(0) & 0x04) == 0x04) &&
+             (i_clearNvramVal.compare("enabled") ==
+              constants::STR_CMP_SUCCESS)) ||
+            ((((*l_pVal).at(0) & 0x04) == 0x00) &&
+             (i_clearNvramVal.compare("disabled") ==
+              constants::STR_CMP_SUCCESS)))
+        {
+            // Don't update, values are same.
+            return;
+        }
+
+        types::BinaryVector l_valToUpdateInVpd;
+        if (i_clearNvramVal.compare("enabled") == constants::STR_CMP_SUCCESS)
+        {
+            // 3rd bit is used to store the value.
+            l_valToUpdateInVpd.emplace_back(
+                (*l_pVal).at(0) | constants::VALUE_4);
+        }
+        else
+        {
+            // 3rd bit is used to store the value.
+            l_valToUpdateInVpd.emplace_back(
+                (*l_pVal).at(0) & ~(constants::VALUE_4));
+        }
+
+        if (-1 ==
+            m_manager->updateKeyword(
+                SYSTEM_VPD_FILE_PATH,
+                types::IpzData("UTIL", constants::kwdClearNVRAM_CreateLPAR,
+                               l_valToUpdateInVpd)))
+        {
+            logging::logMessage(
+                "Failed to update " +
+                std::string(constants::kwdClearNVRAM_CreateLPAR) +
+                " keyword to VPD");
+        }
+
+        return;
+    }
+    logging::logMessage("Invalid type recieved for clear NVRAM from VPD.");
+}
+
+void IbmBiosHandler::saveClearNvramToBios(const std::string& i_clearNvramVal)
+{
+    // Check for the exact length as it is a string and it can have a garbage
+    // value.
+    if (i_clearNvramVal.size() != constants::VALUE_1)
+    {
+        logging::logMessage(
+            "Bad size for clear NVRAM in VPD. Skip writing to BIOS.");
+        return;
+    }
+
+    // 3rd bit is used to store clear NVRAM value.
+    std::string l_valtoUpdate =
+        (i_clearNvramVal.at(0) & constants::VALUE_4) ? "Enabled" : "Disabled";
+
+    types::PendingBIOSAttrs l_pendingBiosAttribute;
+    l_pendingBiosAttribute.push_back(std::make_pair(
+        "pvm_clear_nvram",
+        std::make_tuple(
+            "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+            l_valtoUpdate)));
+
+    try
+    {
+        dbusUtility::writeDbusProperty(
+            constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+            constants::biosConfigMgrInterface, "PendingAttributes",
+            l_pendingBiosAttribute);
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(
+            "DBus call to update NVRAM value in pending attribute failed. " +
+            std::string(l_ex.what()));
+    }
+}
+
+void IbmBiosHandler::processClearNvram()
+{
+    // Read required keyword from VPD.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        saveClearNvramToBios(std::to_string(l_pVal->at(0)));
+        return;
+    }
+    logging::logMessage("Invalid type recieved for clear NVRAM from VPD.");
+}
+
+void IbmBiosHandler::saveKeepAndClearToVpd(const std::string& i_KeepAndClearVal)
+{
+    if (i_KeepAndClearVal.empty())
+    {
+        logging::logMessage(
+            "Empty value received for keep and clear from BIOS. Skip updating to VPD.");
+        return;
+    }
+
+    // Read required keyword from DBus as we need to set only a Bit.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdKeepAndClear);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        commonUtility::toLower(const_cast<std::string&>(i_KeepAndClearVal));
+
+        // Check for first bit. Bit set for enabled else disabled.
+        if (((((*l_pVal).at(0) & 0x01) == 0x01) &&
+             (i_KeepAndClearVal.compare("enabled") ==
+              constants::STR_CMP_SUCCESS)) ||
+            ((((*l_pVal).at(0) & 0x01) == 0x00) &&
+             (i_KeepAndClearVal.compare("disabled") ==
+              constants::STR_CMP_SUCCESS)))
+        {
+            // Don't update, values are same.
+            return;
+        }
+
+        types::BinaryVector l_valToUpdateInVpd;
+        if (i_KeepAndClearVal.compare("enabled") == constants::STR_CMP_SUCCESS)
+        {
+            // 1st bit is used to store the value.
+            l_valToUpdateInVpd.emplace_back(
+                (*l_pVal).at(0) | constants::VALUE_1);
+        }
+        else
+        {
+            // 1st bit is used to store the value.
+            l_valToUpdateInVpd.emplace_back(
+                (*l_pVal).at(0) & ~(constants::VALUE_1));
+        }
+
+        if (-1 == m_manager->updateKeyword(
+                      SYSTEM_VPD_FILE_PATH,
+                      types::IpzData("UTIL", constants::kwdKeepAndClear,
+                                     l_valToUpdateInVpd)))
+        {
+            logging::logMessage(
+                "Failed to update " + std::string(constants::kwdKeepAndClear) +
+                " keyword to VPD");
+        }
+
+        return;
+    }
+    logging::logMessage("Invalid type recieved for keep and clear from VPD.");
+}
+
+void IbmBiosHandler::saveKeepAndClearToBios(
+    const std::string& i_KeepAndClearVal)
+{
+    // checking for exact length as it is a string and can have garbage value.
+    if (i_KeepAndClearVal.size() != constants::VALUE_1)
+    {
+        logging::logMessage(
+            "Bad size for keep and clear in VPD. Skip writing to BIOS.");
+        return;
+    }
+
+    // 1st bit is used to store keep and clear value.
+    std::string l_valtoUpdate =
+        (i_KeepAndClearVal.at(0) & constants::VALUE_1) ? "Enabled" : "Disabled";
+
+    types::PendingBIOSAttrs l_pendingBiosAttribute;
+    l_pendingBiosAttribute.push_back(std::make_pair(
+        "pvm_keep_and_clear",
+        std::make_tuple(
+            "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+            l_valtoUpdate)));
+
+    try
+    {
+        dbusUtility::writeDbusProperty(
+            constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+            constants::biosConfigMgrInterface, "PendingAttributes",
+            l_pendingBiosAttribute);
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(
+            "DBus call to update keep and clear value in pending attribute failed. " +
+            std::string(l_ex.what()));
+    }
+}
+
+void IbmBiosHandler::processKeepAndClear()
+{
+    // Read required keyword from VPD.
+    auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+        constants::pimServiceName, constants::systemVpdInvPath,
+        constants::utilInf, constants::kwdKeepAndClear);
+
+    if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+    {
+        saveKeepAndClearToBios(std::to_string(l_pVal->at(0)));
+        return;
+    }
+    logging::logMessage("Invalid type recieved for keep and clear from VPD.");
+}
+} // namespace vpd
diff --git a/vpd-manager/src/ddimm_parser.cpp b/vpd-manager/src/ddimm_parser.cpp
new file mode 100644
index 0000000..0da4ddc
--- /dev/null
+++ b/vpd-manager/src/ddimm_parser.cpp
@@ -0,0 +1,393 @@
+#include "ddimm_parser.hpp"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <cmath>
+#include <cstdint>
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace vpd
+{
+
+static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24;
+static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32;
+static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48;
+static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64;
+static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0;
+
+static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32;
+static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0;
+
+bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift,
+                                     uint8_t i_minValue, uint8_t i_maxValue)
+{
+    bool l_isValid = true;
+    uint8_t l_ByteValue = i_ByteValue >> i_shift;
+    if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue))
+    {
+        logging::logMessage(
+            "Non valid Value encountered value[" + std::to_string(l_ByteValue) +
+            "] range [" + std::to_string(i_minValue) + ".." +
+            std::to_string(i_maxValue) + "] found ");
+        return false;
+    }
+    return l_isValid;
+}
+
+uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue)
+{
+    uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
+    if (i_ByteValue < constants::VALUE_5)
+    {
+        l_densityPerDie = i_ByteValue * constants::VALUE_4;
+    }
+    else
+    {
+        switch (i_ByteValue)
+        {
+            case constants::VALUE_5:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB;
+                break;
+
+            case constants::VALUE_6:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB;
+                break;
+
+            case constants::VALUE_7:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB;
+                break;
+
+            case constants::VALUE_8:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB;
+                break;
+
+            default:
+                logging::logMessage(
+                    "default value encountered for density per die");
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
+                break;
+        }
+    }
+    return l_densityPerDie;
+}
+
+uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue)
+{
+    uint8_t l_DiePerPackage = constants::VALUE_0;
+    if (i_ByteValue < constants::VALUE_2)
+    {
+        l_DiePerPackage = i_ByteValue + constants::VALUE_1;
+    }
+    else
+    {
+        l_DiePerPackage =
+            pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1));
+    }
+    return l_DiePerPackage;
+}
+
+size_t DdimmVpdParser::getDdr5BasedDdimmSize(
+    types::BinaryVector::const_iterator i_iterator)
+{
+    size_t l_dimmSize = 0;
+
+    do
+    {
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+                                 constants::MASK_BYTE_BITS_01,
+                             constants::SHIFT_BITS_0, constants::VALUE_1,
+                             constants::VALUE_3) ||
+            !checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+                                 constants::MASK_BYTE_BITS_345,
+                             constants::SHIFT_BITS_3, constants::VALUE_1,
+                             constants::VALUE_3))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for channels per DIMM. DDIMM Byte "
+                "235 value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
+            break;
+        }
+        uint8_t l_channelsPerPhy =
+            (((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BITS_01)
+                  ? constants::VALUE_1
+                  : constants::VALUE_0) +
+             ((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BITS_345)
+                  ? constants::VALUE_1
+                  : constants::VALUE_0));
+
+        uint8_t l_channelsPerDdimm =
+            (((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BIT_6) >>
+              constants::VALUE_6) +
+             ((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BIT_7) >>
+              constants::VALUE_7)) *
+            l_channelsPerPhy;
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+                                 constants::MASK_BYTE_BITS_012,
+                             constants::SHIFT_BITS_0, constants::VALUE_1,
+                             constants::VALUE_3))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for bus width per channel. DDIMM "
+                "Byte 235 value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
+            break;
+        }
+        uint8_t l_busWidthPerChannel =
+            (i_iterator[constants::SPD_BYTE_235] &
+             constants::MASK_BYTE_BITS_012)
+                ? PRIMARY_BUS_WIDTH_32_BITS
+                : PRIMARY_BUS_WIDTH_UNUSED;
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
+                                 constants::MASK_BYTE_BITS_567,
+                             constants::SHIFT_BITS_5, constants::VALUE_0,
+                             constants::VALUE_5))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for die per package. DDIMM Byte 4 "
+                "value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
+            break;
+        }
+        uint8_t l_diePerPackage = getDdr5DiePerPackage(
+            (i_iterator[constants::SPD_BYTE_4] &
+             constants::MASK_BYTE_BITS_567) >>
+            constants::VALUE_5);
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
+                                 constants::MASK_BYTE_BITS_01234,
+                             constants::SHIFT_BITS_0, constants::VALUE_1,
+                             constants::VALUE_8))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for SDRAM Density per Die. DDIMM "
+                "Byte 4 value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
+            break;
+        }
+        uint8_t l_densityPerDie = getDdr5DensityPerDie(
+            i_iterator[constants::SPD_BYTE_4] &
+            constants::MASK_BYTE_BITS_01234);
+
+        uint8_t l_ranksPerChannel = 0;
+
+        if (((i_iterator[constants::SPD_BYTE_234] &
+              constants::MASK_BYTE_BIT_7) >>
+             constants::VALUE_7))
+        {
+            l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] &
+                                  constants::MASK_BYTE_BITS_345) >>
+                                 constants::VALUE_3) +
+                                constants::VALUE_1;
+        }
+        else if (((i_iterator[constants::SPD_BYTE_235] &
+                   constants::MASK_BYTE_BIT_6) >>
+                  constants::VALUE_6))
+        {
+            l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] &
+                                 constants::MASK_BYTE_BITS_012) +
+                                constants::VALUE_1;
+        }
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] &
+                                 constants::MASK_BYTE_BITS_567,
+                             constants::SHIFT_BITS_5, constants::VALUE_0,
+                             constants::VALUE_3))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for dram width DDIMM Byte 6 value "
+                "[" +
+                std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]");
+            break;
+        }
+        uint8_t l_dramWidth =
+            constants::VALUE_4 *
+            (constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] &
+                                     constants::MASK_BYTE_BITS_567) >>
+                                    constants::VALUE_5));
+
+        // DDIMM size is calculated in GB
+        l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel *
+                      l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
+                     (8 * l_dramWidth);
+
+    } while (false);
+
+    return constants::CONVERT_GB_TO_KB * l_dimmSize;
+}
+
+size_t DdimmVpdParser::getDdr4BasedDdimmSize(
+    types::BinaryVector::const_iterator i_iterator)
+{
+    size_t l_dimmSize = 0;
+    try
+    {
+        uint8_t l_tmpValue = 0;
+
+        // Calculate SDRAM capacity
+        l_tmpValue = i_iterator[constants::SPD_BYTE_4] &
+                     constants::JEDEC_SDRAM_CAP_MASK;
+
+        /* Make sure the bits are not Reserved */
+        if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 4. Can't calculate SDRAM capacity and so "
+                "dimm size.\n ");
+        }
+
+        uint16_t l_sdramCapacity = 1;
+        l_sdramCapacity = (l_sdramCapacity << l_tmpValue) *
+                          constants::JEDEC_SDRAMCAP_MULTIPLIER;
+
+        /* Calculate Primary bus width */
+        l_tmpValue = i_iterator[constants::SPD_BYTE_13] &
+                     constants::JEDEC_PRI_BUS_WIDTH_MASK;
+
+        if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 13. Can't calculate primary bus width "
+                "and so dimm size.");
+        }
+
+        uint8_t l_primaryBusWid = 1;
+        l_primaryBusWid = (l_primaryBusWid << l_tmpValue) *
+                          constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
+
+        /* Calculate SDRAM width */
+        l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
+                     constants::JEDEC_SDRAM_WIDTH_MASK;
+
+        if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 12. Can't calculate SDRAM width and so "
+                "dimm size.");
+        }
+
+        uint8_t l_sdramWidth = 1;
+        l_sdramWidth = (l_sdramWidth << l_tmpValue) *
+                       constants::JEDEC_SDRAM_WIDTH_MULTIPLIER;
+
+        /* Calculate Number of ranks */
+        l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
+                     constants::JEDEC_NUM_RANKS_MASK;
+        l_tmpValue >>= constants::JEDEC_RESERVED_BITS;
+
+        if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found.");
+        }
+
+        uint8_t l_logicalRanksPerDimm = l_tmpValue + 1;
+
+        // Determine is single load stack (3DS) or not
+        l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
+                     constants::JEDEC_SIGNAL_LOADING_MASK;
+
+        if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK)
+        {
+            // Fetch die count
+            l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
+                         constants::JEDEC_DIE_COUNT_MASK;
+            l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT;
+
+            uint8_t l_dieCount = l_tmpValue + 1;
+            l_logicalRanksPerDimm *= l_dieCount;
+        }
+
+        l_dimmSize =
+            (l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
+            (l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm;
+
+        // Converting dimm size from MB to KB
+        l_dimmSize *= constants::CONVERT_MB_TO_KB;
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO:: Need an error log here
+        logging::logMessage("DDR4 DDIMM calculation is failed, reason: " +
+                            std::string(l_ex.what()));
+    }
+    return l_dimmSize;
+}
+
+size_t
+    DdimmVpdParser::getDdimmSize(types::BinaryVector::const_iterator i_iterator)
+{
+    size_t l_dimmSize = 0;
+    if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5)
+    {
+        l_dimmSize = getDdr5BasedDdimmSize(i_iterator);
+    }
+    else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4)
+    {
+        l_dimmSize = getDdr4BasedDdimmSize(i_iterator);
+    }
+    else
+    {
+        logging::logMessage(
+            "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" +
+            std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]");
+    }
+    return l_dimmSize;
+}
+
+void DdimmVpdParser::readKeywords(
+    types::BinaryVector::const_iterator i_iterator)
+{
+    // collect DDIMM size value
+    auto l_dimmSize = getDdimmSize(i_iterator);
+    if (!l_dimmSize)
+    {
+        throw(DataException("Error: Calculated dimm size is 0."));
+    }
+
+    m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize);
+    // point the i_iterator to DIMM data and skip "11S"
+    advance(i_iterator, constants::DDIMM_11S_BARCODE_START +
+                            constants::DDIMM_11S_FORMAT_LEN);
+    types::BinaryVector l_partNumber(i_iterator,
+                                     i_iterator + constants::PART_NUM_LEN);
+
+    advance(i_iterator, constants::PART_NUM_LEN);
+    types::BinaryVector l_serialNumber(i_iterator,
+                                       i_iterator + constants::SERIAL_NUM_LEN);
+
+    advance(i_iterator, constants::SERIAL_NUM_LEN);
+    types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN);
+
+    m_parsedVpdMap.emplace("FN", l_partNumber);
+    m_parsedVpdMap.emplace("PN", move(l_partNumber));
+    m_parsedVpdMap.emplace("SN", move(l_serialNumber));
+    m_parsedVpdMap.emplace("CC", move(l_ccin));
+}
+
+types::VPDMapVariant DdimmVpdParser::parse()
+{
+    try
+    {
+        // Read the data and return the map
+        auto l_iterator = m_vpdVector.cbegin();
+        readKeywords(l_iterator);
+        return m_parsedVpdMap;
+    }
+    catch (const std::exception& exp)
+    {
+        logging::logMessage(exp.what());
+        throw exp;
+    }
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/event_logger.cpp b/vpd-manager/src/event_logger.cpp
new file mode 100644
index 0000000..11861d1
--- /dev/null
+++ b/vpd-manager/src/event_logger.cpp
@@ -0,0 +1,294 @@
+#include "event_logger.hpp"
+
+#include "logger.hpp"
+
+#include <systemd/sd-bus.h>
+
+namespace vpd
+{
+const std::unordered_map<types::SeverityType, std::string>
+    EventLogger::m_severityMap = {
+        {types::SeverityType::Notice,
+         "xyz.openbmc_project.Logging.Entry.Level.Notice"},
+        {types::SeverityType::Informational,
+         "xyz.openbmc_project.Logging.Entry.Level.Informational"},
+        {types::SeverityType::Debug,
+         "xyz.openbmc_project.Logging.Entry.Level.Debug"},
+        {types::SeverityType::Warning,
+         "xyz.openbmc_project.Logging.Entry.Level.Warning"},
+        {types::SeverityType::Critical,
+         "xyz.openbmc_project.Logging.Entry.Level.Critical"},
+        {types::SeverityType::Emergency,
+         "xyz.openbmc_project.Logging.Entry.Level.Emergency"},
+        {types::SeverityType::Alert,
+         "xyz.openbmc_project.Logging.Entry.Level.Alert"},
+        {types::SeverityType::Error,
+         "xyz.openbmc_project.Logging.Entry.Level.Error"}};
+
+const std::unordered_map<types::ErrorType, std::string>
+    EventLogger::m_errorMsgMap = {
+        {types::ErrorType::DefaultValue, "com.ibm.VPD.Error.DefaultValue"},
+        {types::ErrorType::InvalidVpdMessage, "com.ibm.VPD.Error.InvalidVPD"},
+        {types::ErrorType::VpdMismatch, "com.ibm.VPD.Error.Mismatch"},
+        {types::ErrorType::InvalidEeprom,
+         "com.ibm.VPD.Error.InvalidEepromPath"},
+        {types::ErrorType::EccCheckFailed, "com.ibm.VPD.Error.EccCheckFailed"},
+        {types::ErrorType::JsonFailure, "com.ibm.VPD.Error.InvalidJson"},
+        {types::ErrorType::DbusFailure, "com.ibm.VPD.Error.DbusFailure"},
+        {types::ErrorType::InvalidSystem,
+         "com.ibm.VPD.Error.UnknownSystemType"},
+        {types::ErrorType::EssentialFru,
+         "com.ibm.VPD.Error.RequiredFRUMissing"},
+        {types::ErrorType::GpioError, "com.ibm.VPD.Error.GPIOError"}};
+
+const std::unordered_map<types::CalloutPriority, std::string>
+    EventLogger::m_priorityMap = {
+        {types::CalloutPriority::High, "H"},
+        {types::CalloutPriority::Medium, "M"},
+        {types::CalloutPriority::MediumGroupA, "A"},
+        {types::CalloutPriority::MediumGroupB, "B"},
+        {types::CalloutPriority::MediumGroupC, "C"},
+        {types::CalloutPriority::Low, "L"}};
+
+void EventLogger::createAsyncPelWithInventoryCallout(
+    const types::ErrorType& i_errorType, const types::SeverityType& i_severity,
+    const std::vector<types::InventoryCalloutData>& i_callouts,
+    const std::string& i_fileName, const std::string& i_funcName,
+    const uint8_t i_internalRc, const std::string& i_description,
+    const std::optional<std::string> i_userData1,
+    const std::optional<std::string> i_userData2,
+    const std::optional<std::string> i_symFru,
+    const std::optional<std::string> i_procedure)
+{
+    (void)i_symFru;
+    (void)i_procedure;
+
+    try
+    {
+        if (i_callouts.empty())
+        {
+            logging::logMessage("Callout information is missing to create PEL");
+            // TODO: Revisit this instead of simpley returning.
+            return;
+        }
+
+        if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end())
+        {
+            throw std::runtime_error(
+                "Error type not found in the error message map to create PEL");
+            // TODO: Need to handle, instead of throwing exception. Create
+            // default message in message_registry.json.
+        }
+
+        const std::string& l_message = m_errorMsgMap.at(i_errorType);
+
+        const std::string& l_severity =
+            (m_severityMap.find(i_severity) != m_severityMap.end()
+                 ? m_severityMap.at(i_severity)
+                 : m_severityMap.at(types::SeverityType::Informational));
+
+        std::string l_description =
+            (!i_description.empty() ? i_description : "VPD generic error");
+
+        std::string l_userData1 = (i_userData1) ? (*i_userData1) : "";
+
+        std::string l_userData2 = (i_userData2) ? (*i_userData2) : "";
+
+        const types::InventoryCalloutData& l_invCallout = i_callouts[0];
+        // TODO: Need to handle multiple inventory path callout's, when multiple
+        // callout's is supported by "Logging" service.
+
+        const types::CalloutPriority& l_priorityEnum = get<1>(l_invCallout);
+
+        const std::string& l_priority =
+            (m_priorityMap.find(l_priorityEnum) != m_priorityMap.end()
+                 ? m_priorityMap.at(l_priorityEnum)
+                 : m_priorityMap.at(types::CalloutPriority::Low));
+
+        sd_bus* l_sdBus = nullptr;
+        sd_bus_default(&l_sdBus);
+
+        const uint8_t l_additionalDataCount = 8;
+        auto l_rc = sd_bus_call_method_async(
+            l_sdBus, NULL, constants::eventLoggingServiceName,
+            constants::eventLoggingObjectPath, constants::eventLoggingInterface,
+            "Create", NULL, NULL, "ssa{ss}", l_message.c_str(),
+            l_severity.c_str(), l_additionalDataCount, "FileName",
+            i_fileName.c_str(), "FunctionName", i_funcName.c_str(),
+            "InternalRc", std::to_string(i_internalRc).c_str(), "DESCRIPTION",
+            l_description.c_str(), "UserData1", l_userData1.c_str(),
+            "UserData2", l_userData2.c_str(), "CALLOUT_INVENTORY_PATH",
+            get<0>(l_invCallout).c_str(), "CALLOUT_PRIORITY",
+            l_priority.c_str());
+
+        if (l_rc < 0)
+        {
+            logging::logMessage(
+                "Error calling sd_bus_call_method_async, Message = " +
+                std::string(strerror(-l_rc)));
+        }
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(
+            "Create PEL failed with error: " + std::string(l_ex.what()));
+    }
+}
+
+void EventLogger::createAsyncPelWithI2cDeviceCallout(
+    const types::ErrorType i_errorType, const types::SeverityType i_severity,
+    const std::vector<types::DeviceCalloutData>& i_callouts,
+    const std::string& i_fileName, const std::string& i_funcName,
+    const uint8_t i_internalRc,
+    const std::optional<std::pair<std::string, std::string>> i_userData1,
+    const std::optional<std::pair<std::string, std::string>> i_userData2)
+{
+    // TODO, implementation needs to be added.
+    (void)i_errorType;
+    (void)i_severity;
+    (void)i_callouts;
+    (void)i_fileName;
+    (void)i_funcName;
+    (void)i_internalRc;
+    (void)i_userData1;
+    (void)i_userData2;
+}
+
+void EventLogger::createAsyncPelWithI2cBusCallout(
+    const types::ErrorType i_errorType, const types::SeverityType i_severity,
+    const std::vector<types::I2cBusCalloutData>& i_callouts,
+    const std::string& i_fileName, const std::string& i_funcName,
+    const uint8_t i_internalRc,
+    const std::optional<std::pair<std::string, std::string>> i_userData1,
+    const std::optional<std::pair<std::string, std::string>> i_userData2)
+{
+    // TODO, implementation needs to be added.
+    (void)i_errorType;
+    (void)i_severity;
+    (void)i_callouts;
+    (void)i_fileName;
+    (void)i_funcName;
+    (void)i_internalRc;
+    (void)i_userData1;
+    (void)i_userData2;
+}
+
+void EventLogger::createAsyncPel(
+    const types::ErrorType& i_errorType, const types::SeverityType& i_severity,
+    const std::string& i_fileName, const std::string& i_funcName,
+    const uint8_t i_internalRc, const std::string& i_description,
+    const std::optional<std::string> i_userData1,
+    const std::optional<std::string> i_userData2,
+    const std::optional<std::string> i_symFru,
+    const std::optional<std::string> i_procedure)
+{
+    (void)i_symFru;
+    (void)i_procedure;
+    try
+    {
+        if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end())
+        {
+            throw std::runtime_error("Unsupported error type received");
+            // TODO: Need to handle, instead of throwing an exception.
+        }
+
+        const std::string& l_message = m_errorMsgMap.at(i_errorType);
+
+        const std::string& l_severity =
+            (m_severityMap.find(i_severity) != m_severityMap.end()
+                 ? m_severityMap.at(i_severity)
+                 : m_severityMap.at(types::SeverityType::Informational));
+
+        const std::string l_description =
+            ((!i_description.empty() ? i_description : "VPD generic error"));
+
+        const std::string l_userData1 = ((i_userData1) ? (*i_userData1) : "");
+
+        const std::string l_userData2 = ((i_userData2) ? (*i_userData2) : "");
+
+        sd_bus* l_sdBus = nullptr;
+        sd_bus_default(&l_sdBus);
+
+        // VALUE_6 represents the additional data pair count passing to create
+        // PEL. If there any change in additional data, we need to pass the
+        // correct number.
+        auto l_rc = sd_bus_call_method_async(
+            l_sdBus, NULL, constants::eventLoggingServiceName,
+            constants::eventLoggingObjectPath, constants::eventLoggingInterface,
+            "Create", NULL, NULL, "ssa{ss}", l_message.c_str(),
+            l_severity.c_str(), constants::VALUE_6, "FileName",
+            i_fileName.c_str(), "FunctionName", i_funcName.c_str(),
+            "InternalRc", std::to_string(i_internalRc).c_str(), "DESCRIPTION",
+            l_description.c_str(), "UserData1", l_userData1.c_str(),
+            "UserData2", l_userData2.c_str());
+
+        if (l_rc < 0)
+        {
+            logging::logMessage(
+                "Error calling sd_bus_call_method_async, Message = " +
+                std::string(strerror(-l_rc)));
+        }
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        logging::logMessage("Async PEL creation failed with an error: " +
+                            std::string(l_ex.what()));
+    }
+}
+
+void EventLogger::createSyncPel(
+    const types::ErrorType& i_errorType, const types::SeverityType& i_severity,
+    const std::string& i_fileName, const std::string& i_funcName,
+    const uint8_t i_internalRc, const std::string& i_description,
+    const std::optional<std::string> i_userData1,
+    const std::optional<std::string> i_userData2,
+    const std::optional<std::string> i_symFru,
+    const std::optional<std::string> i_procedure)
+{
+    (void)i_symFru;
+    (void)i_procedure;
+    try
+    {
+        if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end())
+        {
+            throw std::runtime_error("Unsupported error type received");
+            // TODO: Need to handle, instead of throwing an exception.
+        }
+
+        const std::string& l_message = m_errorMsgMap.at(i_errorType);
+
+        const std::string& l_severity =
+            (m_severityMap.find(i_severity) != m_severityMap.end()
+                 ? m_severityMap.at(i_severity)
+                 : m_severityMap.at(types::SeverityType::Informational));
+
+        const std::string l_description =
+            ((!i_description.empty() ? i_description : "VPD generic error"));
+
+        const std::string l_userData1 = ((i_userData1) ? (*i_userData1) : "");
+
+        const std::string l_userData2 = ((i_userData2) ? (*i_userData2) : "");
+
+        std::map<std::string, std::string> l_additionalData{
+            {"FileName", i_fileName},
+            {"FunctionName", i_funcName},
+            {"DESCRIPTION", l_description},
+            {"InteranlRc", std::to_string(i_internalRc)},
+            {"UserData1", l_userData1.c_str()},
+            {"UserData2", l_userData2.c_str()}};
+
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method =
+            l_bus.new_method_call(constants::eventLoggingServiceName,
+                                  constants::eventLoggingObjectPath,
+                                  constants::eventLoggingInterface, "Create");
+        l_method.append(l_message, l_severity, l_additionalData);
+        l_bus.call(l_method);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        logging::logMessage("Sync PEL creation failed with an error: " +
+                            std::string(l_ex.what()));
+    }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/gpio_monitor.cpp b/vpd-manager/src/gpio_monitor.cpp
new file mode 100644
index 0000000..521037c
--- /dev/null
+++ b/vpd-manager/src/gpio_monitor.cpp
@@ -0,0 +1,131 @@
+#include "gpio_monitor.hpp"
+
+#include "constants.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+#include "utility/dbus_utility.hpp"
+#include "utility/json_utility.hpp"
+
+#include <boost/asio.hpp>
+#include <boost/bind/bind.hpp>
+#include <gpiod.hpp>
+
+namespace vpd
+{
+void GpioEventHandler::handleChangeInGpioPin(const bool& i_isFruPresent)
+{
+    try
+    {
+        if (i_isFruPresent)
+        {
+            types::VPDMapVariant l_parsedVpd =
+                m_worker->parseVpdFile(m_fruPath);
+
+            if (std::holds_alternative<std::monostate>(l_parsedVpd))
+            {
+                throw std::runtime_error(
+                    "VPD parsing failed for " + std::string(m_fruPath));
+            }
+
+            types::ObjectMap l_dbusObjectMap;
+            m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, m_fruPath);
+
+            if (l_dbusObjectMap.empty())
+            {
+                throw std::runtime_error("Failed to create D-bus object map.");
+            }
+
+            if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
+            {
+                throw std::runtime_error("call PIM failed");
+            }
+        }
+        else
+        {
+            // TODO -- Add implementation to Delete FRU if FRU is not present.
+        }
+    }
+    catch (std::exception& l_ex)
+    {
+        logging::logMessage(std::string(l_ex.what()));
+    }
+}
+
+void GpioEventHandler::handleTimerExpiry(
+    const boost::system::error_code& i_errorCode,
+    const std::shared_ptr<boost::asio::steady_timer>& i_timerObj)
+{
+    if (i_errorCode == boost::asio::error::operation_aborted)
+    {
+        logging::logMessage("Timer aborted for GPIO pin");
+        return;
+    }
+
+    if (i_errorCode)
+    {
+        logging::logMessage("Timer wait failed for gpio pin" +
+                            std::string(i_errorCode.message()));
+        return;
+    }
+
+    bool l_currentPresencePinValue = jsonUtility::processGpioPresenceTag(
+        m_worker->getSysCfgJsonObj(), m_fruPath, "pollingRequired",
+        "hotPlugging");
+
+    if (m_prevPresencePinValue != l_currentPresencePinValue)
+    {
+        m_prevPresencePinValue = l_currentPresencePinValue;
+        handleChangeInGpioPin(l_currentPresencePinValue);
+    }
+
+    i_timerObj->expires_at(std::chrono::steady_clock::now() +
+                           std::chrono::seconds(constants::VALUE_5));
+    i_timerObj->async_wait(
+        boost::bind(&GpioEventHandler::handleTimerExpiry, this,
+                    boost::asio::placeholders::error, i_timerObj));
+}
+
+void GpioEventHandler::setEventHandlerForGpioPresence(
+    const std::shared_ptr<boost::asio::io_context>& i_ioContext)
+{
+    m_prevPresencePinValue = jsonUtility::processGpioPresenceTag(
+        m_worker->getSysCfgJsonObj(), m_fruPath, "pollingRequired",
+        "hotPlugging");
+
+    static std::vector<std::shared_ptr<boost::asio::steady_timer>> l_timers;
+
+    auto l_timerObj = make_shared<boost::asio::steady_timer>(
+        *i_ioContext, std::chrono::seconds(constants::VALUE_5));
+
+    l_timerObj->async_wait(
+        boost::bind(&GpioEventHandler::handleTimerExpiry, this,
+                    boost::asio::placeholders::error, l_timerObj));
+
+    l_timers.push_back(l_timerObj);
+}
+
+void GpioMonitor::initHandlerForGpio(
+    const std::shared_ptr<boost::asio::io_context>& i_ioContext,
+    const std::shared_ptr<Worker>& i_worker)
+{
+    try
+    {
+        std::vector<std::string> l_gpioPollingRequiredFrusList =
+            jsonUtility::getListOfGpioPollingFrus(m_sysCfgJsonObj);
+
+        for (const auto& l_fruPath : l_gpioPollingRequiredFrusList)
+        {
+            std::shared_ptr<GpioEventHandler> l_gpioEventHandlerObj =
+                std::make_shared<GpioEventHandler>(l_fruPath, i_worker,
+                                                   i_ioContext);
+
+            m_gpioEventHandlerObjects.push_back(l_gpioEventHandlerObj);
+        }
+    }
+    catch (std::exception& l_ex)
+    {
+        // TODO log PEL for exception.
+        logging::logMessage(l_ex.what());
+    }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/ipz_parser.cpp b/vpd-manager/src/ipz_parser.cpp
new file mode 100644
index 0000000..4300e12
--- /dev/null
+++ b/vpd-manager/src/ipz_parser.cpp
@@ -0,0 +1,842 @@
+#include "config.h"
+
+#include "ipz_parser.hpp"
+
+#include "vpdecc/vpdecc.h"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <typeindex>
+
+namespace vpd
+{
+
+// Offset of different entries in VPD data.
+enum Offset
+{
+    VHDR = 17,
+    VHDR_TOC_ENTRY = 29,
+    VTOC_PTR = 35,
+    VTOC_REC_LEN = 37,
+    VTOC_ECC_OFF = 39,
+    VTOC_ECC_LEN = 41,
+    VTOC_DATA = 13,
+    VHDR_ECC = 0,
+    VHDR_RECORD = 11
+};
+
+// Length of some specific entries w.r.t VPD data.
+enum Length
+{
+    RECORD_NAME = 4,
+    KW_NAME = 2,
+    RECORD_OFFSET = 2,
+    RECORD_MIN = 44,
+    RECORD_LENGTH = 2,
+    RECORD_ECC_OFFSET = 2,
+    VHDR_ECC_LENGTH = 11,
+    VHDR_RECORD_LENGTH = 44,
+    RECORD_TYPE = 2,
+    SKIP_A_RECORD_IN_PT = 14,
+    JUMP_TO_RECORD_NAME = 6
+}; // enum Length
+
+/**
+ * @brief API to read 2 bytes LE data.
+ *
+ * @param[in] iterator - iterator to VPD vector.
+ * @return read bytes.
+ */
+static uint16_t readUInt16LE(types::BinaryVector::const_iterator iterator)
+{
+    uint16_t lowByte = *iterator;
+    uint16_t highByte = *(iterator + 1);
+    lowByte |= (highByte << 8);
+    return lowByte;
+}
+
+bool IpzVpdParser::vhdrEccCheck()
+{
+    auto vpdPtr = m_vpdVector.cbegin();
+
+    auto l_status = vpdecc_check_data(
+        const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_RECORD]),
+        Length::VHDR_RECORD_LENGTH,
+        const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_ECC]),
+        Length::VHDR_ECC_LENGTH);
+    if (l_status == VPD_ECC_CORRECTABLE_DATA)
+    {
+        try
+        {
+            if (m_vpdFileStream.is_open())
+            {
+                m_vpdFileStream.seekp(m_vpdStartOffset + Offset::VHDR_RECORD,
+                                      std::ios::beg);
+                m_vpdFileStream.write(reinterpret_cast<const char*>(
+                                          &m_vpdVector[Offset::VHDR_RECORD]),
+                                      Length::VHDR_RECORD_LENGTH);
+            }
+            else
+            {
+                logging::logMessage("File not open");
+                return false;
+            }
+        }
+        catch (const std::fstream::failure& e)
+        {
+            logging::logMessage(
+                "Error while operating on file with exception: " +
+                std::string(e.what()));
+            return false;
+        }
+    }
+    else if (l_status != VPD_ECC_OK)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool IpzVpdParser::vtocEccCheck()
+{
+    auto vpdPtr = m_vpdVector.cbegin();
+
+    std::advance(vpdPtr, Offset::VTOC_PTR);
+
+    // The offset to VTOC could be 1 or 2 bytes long
+    auto vtocOffset = readUInt16LE(vpdPtr);
+
+    // Get the VTOC Length
+    std::advance(vpdPtr, sizeof(types::RecordOffset));
+    auto vtocLength = readUInt16LE(vpdPtr);
+
+    // Get the ECC Offset
+    std::advance(vpdPtr, sizeof(types::RecordLength));
+    auto vtocECCOffset = readUInt16LE(vpdPtr);
+
+    // Get the ECC length
+    std::advance(vpdPtr, sizeof(types::ECCOffset));
+    auto vtocECCLength = readUInt16LE(vpdPtr);
+
+    // Reset pointer to start of the vpd,
+    // so that Offset will point to correct address
+    vpdPtr = m_vpdVector.cbegin();
+    auto l_status = vpdecc_check_data(
+        const_cast<uint8_t*>(&m_vpdVector[vtocOffset]), vtocLength,
+        const_cast<uint8_t*>(&m_vpdVector[vtocECCOffset]), vtocECCLength);
+    if (l_status == VPD_ECC_CORRECTABLE_DATA)
+    {
+        try
+        {
+            if (m_vpdFileStream.is_open())
+            {
+                m_vpdFileStream.seekp(m_vpdStartOffset + vtocOffset,
+                                      std::ios::beg);
+                m_vpdFileStream.write(
+                    reinterpret_cast<const char*>(&m_vpdVector[vtocOffset]),
+                    vtocLength);
+            }
+            else
+            {
+                logging::logMessage("File not open");
+                return false;
+            }
+        }
+        catch (const std::fstream::failure& e)
+        {
+            logging::logMessage(
+                "Error while operating on file with exception " +
+                std::string(e.what()));
+            return false;
+        }
+    }
+    else if (l_status != VPD_ECC_OK)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool IpzVpdParser::recordEccCheck(types::BinaryVector::const_iterator iterator)
+{
+    auto recordOffset = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(types::RecordOffset));
+    auto recordLength = readUInt16LE(iterator);
+
+    if (recordOffset == 0 || recordLength == 0)
+    {
+        throw(DataException("Invalid record offset or length"));
+    }
+
+    std::advance(iterator, sizeof(types::RecordLength));
+    auto eccOffset = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(types::ECCOffset));
+    auto eccLength = readUInt16LE(iterator);
+
+    if (eccLength == 0 || eccOffset == 0)
+    {
+        throw(EccException("Invalid ECC length or offset."));
+    }
+
+    auto vpdPtr = m_vpdVector.cbegin();
+
+    if (vpdecc_check_data(
+            const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
+            const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength) == VPD_ECC_OK)
+    {
+        return true;
+    }
+
+    return false;
+}
+
+void IpzVpdParser::checkHeader(types::BinaryVector::const_iterator itrToVPD)
+{
+    if (m_vpdVector.empty() || (Length::RECORD_MIN > m_vpdVector.size()))
+    {
+        throw(DataException("Malformed VPD"));
+    }
+
+    std::advance(itrToVPD, Offset::VHDR);
+    auto stop = std::next(itrToVPD, Length::RECORD_NAME);
+
+    std::string record(itrToVPD, stop);
+    if ("VHDR" != record)
+    {
+        throw(DataException("VHDR record not found"));
+    }
+
+    if (!vhdrEccCheck())
+    {
+        throw(EccException("ERROR: VHDR ECC check Failed"));
+    }
+}
+
+auto IpzVpdParser::readTOC(types::BinaryVector::const_iterator& itrToVPD)
+{
+    // The offset to VTOC could be 1 or 2 bytes long
+    uint16_t vtocOffset =
+        readUInt16LE((itrToVPD + Offset::VTOC_PTR)); // itrToVPD);
+
+    // Got the offset to VTOC, skip past record header and keyword header
+    // to get to the record name.
+    std::advance(itrToVPD, vtocOffset + sizeof(types::RecordId) +
+                               sizeof(types::RecordSize) +
+                               // Skip past the RT keyword, which contains
+                               // the record name.
+                               Length::KW_NAME + sizeof(types::KwSize));
+
+    std::string record(itrToVPD, std::next(itrToVPD, Length::RECORD_NAME));
+    if ("VTOC" != record)
+    {
+        throw(DataException("VTOC record not found"));
+    }
+
+    if (!vtocEccCheck())
+    {
+        throw(EccException("ERROR: VTOC ECC check Failed"));
+    }
+
+    // VTOC record name is good, now read through the TOC, stored in the PT
+    // PT keyword; vpdBuffer is now pointing at the first character of the
+    // name 'VTOC', jump to PT data.
+    // Skip past record name and KW name, 'PT'
+    std::advance(itrToVPD, Length::RECORD_NAME + Length::KW_NAME);
+
+    // Note size of PT
+    auto ptLen = *itrToVPD;
+
+    // Skip past PT size
+    std::advance(itrToVPD, sizeof(types::KwSize));
+
+    // length of PT keyword
+    return ptLen;
+}
+
+types::RecordOffsetList IpzVpdParser::readPT(
+    types::BinaryVector::const_iterator& itrToPT, auto ptLength)
+{
+    types::RecordOffsetList recordOffsets;
+
+    auto end = itrToPT;
+    std::advance(end, ptLength);
+
+    // Look at each entry in the PT keyword. In the entry,
+    // we care only about the record offset information.
+    while (itrToPT < end)
+    {
+        std::string recordName(itrToPT, itrToPT + Length::RECORD_NAME);
+        // Skip record name and record type
+        std::advance(itrToPT, Length::RECORD_NAME + sizeof(types::RecordType));
+
+        // Get record offset
+        recordOffsets.push_back(readUInt16LE(itrToPT));
+        try
+        {
+            // Verify the ECC for this Record
+            if (!recordEccCheck(itrToPT))
+            {
+                throw(EccException("ERROR: ECC check failed"));
+            }
+        }
+        catch (const EccException& ex)
+        {
+            logging::logMessage(ex.what());
+
+            /*TODO: uncomment when PEL code goes in */
+
+            /*std::string errMsg =
+                std::string{ex.what()} + " Record: " + recordName;
+
+            inventory::PelAdditionalData additionalData{};
+            additionalData.emplace("DESCRIPTION", errMsg);
+            additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+            createPEL(additionalData, PelSeverity::WARNING,
+                      errIntfForEccCheckFail, nullptr);*/
+        }
+        catch (const DataException& ex)
+        {
+            logging::logMessage(ex.what());
+
+            /*TODO: uncomment when PEL code goes in */
+
+            /*std::string errMsg =
+                std::string{ex.what()} + " Record: " + recordName;
+
+            inventory::PelAdditionalData additionalData{};
+            additionalData.emplace("DESCRIPTION", errMsg);
+            additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+            createPEL(additionalData, PelSeverity::WARNING,
+                      errIntfForInvalidVPD, nullptr);*/
+        }
+
+        // Jump record size, record length, ECC offset and ECC length
+        std::advance(itrToPT,
+                     sizeof(types::RecordOffset) + sizeof(types::RecordLength) +
+                         sizeof(types::ECCOffset) + sizeof(types::ECCLength));
+    }
+
+    return recordOffsets;
+}
+
+types::IPZVpdMap::mapped_type
+    IpzVpdParser::readKeywords(types::BinaryVector::const_iterator& itrToKwds)
+{
+    types::IPZVpdMap::mapped_type kwdValueMap{};
+    while (true)
+    {
+        // Note keyword name
+        std::string kwdName(itrToKwds, itrToKwds + Length::KW_NAME);
+        if (constants::LAST_KW == kwdName)
+        {
+            // We're done
+            break;
+        }
+        // Check if the Keyword is '#kw'
+        char kwNameStart = *itrToKwds;
+
+        // Jump past keyword name
+        std::advance(itrToKwds, Length::KW_NAME);
+
+        std::size_t kwdDataLength;
+        std::size_t lengthHighByte;
+
+        if (constants::POUND_KW == kwNameStart)
+        {
+            // Note keyword data length
+            kwdDataLength = *itrToKwds;
+            lengthHighByte = *(itrToKwds + 1);
+            kwdDataLength |= (lengthHighByte << 8);
+
+            // Jump past 2Byte keyword length
+            std::advance(itrToKwds, sizeof(types::PoundKwSize));
+        }
+        else
+        {
+            // Note keyword data length
+            kwdDataLength = *itrToKwds;
+
+            // Jump past keyword length
+            std::advance(itrToKwds, sizeof(types::KwSize));
+        }
+
+        // support all the Keywords
+        auto stop = std::next(itrToKwds, kwdDataLength);
+        std::string kwdata(itrToKwds, stop);
+        kwdValueMap.emplace(std::move(kwdName), std::move(kwdata));
+
+        // Jump past keyword data length
+        std::advance(itrToKwds, kwdDataLength);
+    }
+
+    return kwdValueMap;
+}
+
+void IpzVpdParser::processRecord(auto recordOffset)
+{
+    // Jump to record name
+    auto recordNameOffset =
+        recordOffset + sizeof(types::RecordId) + sizeof(types::RecordSize) +
+        // Skip past the RT keyword, which contains
+        // the record name.
+        Length::KW_NAME + sizeof(types::KwSize);
+
+    // Get record name
+    auto itrToVPDStart = m_vpdVector.cbegin();
+    std::advance(itrToVPDStart, recordNameOffset);
+
+    std::string recordName(itrToVPDStart, itrToVPDStart + Length::RECORD_NAME);
+
+    // proceed to find contained keywords and their values.
+    std::advance(itrToVPDStart, Length::RECORD_NAME);
+
+    // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
+    std::advance(itrToVPDStart, -(Length::KW_NAME + sizeof(types::KwSize) +
+                                  Length::RECORD_NAME));
+
+    // Add entry for this record (and contained keyword:value pairs)
+    // to the parsed vpd output.
+    m_parsedVPDMap.emplace(std::move(recordName),
+                           std::move(readKeywords(itrToVPDStart)));
+}
+
+types::VPDMapVariant IpzVpdParser::parse()
+{
+    try
+    {
+        auto itrToVPD = m_vpdVector.cbegin();
+
+        // Check vaidity of VHDR record
+        checkHeader(itrToVPD);
+
+        // Read the table of contents
+        auto ptLen = readTOC(itrToVPD);
+
+        // Read the table of contents record, to get offsets
+        // to other records.
+        auto recordOffsets = readPT(itrToVPD, ptLen);
+        for (const auto& offset : recordOffsets)
+        {
+            processRecord(offset);
+        }
+
+        return m_parsedVPDMap;
+    }
+    catch (const std::exception& e)
+    {
+        logging::logMessage(e.what());
+        throw e;
+    }
+}
+
+types::BinaryVector IpzVpdParser::getKeywordValueFromRecord(
+    const types::Record& i_recordName, const types::Keyword& i_keywordName,
+    const types::RecordOffset& i_recordDataOffset)
+{
+    auto l_iterator = m_vpdVector.cbegin();
+
+    // Go to the record name in the given record's offset
+    std::ranges::advance(l_iterator,
+                         i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+                         m_vpdVector.cend());
+
+    // Check if the record is present in the given record's offset
+    if (i_recordName !=
+        std::string(l_iterator,
+                    std::ranges::next(l_iterator, Length::RECORD_NAME,
+                                      m_vpdVector.cend())))
+    {
+        throw std::runtime_error(
+            "Given record is not present in the offset provided");
+    }
+
+    std::ranges::advance(l_iterator, Length::RECORD_NAME, m_vpdVector.cend());
+
+    std::string l_kwName = std::string(
+        l_iterator,
+        std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+
+    // Iterate through the keywords until the last keyword PF is found.
+    while (l_kwName != constants::LAST_KW)
+    {
+        // First character required for #D keyword check
+        char l_kwNameStart = *l_iterator;
+
+        std::ranges::advance(l_iterator, Length::KW_NAME, m_vpdVector.cend());
+
+        // Get the keyword's data length
+        auto l_kwdDataLength = 0;
+
+        if (constants::POUND_KW == l_kwNameStart)
+        {
+            l_kwdDataLength = readUInt16LE(l_iterator);
+            std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+                                 m_vpdVector.cend());
+        }
+        else
+        {
+            l_kwdDataLength = *l_iterator;
+            std::ranges::advance(l_iterator, sizeof(types::KwSize),
+                                 m_vpdVector.cend());
+        }
+
+        if (l_kwName == i_keywordName)
+        {
+            // Return keyword's value to the caller
+            return types::BinaryVector(
+                l_iterator, std::ranges::next(l_iterator, l_kwdDataLength,
+                                              m_vpdVector.cend()));
+        }
+
+        // next keyword search
+        std::ranges::advance(l_iterator, l_kwdDataLength, m_vpdVector.cend());
+
+        // next keyword name
+        l_kwName = std::string(
+            l_iterator,
+            std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+    }
+
+    // Keyword not found
+    throw std::runtime_error("Given keyword not found.");
+}
+
+types::RecordData IpzVpdParser::getRecordDetailsFromVTOC(
+    const types::Record& i_recordName, const types::RecordOffset& i_vtocOffset)
+{
+    // Get VTOC's PT keyword value.
+    const auto l_vtocPTKwValue =
+        getKeywordValueFromRecord("VTOC", "PT", i_vtocOffset);
+
+    // Parse through VTOC PT keyword value to find the record which we are
+    // interested in.
+    auto l_vtocPTItr = l_vtocPTKwValue.cbegin();
+
+    types::RecordData l_recordData;
+
+    while (l_vtocPTItr < l_vtocPTKwValue.cend())
+    {
+        if (i_recordName ==
+            std::string(l_vtocPTItr, l_vtocPTItr + Length::RECORD_NAME))
+        {
+            // Record found in VTOC PT keyword. Get offset
+            std::ranges::advance(l_vtocPTItr,
+                                 Length::RECORD_NAME + Length::RECORD_TYPE,
+                                 l_vtocPTKwValue.cend());
+            const auto l_recordOffset = readUInt16LE(l_vtocPTItr);
+
+            std::ranges::advance(l_vtocPTItr, Length::RECORD_OFFSET,
+                                 l_vtocPTKwValue.cend());
+            const auto l_recordLength = readUInt16LE(l_vtocPTItr);
+
+            std::ranges::advance(l_vtocPTItr, Length::RECORD_LENGTH,
+                                 l_vtocPTKwValue.cend());
+            const auto l_eccOffset = readUInt16LE(l_vtocPTItr);
+
+            std::ranges::advance(l_vtocPTItr, Length::RECORD_ECC_OFFSET,
+                                 l_vtocPTKwValue.cend());
+            const auto l_eccLength = readUInt16LE(l_vtocPTItr);
+
+            l_recordData = std::make_tuple(l_recordOffset, l_recordLength,
+                                           l_eccOffset, l_eccLength);
+            break;
+        }
+
+        std::ranges::advance(l_vtocPTItr, Length::SKIP_A_RECORD_IN_PT,
+                             l_vtocPTKwValue.cend());
+    }
+
+    return l_recordData;
+}
+
+types::DbusVariantType IpzVpdParser::readKeywordFromHardware(
+    const types::ReadVpdParams i_paramsToReadData)
+{
+    // Extract record and keyword from i_paramsToReadData
+    types::Record l_record;
+    types::Keyword l_keyword;
+
+    if (const types::IpzType* l_ipzData =
+            std::get_if<types::IpzType>(&i_paramsToReadData))
+    {
+        l_record = std::get<0>(*l_ipzData);
+        l_keyword = std::get<1>(*l_ipzData);
+    }
+    else
+    {
+        logging::logMessage(
+            "Input parameter type provided isn't compatible with the given VPD type.");
+        throw types::DbusInvalidArgument();
+    }
+
+    // Read keyword's value from vector
+    auto l_itrToVPD = m_vpdVector.cbegin();
+
+    if (l_record == "VHDR")
+    {
+// Disable providing a way to read keywords from VHDR for the time being.
+#if 0
+        std::ranges::advance(l_itrToVPD, Offset::VHDR_RECORD,
+                             m_vpdVector.cend());
+
+        return types::DbusVariantType{getKeywordValueFromRecord(
+            l_record, l_keyword, Offset::VHDR_RECORD)};
+#endif
+
+        logging::logMessage("Read cannot be performed on VHDR record.");
+        throw types::DbusInvalidArgument();
+    }
+
+    // Get VTOC offset
+    std::ranges::advance(l_itrToVPD, Offset::VTOC_PTR, m_vpdVector.cend());
+    auto l_vtocOffset = readUInt16LE(l_itrToVPD);
+
+    if (l_record == "VTOC")
+    {
+        // Disable providing a way to read keywords from VTOC for the time
+        // being.
+#if 0
+        return types::DbusVariantType{
+            getKeywordValueFromRecord(l_record, l_keyword, l_vtocOffset)};
+#endif
+
+        logging::logMessage("Read cannot be performed on VTOC record.");
+        throw types::DbusInvalidArgument();
+    }
+
+    // Get record offset from VTOC's PT keyword value.
+    auto l_recordData = getRecordDetailsFromVTOC(l_record, l_vtocOffset);
+    const auto l_recordOffset = std::get<0>(l_recordData);
+
+    if (l_recordOffset == 0)
+    {
+        throw std::runtime_error("Record not found in VTOC PT keyword.");
+    }
+
+    // Get the given keyword's value
+    return types::DbusVariantType{
+        getKeywordValueFromRecord(l_record, l_keyword, l_recordOffset)};
+}
+
+void IpzVpdParser::updateRecordECC(
+    const auto& i_recordDataOffset, const auto& i_recordDataLength,
+    const auto& i_recordECCOffset, size_t i_recordECCLength,
+    types::BinaryVector& io_vpdVector)
+{
+    auto l_recordDataBegin =
+        std::next(io_vpdVector.begin(), i_recordDataOffset);
+
+    auto l_recordECCBegin = std::next(io_vpdVector.begin(), i_recordECCOffset);
+
+    auto l_eccStatus = vpdecc_create_ecc(
+        const_cast<uint8_t*>(&l_recordDataBegin[0]), i_recordDataLength,
+        const_cast<uint8_t*>(&l_recordECCBegin[0]), &i_recordECCLength);
+
+    if (l_eccStatus != VPD_ECC_OK)
+    {
+        throw(EccException("ECC update failed with error " + l_eccStatus));
+    }
+
+    auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
+
+    m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
+
+    std::copy(l_recordECCBegin, l_recordECCEnd,
+              std::ostreambuf_iterator<char>(m_vpdFileStream));
+}
+
+int IpzVpdParser::setKeywordValueInRecord(
+    const types::Record& i_recordName, const types::Keyword& i_keywordName,
+    const types::BinaryVector& i_keywordData,
+    const types::RecordOffset& i_recordDataOffset,
+    types::BinaryVector& io_vpdVector)
+{
+    auto l_iterator = io_vpdVector.begin();
+
+    // Go to the record name in the given record's offset
+    std::ranges::advance(l_iterator,
+                         i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+                         io_vpdVector.end());
+
+    const std::string l_recordFound(
+        l_iterator,
+        std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
+
+    // Check if the record is present in the given record's offset
+    if (i_recordName != l_recordFound)
+    {
+        throw(DataException("Given record found at the offset " +
+                            std::to_string(i_recordDataOffset) + " is : " +
+                            l_recordFound + " and not " + i_recordName));
+    }
+
+    std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
+
+    std::string l_kwName = std::string(
+        l_iterator,
+        std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+
+    // Iterate through the keywords until the last keyword PF is found.
+    while (l_kwName != constants::LAST_KW)
+    {
+        // First character required for #D keyword check
+        char l_kwNameStart = *l_iterator;
+
+        std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
+
+        // Find the keyword's data length
+        size_t l_kwdDataLength = 0;
+
+        if (constants::POUND_KW == l_kwNameStart)
+        {
+            l_kwdDataLength = readUInt16LE(l_iterator);
+            std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+                                 io_vpdVector.end());
+        }
+        else
+        {
+            l_kwdDataLength = *l_iterator;
+            std::ranges::advance(l_iterator, sizeof(types::KwSize),
+                                 io_vpdVector.end());
+        }
+
+        if (l_kwName == i_keywordName)
+        {
+            // Before writing the keyword's value, get the maximum size that can
+            // be updated.
+            const auto l_lengthToUpdate =
+                i_keywordData.size() <= l_kwdDataLength
+                    ? i_keywordData.size()
+                    : l_kwdDataLength;
+
+            // Set the keyword's value on vector. This is required to update the
+            // record's ECC based on the new value set.
+            const auto i_keywordDataEnd = std::ranges::next(
+                i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
+
+            std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
+
+            // Set the keyword's value on hardware
+            const auto l_kwdDataOffset =
+                std::distance(io_vpdVector.begin(), l_iterator);
+            m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
+                                  std::ios::beg);
+
+            std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
+                      std::ostreambuf_iterator<char>(m_vpdFileStream));
+
+            // return no of bytes set
+            return l_lengthToUpdate;
+        }
+
+        // next keyword search
+        std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
+
+        // next keyword name
+        l_kwName = std::string(
+            l_iterator,
+            std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+    }
+
+    // Keyword not found
+    throw(DataException(
+        "Keyword " + i_keywordName + " not found in record " + i_recordName));
+}
+
+int IpzVpdParser::writeKeywordOnHardware(
+    const types::WriteVpdParams i_paramsToWriteData)
+{
+    int l_sizeWritten = -1;
+
+    try
+    {
+        types::Record l_recordName;
+        types::Keyword l_keywordName;
+        types::BinaryVector l_keywordData;
+
+        // Extract record, keyword and value from i_paramsToWriteData
+        if (const types::IpzData* l_ipzData =
+                std::get_if<types::IpzData>(&i_paramsToWriteData))
+        {
+            l_recordName = std::get<0>(*l_ipzData);
+            l_keywordName = std::get<1>(*l_ipzData);
+            l_keywordData = std::get<2>(*l_ipzData);
+        }
+        else
+        {
+            logging::logMessage(
+                "Input parameter type provided isn't compatible with the given FRU's VPD type.");
+            throw types::DbusInvalidArgument();
+        }
+
+        if (l_recordName == "VHDR" || l_recordName == "VTOC")
+        {
+            logging::logMessage(
+                "Write operation not allowed on the given record : " +
+                l_recordName);
+            throw types::DbusNotAllowed();
+        }
+
+        if (l_keywordData.size() == 0)
+        {
+            logging::logMessage(
+                "Write operation not allowed as the given keyword's data length is 0.");
+            throw types::DbusInvalidArgument();
+        }
+
+        auto l_vpdBegin = m_vpdVector.begin();
+
+        // Get VTOC offset
+        std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
+        auto l_vtocOffset = readUInt16LE(l_vpdBegin);
+
+        // Get the details of user given record from VTOC
+        const types::RecordData& l_inputRecordDetails =
+            getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
+
+        const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
+
+        if (l_inputRecordOffset == 0)
+        {
+            throw(DataException("Record not found in VTOC PT keyword."));
+        }
+
+        // Create a local copy of m_vpdVector to perform keyword update and ecc
+        // update on filestream.
+        types::BinaryVector l_vpdVector = m_vpdVector;
+
+        // write keyword's value on hardware
+        l_sizeWritten =
+            setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
+                                    l_inputRecordOffset, l_vpdVector);
+
+        if (l_sizeWritten <= 0)
+        {
+            throw(DataException("Unable to set value on " + l_recordName + ":" +
+                                l_keywordName));
+        }
+
+        // Update the record's ECC
+        updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
+                        std::get<2>(l_inputRecordDetails),
+                        std::get<3>(l_inputRecordDetails), l_vpdVector);
+
+        logging::logMessage(std::to_string(l_sizeWritten) +
+                            " bytes updated successfully on hardware for " +
+                            l_recordName + ":" + l_keywordName);
+    }
+    catch (const std::exception& l_exception)
+    {
+        throw;
+    }
+
+    return l_sizeWritten;
+}
+} // namespace vpd
diff --git a/vpd-manager/src/isdimm_parser.cpp b/vpd-manager/src/isdimm_parser.cpp
new file mode 100644
index 0000000..76e1dea
--- /dev/null
+++ b/vpd-manager/src/isdimm_parser.cpp
@@ -0,0 +1,313 @@
+#include "isdimm_parser.hpp"
+
+#include "constants.hpp"
+#include "logger.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <numeric>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace vpd
+{
+
+// Constants
+constexpr auto SPD_JEDEC_DDR4_SDRAM_CAP_MASK = 0x0F;
+constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK = 0x07;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK = 0x07;
+constexpr auto SPD_JEDEC_DDR4_NUM_RANKS_MASK = 0x38;
+constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_MASK = 0x70;
+constexpr auto SPD_JEDEC_DDR4_SINGLE_LOAD_STACK = 0x02;
+constexpr auto SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK = 0x03;
+
+constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER = 256;
+constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER = 8;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER = 4;
+constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_RESERVED = 8;
+constexpr auto SPD_JEDEC_DDR4_4_RESERVED_BITS = 4;
+constexpr auto SPD_JEDEC_DDR4_3_RESERVED_BITS = 3;
+constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT = 4;
+
+constexpr auto SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET = 321;
+constexpr auto SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET = 320;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE0_OFFSET = 325;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE1_OFFSET = 326;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE2_OFFSET = 327;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE3_OFFSET = 328;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET = 4;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET = 5;
+constexpr auto SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET = 6;
+constexpr auto SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET = 12;
+
+// Lookup tables
+const std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap = {
+    {std::make_tuple("8421000", 6), "78P4191"},
+    {std::make_tuple("8421008", 6), "78P4192"},
+    {std::make_tuple("8529000", 6), "78P4197"},
+    {std::make_tuple("8529008", 6), "78P4198"},
+    {std::make_tuple("8529928", 6), "78P4199"},
+    {std::make_tuple("8529B28", 6), "78P4200"},
+    {std::make_tuple("8631928", 6), "78P6925"},
+    {std::make_tuple("8529000", 5), "78P7317"},
+    {std::make_tuple("8529008", 5), "78P7318"},
+    {std::make_tuple("8631008", 5), "78P6815"}};
+
+const std::unordered_map<std::string, std::string> pnCCINMap = {
+    {"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"},
+    {"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"},
+    {"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"},
+    {"78P6815", "32BB"}};
+
+auto JedecSpdParser::getDDR4DimmCapacity(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    size_t l_tmp = 0, l_dimmSize = 0;
+
+    size_t l_sdramCap = 1, l_priBusWid = 1, l_sdramWid = 1,
+           l_logicalRanksPerDimm = 1;
+    size_t l_dieCount = 1;
+
+    // NOTE: This calculation is Only for DDR4
+
+    // Calculate SDRAM  capacity
+    l_tmp = i_iterator[constants::SPD_BYTE_4] & SPD_JEDEC_DDR4_SDRAM_CAP_MASK;
+
+    /* Make sure the bits are not Reserved */
+    if (l_tmp >= SPD_JEDEC_DDR4_SDRAMCAP_RESERVED)
+    {
+        logging::logMessage(
+            "Bad data in spd byte 4. Can't calculate SDRAM capacity "
+            "and so dimm size.\n ");
+        return l_dimmSize;
+    }
+    l_sdramCap = (l_sdramCap << l_tmp) * SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER;
+
+    /* Calculate Primary bus width */
+    l_tmp = i_iterator[constants::SPD_BYTE_13] &
+            SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK;
+    if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
+    {
+        logging::logMessage(
+            "Bad data in spd byte 13. Can't calculate primary bus "
+            "width and so dimm size.\n ");
+        return l_dimmSize;
+    }
+    l_priBusWid = (l_priBusWid << l_tmp) *
+                  SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER;
+
+    /* Calculate SDRAM width */
+    l_tmp = i_iterator[constants::SPD_BYTE_12] &
+            SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK;
+    if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
+    {
+        logging::logMessage(
+            "Bad data in spd byte 12. Can't calculate SDRAM width and "
+            "so dimm size.\n ");
+        return l_dimmSize;
+    }
+    l_sdramWid = (l_sdramWid << l_tmp) * SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER;
+
+    l_tmp = i_iterator[constants::SPD_BYTE_6] &
+            SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK;
+    if (l_tmp == SPD_JEDEC_DDR4_SINGLE_LOAD_STACK)
+    {
+        // Fetch die count
+        l_tmp = i_iterator[constants::SPD_BYTE_6] &
+                SPD_JEDEC_DDR4_DIE_COUNT_MASK;
+        l_tmp >>= SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT;
+        l_dieCount = l_tmp + 1;
+    }
+
+    /* Calculate Number of ranks */
+    l_tmp = i_iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_NUM_RANKS_MASK;
+    l_tmp >>= SPD_JEDEC_DDR4_3_RESERVED_BITS;
+
+    if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
+    {
+        logging::logMessage(
+            "Can't calculate number of ranks. Invalid data found.\n ");
+        return l_dimmSize;
+    }
+    l_logicalRanksPerDimm = (l_tmp + 1) * l_dieCount;
+
+    l_dimmSize = (l_sdramCap / SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER) *
+                 (l_priBusWid / l_sdramWid) * l_logicalRanksPerDimm;
+
+    return l_dimmSize;
+}
+
+std::string_view JedecSpdParser::getDDR4PartNumber(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    char l_tmpPN[constants::PART_NUM_LEN + 1] = {'\0'};
+    sprintf(l_tmpPN, "%02X%02X%02X%X",
+            i_iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F);
+    std::string l_partNumber(l_tmpPN, sizeof(l_tmpPN) - 1);
+    return l_partNumber;
+}
+
+std::string JedecSpdParser::getDDR4SerialNumber(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    char l_tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'};
+    sprintf(l_tmpSN, "%02X%02X%02X%02X%02X%02X",
+            i_iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET],
+            i_iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]);
+    std::string l_serialNumber(l_tmpSN, sizeof(l_tmpSN) - 1);
+    return l_serialNumber;
+}
+
+std::string_view JedecSpdParser::getDDR4FruNumber(
+    const std::string& i_partNumber,
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    // check for 128GB ISRDIMM not implemented
+    //(128GB 2RX4(8GX72) IS RDIMM 36*(16GBIT, 2H),1.2V 288PIN,1.2" ROHS) - NA
+
+    // MTB Units is used in deciding the frequency of the DIMM
+    // This is applicable only for DDR4 specification
+    // 10 - DDR4-1600
+    // 9  - DDR4-1866
+    // 8  - DDR4-2133
+    // 7  - DDR4-2400
+    // 6  - DDR4-2666
+    // 5  - DDR4-3200
+    // pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber>
+    uint8_t l_mtbUnits = i_iterator[constants::SPD_BYTE_18] &
+                         constants::SPD_BYTE_MASK;
+    std::string l_fruNumber = "FFFFFFF";
+    auto it = pnFreqFnMap.find({i_partNumber, l_mtbUnits});
+    if (it != pnFreqFnMap.end())
+    {
+        l_fruNumber = it->second;
+    }
+
+    return l_fruNumber;
+}
+
+std::string_view JedecSpdParser::getDDR4CCIN(const std::string& i_fruNumber)
+{
+    auto it = pnCCINMap.find(i_fruNumber);
+    if (it != pnCCINMap.end())
+    {
+        return it->second;
+    }
+    return "XXXX"; // Return default value as XXXX
+}
+
+auto JedecSpdParser::getDDR5DimmCapacity(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    // dummy implementation to be updated when required
+    size_t dimmSize = 0;
+    (void)i_iterator;
+    return dimmSize;
+}
+
+auto JedecSpdParser::getDDR5PartNumber(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    // dummy implementation to be updated when required
+    std::string l_partNumber;
+    (void)i_iterator;
+    l_partNumber = "0123456";
+    return l_partNumber;
+}
+
+auto JedecSpdParser::getDDR5SerialNumber(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    // dummy implementation to be updated when required
+    std::string l_serialNumber;
+    (void)i_iterator;
+    l_serialNumber = "444444444444";
+    return l_serialNumber;
+}
+
+auto JedecSpdParser::getDDR5FruNumber(const std::string& i_partNumber)
+{
+    // dummy implementation to be updated when required
+    static std::unordered_map<std::string, std::string> pnFruMap = {
+        {"1234567", "XXXXXXX"}};
+
+    std::string l_fruNumber;
+    auto itr = pnFruMap.find(i_partNumber);
+    if (itr != pnFruMap.end())
+    {
+        l_fruNumber = itr->second;
+    }
+    else
+    {
+        l_fruNumber = "FFFFFFF";
+    }
+    return l_fruNumber;
+}
+
+auto JedecSpdParser::getDDR5CCIN(const std::string& i_partNumber)
+{
+    // dummy implementation to be updated when required
+    static std::unordered_map<std::string, std::string> pnCCINMap = {
+        {"1234567", "XXXX"}};
+
+    std::string ccin = "XXXX";
+    auto itr = pnCCINMap.find(i_partNumber);
+    if (itr != pnCCINMap.end())
+    {
+        ccin = itr->second;
+    }
+    return ccin;
+}
+
+types::JedecSpdMap JedecSpdParser::readKeywords(
+    types::BinaryVector::const_iterator& i_iterator)
+{
+    types::JedecSpdMap l_keywordValueMap{};
+    size_t dimmSize = getDDR4DimmCapacity(i_iterator);
+    if (!dimmSize)
+    {
+        logging::logMessage("Error: Calculated dimm size is 0.");
+    }
+    else
+    {
+        l_keywordValueMap.emplace("MemorySizeInKB",
+                                  dimmSize * constants::CONVERT_MB_TO_KB);
+    }
+
+    auto l_partNumber = getDDR4PartNumber(i_iterator);
+    auto l_fruNumber = getDDR4FruNumber(
+        std::string(l_partNumber.begin(), l_partNumber.end()), i_iterator);
+    auto l_serialNumber = getDDR4SerialNumber(i_iterator);
+    auto ccin =
+        getDDR4CCIN(std::string(l_fruNumber.begin(), l_fruNumber.end()));
+    // PN value is made same as FN value
+    auto l_displayPartNumber = l_fruNumber;
+    l_keywordValueMap.emplace("PN",
+                              move(std::string(l_displayPartNumber.begin(),
+                                               l_displayPartNumber.end())));
+    l_keywordValueMap.emplace(
+        "FN", move(std::string(l_fruNumber.begin(), l_fruNumber.end())));
+    l_keywordValueMap.emplace("SN", move(l_serialNumber));
+    l_keywordValueMap.emplace("CC",
+                              move(std::string(ccin.begin(), ccin.end())));
+
+    return l_keywordValueMap;
+}
+
+types::VPDMapVariant JedecSpdParser::parse()
+{
+    // Read the data and return the map
+    auto l_iterator = m_memSpd.cbegin();
+    auto l_spdDataMap = readKeywords(l_iterator);
+    return l_spdDataMap;
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/keyword_vpd_parser.cpp b/vpd-manager/src/keyword_vpd_parser.cpp
new file mode 100644
index 0000000..9997f94
--- /dev/null
+++ b/vpd-manager/src/keyword_vpd_parser.cpp
@@ -0,0 +1,136 @@
+#include "keyword_vpd_parser.hpp"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace vpd
+{
+
+types::VPDMapVariant KeywordVpdParser::parse()
+{
+    if (m_keywordVpdVector.empty())
+    {
+        throw(DataException("Vector for Keyword format VPD is empty"));
+    }
+    m_vpdIterator = m_keywordVpdVector.begin();
+
+    if (*m_vpdIterator != constants::KW_VPD_START_TAG)
+    {
+        throw(DataException("Invalid Large resource type Identifier String"));
+    }
+
+    checkNextBytesValidity(sizeof(constants::KW_VPD_START_TAG));
+    std::advance(m_vpdIterator, sizeof(constants::KW_VPD_START_TAG));
+
+    uint16_t l_dataSize = getKwDataSize();
+
+    checkNextBytesValidity(constants::TWO_BYTES + l_dataSize);
+    std::advance(m_vpdIterator, constants::TWO_BYTES + l_dataSize);
+
+    // Check for invalid vendor defined large resource type
+    if (*m_vpdIterator != constants::KW_VPD_PAIR_START_TAG)
+    {
+        if (*m_vpdIterator != constants::ALT_KW_VPD_PAIR_START_TAG)
+        {
+            throw(DataException("Invalid Keyword Vpd Start Tag"));
+        }
+    }
+    types::BinaryVector::const_iterator l_checkSumStart = m_vpdIterator;
+    auto l_kwValMap = populateVpdMap();
+
+    // Do these validations before returning parsed data.
+    // Check for small resource type end tag
+    if (*m_vpdIterator != constants::KW_VAL_PAIR_END_TAG)
+    {
+        throw(DataException("Invalid Small resource type End"));
+    }
+
+    types::BinaryVector::const_iterator l_checkSumEnd = m_vpdIterator;
+    validateChecksum(l_checkSumStart, l_checkSumEnd);
+
+    checkNextBytesValidity(constants::TWO_BYTES);
+    std::advance(m_vpdIterator, constants::TWO_BYTES);
+
+    // Check VPD end Tag.
+    if (*m_vpdIterator != constants::KW_VPD_END_TAG)
+    {
+        throw(DataException("Invalid Small resource type."));
+    }
+
+    return l_kwValMap;
+}
+
+types::KeywordVpdMap KeywordVpdParser::populateVpdMap()
+{
+    checkNextBytesValidity(constants::ONE_BYTE);
+    std::advance(m_vpdIterator, constants::ONE_BYTE);
+
+    auto l_totalSize = getKwDataSize();
+    if (l_totalSize == 0)
+    {
+        throw(DataException("Data size is 0, badly formed keyword VPD"));
+    }
+
+    checkNextBytesValidity(constants::TWO_BYTES);
+    std::advance(m_vpdIterator, constants::TWO_BYTES);
+
+    types::KeywordVpdMap l_kwValMap;
+
+    // Parse the keyword-value and store the pairs in map
+    while (l_totalSize > 0)
+    {
+        checkNextBytesValidity(constants::TWO_BYTES);
+        std::string l_keywordName(m_vpdIterator,
+                                  m_vpdIterator + constants::TWO_BYTES);
+        std::advance(m_vpdIterator, constants::TWO_BYTES);
+
+        size_t l_kwSize = *m_vpdIterator;
+        checkNextBytesValidity(constants::ONE_BYTE + l_kwSize);
+        m_vpdIterator++;
+        std::vector<uint8_t> l_valueBytes(m_vpdIterator,
+                                          m_vpdIterator + l_kwSize);
+        std::advance(m_vpdIterator, l_kwSize);
+
+        l_kwValMap.emplace(
+            std::make_pair(std::move(l_keywordName), std::move(l_valueBytes)));
+
+        l_totalSize -= constants::TWO_BYTES + constants::ONE_BYTE + l_kwSize;
+    }
+
+    return l_kwValMap;
+}
+
+void KeywordVpdParser::validateChecksum(
+    types::BinaryVector::const_iterator i_checkSumStart,
+    types::BinaryVector::const_iterator i_checkSumEnd)
+{
+    uint8_t l_checkSumCalculated = 0;
+
+    // Checksum calculation
+    l_checkSumCalculated =
+        std::accumulate(i_checkSumStart, i_checkSumEnd, l_checkSumCalculated);
+    l_checkSumCalculated = ~l_checkSumCalculated + 1;
+    uint8_t l_checksumVpdValue = *(m_vpdIterator + constants::ONE_BYTE);
+
+    if (l_checkSumCalculated != l_checksumVpdValue)
+    {
+        throw(DataException("Invalid Checksum"));
+    }
+}
+
+void KeywordVpdParser::checkNextBytesValidity(uint8_t i_numberOfBytes)
+{
+    if ((std::distance(m_keywordVpdVector.begin(),
+                       m_vpdIterator + i_numberOfBytes)) >
+        std::distance(m_keywordVpdVector.begin(), m_keywordVpdVector.end()))
+    {
+        throw(DataException("Truncated VPD data"));
+    }
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/logger.cpp b/vpd-manager/src/logger.cpp
new file mode 100644
index 0000000..19959a1
--- /dev/null
+++ b/vpd-manager/src/logger.cpp
@@ -0,0 +1,23 @@
+#include "logger.hpp"
+
+#include <sstream>
+
+namespace vpd
+{
+namespace logging
+{
+void logMessage(std::string_view message, const std::source_location& location)
+{
+    std::ostringstream log;
+    log << "FileName: " << location.file_name() << ","
+        << " Line: " << location.line() << " " << message;
+
+    /* TODO: Check on this later.
+    log << "FileName: " << location.file_name() << ","
+        << " Line: " << location.line() << ","
+        << " Func: " << location.function_name() << ", " << message;*/
+
+    std::cout << log.str() << std::endl;
+}
+} // namespace logging
+} // namespace vpd
diff --git a/vpd-manager/src/manager.cpp b/vpd-manager/src/manager.cpp
new file mode 100644
index 0000000..51eb4af
--- /dev/null
+++ b/vpd-manager/src/manager.cpp
@@ -0,0 +1,915 @@
+#include "config.h"
+
+#include "manager.hpp"
+
+#include "backup_restore.hpp"
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "parser_factory.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+#include "utility/dbus_utility.hpp"
+#include "utility/json_utility.hpp"
+#include "utility/vpd_specific_utility.hpp"
+
+#include <boost/asio/steady_timer.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+
+namespace vpd
+{
+Manager::Manager(
+    const std::shared_ptr<boost::asio::io_context>& ioCon,
+    const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
+    const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
+    m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
+{
+    try
+    {
+#ifdef IBM_SYSTEM
+        m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
+
+        // Set up minimal things that is needed before bus name is claimed.
+        m_worker->performInitialSetup();
+
+        // set callback to detect any asset tag change
+        registerAssetTagChangeCallback();
+
+        // set async timer to detect if system VPD is published on D-Bus.
+        SetTimerToDetectSVPDOnDbus();
+
+        // set async timer to detect if VPD collection is done.
+        SetTimerToDetectVpdCollectionStatus();
+
+        // Instantiate GpioMonitor class
+        m_gpioMonitor = std::make_shared<GpioMonitor>(
+            m_worker->getSysCfgJsonObj(), m_worker, m_ioContext);
+
+#endif
+        // set callback to detect host state change.
+        registerHostStateChangeCallback();
+
+        // For backward compatibility. Should be depricated.
+        iFace->register_method(
+            "WriteKeyword",
+            [this](const sdbusplus::message::object_path i_path,
+                   const std::string i_recordName, const std::string i_keyword,
+                   const types::BinaryVector i_value) -> int {
+                return this->updateKeyword(
+                    i_path, std::make_tuple(i_recordName, i_keyword, i_value));
+            });
+
+        // Register methods under com.ibm.VPD.Manager interface
+        iFace->register_method(
+            "UpdateKeyword",
+            [this](const types::Path i_vpdPath,
+                   const types::WriteVpdParams i_paramsToWriteData) -> int {
+                return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
+            });
+
+        iFace->register_method(
+            "WriteKeywordOnHardware",
+            [this](const types::Path i_fruPath,
+                   const types::WriteVpdParams i_paramsToWriteData) -> int {
+                return this->updateKeywordOnHardware(i_fruPath,
+                                                     i_paramsToWriteData);
+            });
+
+        iFace->register_method(
+            "ReadKeyword",
+            [this](const types::Path i_fruPath,
+                   const types::ReadVpdParams i_paramsToReadData)
+                -> types::DbusVariantType {
+                return this->readKeyword(i_fruPath, i_paramsToReadData);
+            });
+
+        iFace->register_method(
+            "CollectFRUVPD",
+            [this](const sdbusplus::message::object_path& i_dbusObjPath) {
+                this->collectSingleFruVpd(i_dbusObjPath);
+            });
+
+        iFace->register_method(
+            "deleteFRUVPD",
+            [this](const sdbusplus::message::object_path& i_dbusObjPath) {
+                this->deleteSingleFruVpd(i_dbusObjPath);
+            });
+
+        iFace->register_method(
+            "GetExpandedLocationCode",
+            [this](const std::string& i_unexpandedLocationCode,
+                   uint16_t& i_nodeNumber) -> std::string {
+                return this->getExpandedLocationCode(i_unexpandedLocationCode,
+                                                     i_nodeNumber);
+            });
+
+        iFace->register_method("GetFRUsByExpandedLocationCode",
+                               [this](const std::string& i_expandedLocationCode)
+                                   -> types::ListOfPaths {
+                                   return this->getFrusByExpandedLocationCode(
+                                       i_expandedLocationCode);
+                               });
+
+        iFace->register_method(
+            "GetFRUsByUnexpandedLocationCode",
+            [this](const std::string& i_unexpandedLocationCode,
+                   uint16_t& i_nodeNumber) -> types::ListOfPaths {
+                return this->getFrusByUnexpandedLocationCode(
+                    i_unexpandedLocationCode, i_nodeNumber);
+            });
+
+        iFace->register_method(
+            "GetHardwarePath",
+            [this](const sdbusplus::message::object_path& i_dbusObjPath)
+                -> std::string { return this->getHwPath(i_dbusObjPath); });
+
+        iFace->register_method("PerformVPDRecollection", [this]() {
+            this->performVpdRecollection();
+        });
+
+        // Indicates FRU VPD collection for the system has not started.
+        iFace->register_property_rw<std::string>(
+            "CollectionStatus", sdbusplus::vtable::property_::emits_change,
+            [this](const std::string l_currStatus, const auto&) {
+                m_vpdCollectionStatus = l_currStatus;
+                return 0;
+            },
+            [this](const auto&) { return m_vpdCollectionStatus; });
+    }
+    catch (const std::exception& e)
+    {
+        logging::logMessage(
+            "VPD-Manager service failed. " + std::string(e.what()));
+        throw;
+    }
+}
+
+#ifdef IBM_SYSTEM
+void Manager::registerAssetTagChangeCallback()
+{
+    static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
+        std::make_shared<sdbusplus::bus::match_t>(
+            *m_asioConnection,
+            sdbusplus::bus::match::rules::propertiesChanged(
+                constants::systemInvPath, constants::assetTagInf),
+            [this](sdbusplus::message_t& l_msg) {
+                processAssetTagChangeCallback(l_msg);
+            });
+}
+
+void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
+{
+    try
+    {
+        if (i_msg.is_method_error())
+        {
+            throw std::runtime_error(
+                "Error reading callback msg for asset tag.");
+        }
+
+        std::string l_objectPath;
+        types::PropertyMap l_propMap;
+        i_msg.read(l_objectPath, l_propMap);
+
+        const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
+        if (l_itrToAssetTag != l_propMap.end())
+        {
+            if (auto l_assetTag =
+                    std::get_if<std::string>(&(l_itrToAssetTag->second)))
+            {
+                // Call Notify to persist the AssetTag
+                types::ObjectMap l_objectMap = {
+                    {sdbusplus::message::object_path(constants::systemInvPath),
+                     {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
+
+                // Notify PIM
+                if (!dbusUtility::callPIM(move(l_objectMap)))
+                {
+                    throw std::runtime_error(
+                        "Call to PIM failed for asset tag update.");
+                }
+            }
+        }
+        else
+        {
+            throw std::runtime_error(
+                "Could not find asset tag in callback message.");
+        }
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO: Log PEL with below description.
+        logging::logMessage("Asset tag callback update failed with error: " +
+                            std::string(l_ex.what()));
+    }
+}
+
+void Manager::SetTimerToDetectSVPDOnDbus()
+{
+    static boost::asio::steady_timer timer(*m_ioContext);
+
+    // timer for 2 seconds
+    auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
+
+    (asyncCancelled == 0) ? logging::logMessage("Timer started")
+                          : logging::logMessage("Timer re-started");
+
+    timer.async_wait([this](const boost::system::error_code& ec) {
+        if (ec == boost::asio::error::operation_aborted)
+        {
+            throw std::runtime_error(
+                "Timer to detect system VPD collection status was aborted");
+        }
+
+        if (ec)
+        {
+            throw std::runtime_error(
+                "Timer to detect System VPD collection failed");
+        }
+
+        if (m_worker->isSystemVPDOnDBus())
+        {
+            // cancel the timer
+            timer.cancel();
+
+            // Triggering FRU VPD collection. Setting status to "In
+            // Progress".
+            m_interface->set_property("CollectionStatus",
+                                      std::string("InProgress"));
+            m_worker->collectFrusFromJson();
+        }
+    });
+}
+
+void Manager::SetTimerToDetectVpdCollectionStatus()
+{
+    // Keeping max retry for 2 minutes. TODO: Make it cinfigurable based on
+    // system type.
+    static constexpr auto MAX_RETRY = 40;
+
+    static boost::asio::steady_timer l_timer(*m_ioContext);
+    static uint8_t l_timerRetry = 0;
+
+    auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(3));
+
+    (l_asyncCancelled == 0)
+        ? logging::logMessage("Collection Timer started")
+        : logging::logMessage("Collection Timer re-started");
+
+    l_timer.async_wait([this](const boost::system::error_code& ec) {
+        if (ec == boost::asio::error::operation_aborted)
+        {
+            throw std::runtime_error(
+                "Timer to detect thread collection status was aborted");
+        }
+
+        if (ec)
+        {
+            throw std::runtime_error(
+                "Timer to detect thread collection failed");
+        }
+
+        if (m_worker->isAllFruCollectionDone())
+        {
+            // cancel the timer
+            l_timer.cancel();
+            m_interface->set_property("CollectionStatus",
+                                      std::string("Completed"));
+
+            const nlohmann::json& l_sysCfgJsonObj =
+                m_worker->getSysCfgJsonObj();
+            if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
+            {
+                BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
+                l_backupAndRestoreObj.backupAndRestore();
+            }
+        }
+        else
+        {
+            auto l_threadCount = m_worker->getActiveThreadCount();
+            if (l_timerRetry == MAX_RETRY)
+            {
+                l_timer.cancel();
+                logging::logMessage("Taking too long. Active thread = " +
+                                    std::to_string(l_threadCount));
+            }
+            else
+            {
+                l_timerRetry++;
+                logging::logMessage("Waiting... active thread = " +
+                                    std::to_string(l_threadCount) + "After " +
+                                    std::to_string(l_timerRetry) + " re-tries");
+
+                SetTimerToDetectVpdCollectionStatus();
+            }
+        }
+    });
+}
+#endif
+
+int Manager::updateKeyword(const types::Path i_vpdPath,
+                           const types::WriteVpdParams i_paramsToWriteData)
+{
+    if (i_vpdPath.empty())
+    {
+        logging::logMessage("Given VPD path is empty.");
+        return -1;
+    }
+
+    types::Path l_fruPath;
+    nlohmann::json l_sysCfgJsonObj{};
+
+    if (m_worker.get() != nullptr)
+    {
+        l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+
+        // Get the EEPROM path
+        if (!l_sysCfgJsonObj.empty())
+        {
+            try
+            {
+                l_fruPath =
+                    jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
+            }
+            catch (const std::exception& l_exception)
+            {
+                logging::logMessage(
+                    "Error while getting FRU path, Path: " + i_vpdPath +
+                    ", error: " + std::string(l_exception.what()));
+                return -1;
+            }
+        }
+    }
+
+    if (l_fruPath.empty())
+    {
+        l_fruPath = i_vpdPath;
+    }
+
+    try
+    {
+        std::shared_ptr<Parser> l_parserObj =
+            std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
+        return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
+    }
+    catch (const std::exception& l_exception)
+    {
+        // TODO:: error log needed
+        logging::logMessage("Update keyword failed for file[" + i_vpdPath +
+                            "], reason: " + std::string(l_exception.what()));
+        return -1;
+    }
+}
+
+int Manager::updateKeywordOnHardware(
+    const types::Path i_fruPath,
+    const types::WriteVpdParams i_paramsToWriteData) noexcept
+{
+    try
+    {
+        if (i_fruPath.empty())
+        {
+            throw std::runtime_error("Given FRU path is empty");
+        }
+
+        nlohmann::json l_sysCfgJsonObj{};
+
+        if (m_worker.get() != nullptr)
+        {
+            l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+        }
+
+        std::shared_ptr<Parser> l_parserObj =
+            std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
+        return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
+    }
+    catch (const std::exception& l_exception)
+    {
+        EventLogger::createAsyncPel(
+            types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
+            __FILE__, __FUNCTION__, 0,
+            "Update keyword on hardware failed for file[" + i_fruPath +
+                "], reason: " + std::string(l_exception.what()),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+        return constants::FAILURE;
+    }
+}
+
+types::DbusVariantType Manager::readKeyword(
+    const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
+{
+    try
+    {
+        nlohmann::json l_jsonObj{};
+
+        if (m_worker.get() != nullptr)
+        {
+            l_jsonObj = m_worker->getSysCfgJsonObj();
+        }
+
+        std::error_code ec;
+
+        // Check if given path is filesystem path
+        if (!std::filesystem::exists(i_fruPath, ec) && (ec))
+        {
+            throw std::runtime_error(
+                "Given file path " + i_fruPath + " not found.");
+        }
+
+        logging::logMessage("Performing VPD read on " + i_fruPath);
+
+        std::shared_ptr<vpd::Parser> l_parserObj =
+            std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
+
+        std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
+            l_parserObj->getVpdParserInstance();
+
+        return (
+            l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
+    }
+    catch (const std::exception& e)
+    {
+        logging::logMessage(
+            e.what() + std::string(". VPD manager read operation failed for ") +
+            i_fruPath);
+        throw types::DeviceError::ReadFailure();
+    }
+}
+
+void Manager::collectSingleFruVpd(
+    const sdbusplus::message::object_path& i_dbusObjPath)
+{
+    try
+    {
+        if (m_vpdCollectionStatus != "Completed")
+        {
+            throw std::runtime_error(
+                "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
+                std::string(i_dbusObjPath));
+        }
+
+        // Get system config JSON object from worker class
+        nlohmann::json l_sysCfgJsonObj{};
+
+        if (m_worker.get() != nullptr)
+        {
+            l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+        }
+
+        // Check if system config JSON is present
+        if (l_sysCfgJsonObj.empty())
+        {
+            throw std::runtime_error(
+                "System config JSON object not present. Single FRU VPD collection failed for " +
+                std::string(i_dbusObjPath));
+        }
+
+        // Get FRU path for the given D-bus object path from JSON
+        const std::string& l_fruPath =
+            jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
+
+        if (l_fruPath.empty())
+        {
+            throw std::runtime_error(
+                "D-bus object path not present in JSON. Single FRU VPD collection failed for " +
+                std::string(i_dbusObjPath));
+        }
+
+        // Check if host is up and running
+        if (dbusUtility::isHostRunning())
+        {
+            if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
+                                                        l_fruPath))
+            {
+                throw std::runtime_error(
+                    "Given FRU is not replaceable at host runtime. Single FRU VPD collection failed for " +
+                    std::string(i_dbusObjPath));
+            }
+        }
+        else if (dbusUtility::isBMCReady())
+        {
+            if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
+                                                        l_fruPath) &&
+                (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
+                                                         l_fruPath)))
+            {
+                throw std::runtime_error(
+                    "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection failed for " +
+                    std::string(i_dbusObjPath));
+            }
+        }
+
+        // Parse VPD
+        types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
+
+        // If l_parsedVpd is pointing to std::monostate
+        if (l_parsedVpd.index() == 0)
+        {
+            throw std::runtime_error(
+                "VPD parsing failed for " + std::string(i_dbusObjPath));
+        }
+
+        // Get D-bus object map from worker class
+        types::ObjectMap l_dbusObjectMap;
+        m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
+
+        if (l_dbusObjectMap.empty())
+        {
+            throw std::runtime_error(
+                "Failed to create D-bus object map. Single FRU VPD collection failed for " +
+                std::string(i_dbusObjPath));
+        }
+
+        // Call PIM's Notify method
+        if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
+        {
+            throw std::runtime_error(
+                "Notify PIM failed. Single FRU VPD collection failed for " +
+                std::string(i_dbusObjPath));
+        }
+    }
+    catch (const std::exception& l_error)
+    {
+        // TODO: Log PEL
+        logging::logMessage(std::string(l_error.what()));
+    }
+}
+
+void Manager::deleteSingleFruVpd(
+    const sdbusplus::message::object_path& i_dbusObjPath)
+{
+    try
+    {
+        if (std::string(i_dbusObjPath).empty())
+        {
+            throw std::runtime_error(
+                "Given DBus object path is empty. Aborting FRU VPD deletion.");
+        }
+
+        if (m_worker.get() == nullptr)
+        {
+            throw std::runtime_error(
+                "Worker object not found, can't perform FRU VPD deletion for: " +
+                std::string(i_dbusObjPath));
+        }
+
+        m_worker->deleteFruVpd(std::string(i_dbusObjPath));
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO: Log PEL
+        logging::logMessage(l_ex.what());
+    }
+}
+
+bool Manager::isValidUnexpandedLocationCode(
+    const std::string& i_unexpandedLocationCode)
+{
+    if ((i_unexpandedLocationCode.length() <
+         constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
+        ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
+          constants::STR_CMP_SUCCESS) &&
+         (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
+          constants::STR_CMP_SUCCESS)) ||
+        ((i_unexpandedLocationCode.length() >
+          constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
+         (i_unexpandedLocationCode.find("-") != 4)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+std::string Manager::getExpandedLocationCode(
+    const std::string& i_unexpandedLocationCode,
+    [[maybe_unused]] const uint16_t i_nodeNumber)
+{
+    if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
+    {
+        phosphor::logging::elog<types::DbusInvalidArgument>(
+            types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+            types::InvalidArgument::ARGUMENT_VALUE(
+                i_unexpandedLocationCode.c_str()));
+    }
+
+    const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+    if (!l_sysCfgJsonObj.contains("frus"))
+    {
+        logging::logMessage("Missing frus tag in system config JSON");
+    }
+
+    const nlohmann::json& l_listOfFrus =
+        l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+    for (const auto& l_frus : l_listOfFrus.items())
+    {
+        for (const auto& l_aFru : l_frus.value())
+        {
+            if (l_aFru["extraInterfaces"].contains(
+                    constants::locationCodeInf) &&
+                l_aFru["extraInterfaces"][constants::locationCodeInf].value(
+                    "LocationCode", "") == i_unexpandedLocationCode)
+            {
+                return std::get<std::string>(dbusUtility::readDbusProperty(
+                    l_aFru["serviceName"], l_aFru["inventoryPath"],
+                    constants::locationCodeInf, "LocationCode"));
+            }
+        }
+    }
+    phosphor::logging::elog<types::DbusInvalidArgument>(
+        types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+        types::InvalidArgument::ARGUMENT_VALUE(
+            i_unexpandedLocationCode.c_str()));
+}
+
+types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
+    const std::string& i_unexpandedLocationCode,
+    [[maybe_unused]] const uint16_t i_nodeNumber)
+{
+    types::ListOfPaths l_inventoryPaths;
+
+    if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
+    {
+        phosphor::logging::elog<types::DbusInvalidArgument>(
+            types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+            types::InvalidArgument::ARGUMENT_VALUE(
+                i_unexpandedLocationCode.c_str()));
+    }
+
+    const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+    if (!l_sysCfgJsonObj.contains("frus"))
+    {
+        logging::logMessage("Missing frus tag in system config JSON");
+    }
+
+    const nlohmann::json& l_listOfFrus =
+        l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+    for (const auto& l_frus : l_listOfFrus.items())
+    {
+        for (const auto& l_aFru : l_frus.value())
+        {
+            if (l_aFru["extraInterfaces"].contains(
+                    constants::locationCodeInf) &&
+                l_aFru["extraInterfaces"][constants::locationCodeInf].value(
+                    "LocationCode", "") == i_unexpandedLocationCode)
+            {
+                l_inventoryPaths.push_back(
+                    l_aFru.at("inventoryPath")
+                        .get_ref<const nlohmann::json::string_t&>());
+            }
+        }
+    }
+
+    if (l_inventoryPaths.empty())
+    {
+        phosphor::logging::elog<types::DbusInvalidArgument>(
+            types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+            types::InvalidArgument::ARGUMENT_VALUE(
+                i_unexpandedLocationCode.c_str()));
+    }
+
+    return l_inventoryPaths;
+}
+
+std::string
+    Manager::getHwPath(const sdbusplus::message::object_path& i_dbusObjPath)
+{
+    // Dummy code to supress unused variable warning. To be removed.
+    logging::logMessage(std::string(i_dbusObjPath));
+
+    return std::string{};
+}
+
+std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
+    const std::string& i_expandedLocationCode)
+{
+    /**
+     * Location code should always start with U and fulfil minimum length
+     * criteria.
+     */
+    if (i_expandedLocationCode[0] != 'U' ||
+        i_expandedLocationCode.length() <
+            constants::EXP_LOCATION_CODE_MIN_LENGTH)
+    {
+        phosphor::logging::elog<types::DbusInvalidArgument>(
+            types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+            types::InvalidArgument::ARGUMENT_VALUE(
+                i_expandedLocationCode.c_str()));
+    }
+
+    std::string l_fcKwd;
+
+    auto l_fcKwdValue = dbusUtility::readDbusProperty(
+        "xyz.openbmc_project.Inventory.Manager",
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard",
+        "com.ibm.ipzvpd.VCEN", "FC");
+
+    if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
+    {
+        l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
+    }
+
+    // Get the first part of expanded location code to check for FC or TM.
+    std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
+
+    std::string l_unexpandedLocationCode{};
+    uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
+
+    // Check if this value matches the value of FC keyword.
+    if (l_fcKwd.substr(0, 4) == l_firstKwd)
+    {
+        /**
+         * Period(.) should be there in expanded location code to seggregate
+         * FC, node number and SE values.
+         */
+        size_t l_nodeStartPos = i_expandedLocationCode.find('.');
+        if (l_nodeStartPos == std::string::npos)
+        {
+            phosphor::logging::elog<types::DbusInvalidArgument>(
+                types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+                types::InvalidArgument::ARGUMENT_VALUE(
+                    i_expandedLocationCode.c_str()));
+        }
+
+        size_t l_nodeEndPos =
+            i_expandedLocationCode.find('.', l_nodeStartPos + 1);
+        if (l_nodeEndPos == std::string::npos)
+        {
+            phosphor::logging::elog<types::DbusInvalidArgument>(
+                types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+                types::InvalidArgument::ARGUMENT_VALUE(
+                    i_expandedLocationCode.c_str()));
+        }
+
+        // Skip 3 bytes for '.ND'
+        l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
+            l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
+
+        /**
+         * Confirm if there are other details apart FC, node number and SE
+         * in location code
+         */
+        if (i_expandedLocationCode.length() >
+            constants::EXP_LOCATION_CODE_MIN_LENGTH)
+        {
+            l_unexpandedLocationCode =
+                i_expandedLocationCode[0] + std::string("fcs") +
+                i_expandedLocationCode.substr(
+                    l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
+                    std::string::npos);
+        }
+        else
+        {
+            l_unexpandedLocationCode = "Ufcs";
+        }
+    }
+    else
+    {
+        std::string l_tmKwd;
+        // Read TM keyword value.
+        auto l_tmKwdValue = dbusUtility::readDbusProperty(
+            "xyz.openbmc_project.Inventory.Manager",
+            "/xyz/openbmc_project/inventory/system/chassis/motherboard",
+            "com.ibm.ipzvpd.VSYS", "TM");
+
+        if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
+        {
+            l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
+        }
+
+        // Check if the substr matches to TM keyword value.
+        if (l_tmKwd.substr(0, 4) == l_firstKwd)
+        {
+            /**
+             * System location code will not have node number and any other
+             * details.
+             */
+            l_unexpandedLocationCode = "Umts";
+        }
+        // The given location code is neither "fcs" or "mts".
+        else
+        {
+            phosphor::logging::elog<types::DbusInvalidArgument>(
+                types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+                types::InvalidArgument::ARGUMENT_VALUE(
+                    i_expandedLocationCode.c_str()));
+        }
+    }
+
+    return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
+}
+
+types::ListOfPaths Manager::getFrusByExpandedLocationCode(
+    const std::string& i_expandedLocationCode)
+{
+    std::tuple<std::string, uint16_t> l_locationAndNodePair =
+        getUnexpandedLocationCode(i_expandedLocationCode);
+
+    return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
+                                           std::get<1>(l_locationAndNodePair));
+}
+
+void Manager::registerHostStateChangeCallback()
+{
+    static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
+        std::make_shared<sdbusplus::bus::match_t>(
+            *m_asioConnection,
+            sdbusplus::bus::match::rules::propertiesChanged(
+                constants::hostObjectPath, constants::hostInterface),
+            [this](sdbusplus::message_t& i_msg) {
+                hostStateChangeCallBack(i_msg);
+            });
+}
+
+void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
+{
+    try
+    {
+        if (i_msg.is_method_error())
+        {
+            throw std::runtime_error(
+                "Error reading callback message for host state");
+        }
+
+        std::string l_objectPath;
+        types::PropertyMap l_propMap;
+        i_msg.read(l_objectPath, l_propMap);
+
+        const auto l_itr = l_propMap.find("CurrentHostState");
+
+        if (l_itr == l_propMap.end())
+        {
+            throw std::runtime_error(
+                "CurrentHostState field is missing in callback message");
+        }
+
+        if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
+        {
+            // implies system is moving from standby to power on state
+            if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
+                                "TransitioningToRunning")
+            {
+                // TODO: check for all the essential FRUs in the system.
+
+                // Perform recollection.
+                performVpdRecollection();
+                return;
+            }
+        }
+        else
+        {
+            throw std::runtime_error(
+                "Invalid type recieved in variant for host state.");
+        }
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO: Log PEL.
+        logging::logMessage(l_ex.what());
+    }
+}
+
+void Manager::performVpdRecollection()
+{
+    try
+    {
+        if (m_worker.get() != nullptr)
+        {
+            nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+
+            // Check if system config JSON is present
+            if (l_sysCfgJsonObj.empty())
+            {
+                throw std::runtime_error(
+                    "System config json object is empty, can't process recollection.");
+            }
+
+            const auto& l_frusReplaceableAtStandby =
+                jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
+
+            for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
+            {
+                // ToDo: Add some logic/trace to know the flow to
+                // collectSingleFruVpd has been directed via
+                // performVpdRecollection.
+                collectSingleFruVpd(l_fruInventoryPath);
+            }
+            return;
+        }
+
+        throw std::runtime_error(
+            "Worker object not found can't process recollection");
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO Log PEL
+        logging::logMessage(
+            "VPD recollection failed with error: " + std::string(l_ex.what()));
+    }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/manager_main.cpp b/vpd-manager/src/manager_main.cpp
new file mode 100644
index 0000000..a1d61e4
--- /dev/null
+++ b/vpd-manager/src/manager_main.cpp
@@ -0,0 +1,94 @@
+#include "config.h"
+
+#include "bios_handler.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "manager.hpp"
+#include "types.hpp"
+
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <iostream>
+
+/**
+ * @brief Main function for VPD parser application.
+ */
+int main(int, char**)
+{
+    try
+    {
+        auto io_con = std::make_shared<boost::asio::io_context>();
+        auto connection =
+            std::make_shared<sdbusplus::asio::connection>(*io_con);
+        auto server = sdbusplus::asio::object_server(connection);
+
+        std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
+            server.add_interface(OBJPATH, IFACE);
+
+        auto vpdManager =
+            std::make_shared<vpd::Manager>(io_con, interface, connection);
+
+        // TODO: Take this under conditional compilation for IBM
+        auto biosHandler =
+            std::make_shared<vpd::BiosHandler<vpd::IbmBiosHandler>>(
+                connection, vpdManager);
+
+        interface->initialize();
+
+        vpd::logging::logMessage("Start VPD-Manager event loop");
+
+        // Grab the bus name
+        connection->request_name(BUSNAME);
+
+        // Start event loop.
+        io_con->run();
+
+        exit(EXIT_SUCCESS);
+    }
+    catch (const std::exception& l_ex)
+    {
+        if (typeid(l_ex) == typeid(vpd::JsonException))
+        {
+            // ToDo: Severity needs to be revisited.
+            vpd::EventLogger::createAsyncPel(
+                vpd::types::ErrorType::JsonFailure,
+                vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+                0,
+                std::string("VPD Manager service failed with : ") + l_ex.what(),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        }
+        else if (typeid(l_ex) == typeid(vpd::GpioException))
+        {
+            // ToDo: Severity needs to be revisited.
+            vpd::EventLogger::createAsyncPel(
+                vpd::types::ErrorType::GpioError,
+                vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+                0,
+                std::string("VPD Manager service failed with : ") + l_ex.what(),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        }
+        else if (typeid(l_ex) == typeid(sdbusplus::exception::SdBusError))
+        {
+            // ToDo: Severity needs to be revisited.
+            vpd::EventLogger::createAsyncPel(
+                vpd::types::ErrorType::DbusFailure,
+                vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+                0,
+                std::string("VPD Manager service failed with : ") + l_ex.what(),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        }
+        else
+        {
+            // ToDo: Severity needs to be revisited.
+            vpd::EventLogger::createAsyncPel(
+                vpd::types::ErrorType::InvalidVpdMessage,
+                vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+                0,
+                std::string("VPD Manager service failed with : ") + l_ex.what(),
+                "BMC0001", std::nullopt, std::nullopt, std::nullopt);
+        }
+    }
+    exit(EXIT_FAILURE);
+}
diff --git a/vpd-manager/src/parser.cpp b/vpd-manager/src/parser.cpp
new file mode 100644
index 0000000..d6266cb
--- /dev/null
+++ b/vpd-manager/src/parser.cpp
@@ -0,0 +1,342 @@
+#include "parser.hpp"
+
+#include "constants.hpp"
+#include "event_logger.hpp"
+
+#include <utility/dbus_utility.hpp>
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+#include <fstream>
+
+namespace vpd
+{
+Parser::Parser(const std::string& vpdFilePath, nlohmann::json parsedJson) :
+    m_vpdFilePath(vpdFilePath), m_parsedJson(parsedJson)
+{
+    std::error_code l_errCode;
+
+    // ToDo: Add minimum file size check in all the concert praser classes,
+    // depends on their VPD type.
+    if (!std::filesystem::exists(m_vpdFilePath, l_errCode))
+    {
+        std::string l_message{"Parser object creation failed, file [" +
+                              m_vpdFilePath + "] doesn't exists."};
+
+        if (l_errCode)
+        {
+            l_message += " Error message: " + l_errCode.message();
+        }
+
+        throw std::runtime_error(l_message);
+    }
+
+    // Read VPD offset if applicable.
+    if (!m_parsedJson.empty())
+    {
+        m_vpdStartOffset = jsonUtility::getVPDOffset(m_parsedJson, vpdFilePath);
+    }
+}
+
+std::shared_ptr<vpd::ParserInterface> Parser::getVpdParserInstance()
+{
+    // Read the VPD data into a vector.
+    vpdSpecificUtility::getVpdDataInVector(m_vpdFilePath, m_vpdVector,
+                                           m_vpdStartOffset);
+
+    // This will detect the type of parser required.
+    std::shared_ptr<vpd::ParserInterface> l_parser =
+        ParserFactory::getParser(m_vpdVector, m_vpdFilePath, m_vpdStartOffset);
+
+    return l_parser;
+}
+
+types::VPDMapVariant Parser::parse()
+{
+    std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance();
+    return l_parser->parse();
+}
+
+int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
+{
+    int l_bytesUpdatedOnHardware = constants::FAILURE;
+
+    // A lambda to extract Record : Keyword string from i_paramsToWriteData
+    auto l_keyWordIdentifier =
+        [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
+        std::string l_keywordString{};
+        if (const types::IpzData* l_ipzData =
+                std::get_if<types::IpzData>(&i_paramsToWriteData))
+        {
+            l_keywordString =
+                std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
+        }
+        else if (const types::KwData* l_kwData =
+                     std::get_if<types::KwData>(&i_paramsToWriteData))
+        {
+            l_keywordString = std::get<0>(*l_kwData);
+        }
+        return l_keywordString;
+    };
+
+    try
+    {
+        // Enable Reboot Guard
+        if (constants::FAILURE == dbusUtility::EnableRebootGuard())
+        {
+            EventLogger::createAsyncPel(
+                types::ErrorType::DbusFailure,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                std::string(
+                    "Failed to enable BMC Reboot Guard while updating " +
+                    l_keyWordIdentifier(i_paramsToWriteData)),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+            return constants::FAILURE;
+        }
+
+        // Update keyword's value on hardware
+        try
+        {
+            std::shared_ptr<ParserInterface> l_vpdParserInstance =
+                getVpdParserInstance();
+            l_bytesUpdatedOnHardware =
+                l_vpdParserInstance->writeKeywordOnHardware(
+                    i_paramsToWriteData);
+        }
+        catch (const std::exception& l_exception)
+        {
+            std::string l_errMsg(
+                "Error while updating keyword's value on hardware path " +
+                m_vpdFilePath + ", error: " + std::string(l_exception.what()));
+
+            // TODO : Log PEL
+
+            throw std::runtime_error(l_errMsg);
+        }
+
+        auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] =
+            jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson,
+                                                    m_vpdFilePath);
+
+        // If inventory D-bus object path is present, update keyword's value on
+        // DBus
+        if (!l_inventoryObjPath.empty())
+        {
+            types::Record l_recordName;
+            std::string l_interfaceName;
+            std::string l_propertyName;
+            types::DbusVariantType l_keywordValue;
+
+            if (const types::IpzData* l_ipzData =
+                    std::get_if<types::IpzData>(&i_paramsToWriteData))
+            {
+                l_recordName = std::get<0>(*l_ipzData);
+                l_interfaceName = constants::ipzVpdInf + l_recordName;
+                l_propertyName = std::get<1>(*l_ipzData);
+
+                try
+                {
+                    // Read keyword's value from hardware to write the same on
+                    // D-bus.
+                    std::shared_ptr<ParserInterface> l_vpdParserInstance =
+                        getVpdParserInstance();
+
+                    logging::logMessage(
+                        "Performing VPD read on " + m_vpdFilePath);
+
+                    l_keywordValue =
+                        l_vpdParserInstance->readKeywordFromHardware(
+                            types::ReadVpdParams(
+                                std::make_tuple(l_recordName, l_propertyName)));
+                }
+                catch (const std::exception& l_exception)
+                {
+                    // Unable to read keyword's value from hardware.
+                    std::string l_errMsg(
+                        "Error while reading keyword's value from hadware path " +
+                        m_vpdFilePath +
+                        ", error: " + std::string(l_exception.what()));
+
+                    // TODO: Log PEL
+
+                    throw std::runtime_error(l_errMsg);
+                }
+            }
+            else
+            {
+                // Input parameter type provided isn't compatible to perform
+                // update.
+                std::string l_errMsg(
+                    "Input parameter type isn't compatible to update keyword's value on DBus for object path: " +
+                    l_inventoryObjPath);
+                throw std::runtime_error(l_errMsg);
+            }
+
+            // Get D-bus name for the given keyword
+            l_propertyName =
+                vpdSpecificUtility::getDbusPropNameForGivenKw(l_propertyName);
+
+            // Create D-bus object map
+            types::ObjectMap l_dbusObjMap = {std::make_pair(
+                l_inventoryObjPath,
+                types::InterfaceMap{std::make_pair(
+                    l_interfaceName, types::PropertyMap{std::make_pair(
+                                         l_propertyName, l_keywordValue)})})};
+
+            // Call PIM's Notify method to perform update
+            if (!dbusUtility::callPIM(std::move(l_dbusObjMap)))
+            {
+                // Call to PIM's Notify method failed.
+                std::string l_errMsg("Notify PIM is failed for object path: " +
+                                     l_inventoryObjPath);
+                throw std::runtime_error(l_errMsg);
+            }
+        }
+
+        // Update keyword's value on redundant hardware if present
+        if (!l_redundantFruPath.empty())
+        {
+            if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
+                                                i_paramsToWriteData) < 0)
+            {
+                std::string l_errMsg(
+                    "Error while updating keyword's value on redundant path " +
+                    l_redundantFruPath);
+                throw std::runtime_error(l_errMsg);
+            }
+        }
+
+        // TODO: Check if revert is required when any of the writes fails.
+        // TODO: Handle error logging
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage("Update VPD Keyword failed for : " +
+                            l_keyWordIdentifier(i_paramsToWriteData) +
+                            " failed due to error: " + l_ex.what());
+
+        // update failed, set return value to failure
+        l_bytesUpdatedOnHardware = constants::FAILURE;
+    }
+
+    // Disable Reboot Guard
+    if (constants::FAILURE == dbusUtility::DisableRebootGuard())
+    {
+        EventLogger::createAsyncPel(
+            types::ErrorType::DbusFailure, types::SeverityType::Critical,
+            __FILE__, __FUNCTION__, 0,
+            std::string("Failed to disable BMC Reboot Guard while updating " +
+                        l_keyWordIdentifier(i_paramsToWriteData)),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+
+    return l_bytesUpdatedOnHardware;
+}
+
+int Parser::updateVpdKeywordOnRedundantPath(
+    const std::string& i_fruPath,
+    const types::WriteVpdParams& i_paramsToWriteData)
+{
+    try
+    {
+        std::shared_ptr<Parser> l_parserObj =
+            std::make_shared<Parser>(i_fruPath, m_parsedJson);
+
+        std::shared_ptr<ParserInterface> l_vpdParserInstance =
+            l_parserObj->getVpdParserInstance();
+
+        return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
+    }
+    catch (const std::exception& l_exception)
+    {
+        EventLogger::createSyncPel(
+            types::ErrorType::InvalidVpdMessage,
+            types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+            "Error while updating keyword's value on redundant path " +
+                i_fruPath + ", error: " + std::string(l_exception.what()),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        return -1;
+    }
+}
+
+int Parser::updateVpdKeywordOnHardware(
+    const types::WriteVpdParams& i_paramsToWriteData)
+{
+    int l_bytesUpdatedOnHardware = constants::FAILURE;
+
+    // A lambda to extract Record : Keyword string from i_paramsToWriteData
+    auto l_keyWordIdentifier =
+        [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
+        std::string l_keywordString{};
+        if (const types::IpzData* l_ipzData =
+                std::get_if<types::IpzData>(&i_paramsToWriteData))
+        {
+            l_keywordString =
+                std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
+        }
+        else if (const types::KwData* l_kwData =
+                     std::get_if<types::KwData>(&i_paramsToWriteData))
+        {
+            l_keywordString = std::get<0>(*l_kwData);
+        }
+        return l_keywordString;
+    };
+
+    try
+    {
+        // Enable Reboot Guard
+        if (constants::FAILURE == dbusUtility::EnableRebootGuard())
+        {
+            EventLogger::createAsyncPel(
+                types::ErrorType::DbusFailure,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                std::string(
+                    "Failed to enable BMC Reboot Guard while updating " +
+                    l_keyWordIdentifier(i_paramsToWriteData)),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+            return constants::FAILURE;
+        }
+
+        std::shared_ptr<ParserInterface> l_vpdParserInstance =
+            getVpdParserInstance();
+        l_bytesUpdatedOnHardware =
+            l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
+    }
+    catch (const std::exception& l_exception)
+    {
+        types::ErrorType l_errorType;
+
+        if (typeid(l_exception) == typeid(EccException))
+        {
+            l_errorType = types::ErrorType::EccCheckFailed;
+        }
+        else
+        {
+            l_errorType = types::ErrorType::InvalidVpdMessage;
+        }
+
+        EventLogger::createAsyncPel(
+            l_errorType, types::SeverityType::Informational, __FILE__,
+            __FUNCTION__, 0,
+            "Error while updating keyword's value on hardware path [" +
+                m_vpdFilePath + "], error: " + std::string(l_exception.what()),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+
+    // Disable Reboot Guard
+    if (constants::FAILURE == dbusUtility::DisableRebootGuard())
+    {
+        EventLogger::createAsyncPel(
+            types::ErrorType::DbusFailure, types::SeverityType::Critical,
+            __FILE__, __FUNCTION__, 0,
+            std::string("Failed to disable BMC Reboot Guard while updating " +
+                        l_keyWordIdentifier(i_paramsToWriteData)),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+
+    return l_bytesUpdatedOnHardware;
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/parser_factory.cpp b/vpd-manager/src/parser_factory.cpp
new file mode 100644
index 0000000..546e67b
--- /dev/null
+++ b/vpd-manager/src/parser_factory.cpp
@@ -0,0 +1,140 @@
+#include "parser_factory.hpp"
+
+#include "constants.hpp"
+#include "ddimm_parser.hpp"
+#include "exceptions.hpp"
+#include "ipz_parser.hpp"
+#include "isdimm_parser.hpp"
+#include "keyword_vpd_parser.hpp"
+
+namespace vpd
+{
+
+/**
+ * @brief Type of VPD formats.
+ */
+enum vpdType
+{
+    IPZ_VPD,                /**< IPZ VPD type */
+    KEYWORD_VPD,            /**< Keyword VPD type */
+    DDR4_DDIMM_MEMORY_VPD,  /**< DDR4 DDIMM Memory VPD type */
+    DDR5_DDIMM_MEMORY_VPD,  /**< DDR5 DDIMM Memory VPD type */
+    DDR4_ISDIMM_MEMORY_VPD, /**< DDR4 ISDIMM Memory VPD type */
+    DDR5_ISDIMM_MEMORY_VPD, /**< DDR5 ISDIMM Memory VPD type */
+    INVALID_VPD_FORMAT      /**< Invalid VPD type */
+};
+
+/**
+ * @brief API to get the type of VPD.
+ *
+ * @param[in] i_vpdVector - VPD file content
+ *
+ * @return Type of VPD data, "INVALID_VPD_FORMAT" in case of unknown type.
+ */
+static vpdType vpdTypeCheck(const types::BinaryVector& i_vpdVector)
+{
+    if (i_vpdVector[constants::IPZ_DATA_START] == constants::IPZ_DATA_START_TAG)
+    {
+        return vpdType::IPZ_VPD;
+    }
+    else if (i_vpdVector[constants::KW_VPD_DATA_START] ==
+             constants::KW_VPD_START_TAG)
+    {
+        return vpdType::KEYWORD_VPD;
+    }
+    else if (((i_vpdVector[constants::SPD_BYTE_3] &
+               constants::SPD_BYTE_BIT_0_3_MASK) ==
+              constants::SPD_MODULE_TYPE_DDIMM))
+    {
+        std::string l_is11SFormat;
+        if (i_vpdVector.size() > (constants::DDIMM_11S_BARCODE_START +
+                                  constants::DDIMM_11S_BARCODE_LEN))
+        {
+            // Read first 3 Bytes to check the 11S bar code format
+            for (uint8_t l_index = 0; l_index < constants::DDIMM_11S_FORMAT_LEN;
+                 l_index++)
+            {
+                l_is11SFormat +=
+                    i_vpdVector[constants::DDIMM_11S_BARCODE_START + l_index];
+            }
+        }
+
+        if (l_is11SFormat.compare(constants::DDIMM_11S_BARCODE_START_TAG) == 0)
+        {
+            // DDIMM memory VPD format
+            if ((i_vpdVector[constants::SPD_BYTE_2] &
+                 constants::SPD_BYTE_MASK) == constants::SPD_DRAM_TYPE_DDR5)
+            {
+                return vpdType::DDR5_DDIMM_MEMORY_VPD;
+            }
+
+            if ((i_vpdVector[constants::SPD_BYTE_2] &
+                 constants::SPD_BYTE_MASK) == constants::SPD_DRAM_TYPE_DDR4)
+            {
+                return vpdType::DDR4_DDIMM_MEMORY_VPD;
+            }
+        }
+
+        logging::logMessage("11S format is not found in the DDIMM VPD.");
+        return vpdType::INVALID_VPD_FORMAT;
+    }
+    else if ((i_vpdVector[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
+             constants::SPD_DRAM_TYPE_DDR5)
+    {
+        // ISDIMM memory VPD format
+        return vpdType::DDR5_ISDIMM_MEMORY_VPD;
+    }
+    else if ((i_vpdVector[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
+             constants::SPD_DRAM_TYPE_DDR4)
+    {
+        // ISDIMM memory VPD format
+        return vpdType::DDR4_ISDIMM_MEMORY_VPD;
+    }
+
+    return vpdType::INVALID_VPD_FORMAT;
+}
+
+std::shared_ptr<ParserInterface> ParserFactory::getParser(
+    const types::BinaryVector& i_vpdVector, const std::string& i_vpdFilePath,
+    size_t i_vpdStartOffset)
+{
+    if (i_vpdVector.empty())
+    {
+        throw std::runtime_error("Empty VPD vector passed to parser factory");
+    }
+
+    vpdType l_type = vpdTypeCheck(i_vpdVector);
+
+    switch (l_type)
+    {
+        case vpdType::IPZ_VPD:
+        {
+            return std::make_shared<IpzVpdParser>(i_vpdVector, i_vpdFilePath,
+                                                  i_vpdStartOffset);
+        }
+
+        case vpdType::KEYWORD_VPD:
+        {
+            return std::make_shared<KeywordVpdParser>(i_vpdVector);
+        }
+
+        case vpdType::DDR5_DDIMM_MEMORY_VPD:
+        case vpdType::DDR4_DDIMM_MEMORY_VPD:
+        {
+            return std::make_shared<DdimmVpdParser>(i_vpdVector);
+        }
+
+        case vpdType::DDR4_ISDIMM_MEMORY_VPD:
+        case vpdType::DDR5_ISDIMM_MEMORY_VPD:
+        {
+            // return shared pointer to class object.
+            logging::logMessage(
+                "ISDIMM parser selected for VPD path: " + i_vpdFilePath);
+            return std::make_shared<JedecSpdParser>(i_vpdVector);
+        }
+
+        default:
+            throw DataException("Unable to determine VPD format");
+    }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/vpd_parser_main.cpp b/vpd-manager/src/vpd_parser_main.cpp
new file mode 100644
index 0000000..0c434d8
--- /dev/null
+++ b/vpd-manager/src/vpd_parser_main.cpp
@@ -0,0 +1,101 @@
+#include "logger.hpp"
+#include "parser.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+#include "worker.hpp"
+
+#include <CLI/CLI.hpp>
+#include <nlohmann/json.hpp>
+#include <parser_factory.hpp>
+
+#include <filesystem>
+#include <iostream>
+
+/**
+ * @brief This file implements a generic parser APP.
+ *
+ * It recieves path of the VPD file(mandatory) and path to a config
+ * file(optional) as arguments. It will parse the data and return parsed data in
+ * a required format.
+ *
+ * Steps to get parsed VPD.
+ * - Pass VPD file path and config file (if applicable).
+ * - Read VPD file to vector.
+ * - Pass that to parser_factory to get the parser and call parse API on that
+ * parser object to get the Parsed VPD map.
+ * - If VPD format is other than the existing formats. Follow the steps
+ * - a) Add logic in parser_factory.cpp, vpdTypeCheck API to detect the format.
+ * - b) Implement a custom parser class.
+ * - c) Override parse API in the newly added parser class.
+ * - d) Add type of parsed data returned by parse API into types.hpp,
+ * "VPDMapVariant".
+ *
+ */
+
+int main(int argc, char** argv)
+{
+    try
+    {
+        std::string vpdFilePath{};
+        CLI::App app{"VPD-parser-app - APP to parse VPD. "};
+
+        app.add_option("-f, --file", vpdFilePath, "VPD file path")->required();
+
+        std::string configFilePath{};
+
+        app.add_option("-c,--config", configFilePath, "Path to JSON config");
+
+        CLI11_PARSE(app, argc, argv);
+
+        vpd::logging::logMessage("VPD file path recieved" + vpdFilePath);
+
+        // VPD file path is a mandatory parameter to execute any parser.
+        if (vpdFilePath.empty())
+        {
+            throw std::runtime_error("Empty VPD file path");
+        }
+
+        nlohmann::json json;
+        vpd::types::VPDMapVariant parsedVpdDataMap;
+
+        // Below are two different ways of parsing the VPD.
+        if (!configFilePath.empty())
+        {
+            vpd::logging::logMessage(
+                "Processing with config file - " + configFilePath);
+
+            std::shared_ptr<vpd::Worker> objWorker =
+                std::make_shared<vpd::Worker>(configFilePath);
+            parsedVpdDataMap = objWorker->parseVpdFile(vpdFilePath);
+
+            // Based on requirement, call appropriate public API of worker class
+            /*If required to publish the FRU data on Dbus*/
+            // objWorker->publishFruDataOnDbus(parsedVpdDataMap);
+        }
+        else
+        {
+            // Will work with empty JSON
+            std::shared_ptr<vpd::Parser> vpdParser =
+                std::make_shared<vpd::Parser>(vpdFilePath, json);
+            parsedVpdDataMap = vpdParser->parse();
+        }
+
+        // If custom handling is required then custom logic to be implemented
+        // based on the type of variant,
+        //  eg: for IPZ VPD format
+        if (auto ipzVpdMap =
+                std::get_if<vpd::types::IPZVpdMap>(&parsedVpdDataMap))
+        {
+            // get rid of unused variable warning/error
+            (void)ipzVpdMap;
+            // implement code that needs to handle parsed IPZ VPD.
+        }
+    }
+    catch (const std::exception& ex)
+    {
+        vpd::logging::logMessage(ex.what());
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/vpd-manager/src/worker.cpp b/vpd-manager/src/worker.cpp
new file mode 100644
index 0000000..2cf8cb5
--- /dev/null
+++ b/vpd-manager/src/worker.cpp
@@ -0,0 +1,1677 @@
+#include "config.h"
+
+#include "worker.hpp"
+
+#include "backup_restore.hpp"
+#include "configuration.hpp"
+#include "constants.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "parser_factory.hpp"
+#include "parser_interface.hpp"
+
+#include <utility/dbus_utility.hpp>
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <future>
+#include <typeindex>
+#include <unordered_set>
+
+namespace vpd
+{
+
+Worker::Worker(std::string pathToConfigJson) :
+    m_configJsonPath(pathToConfigJson)
+{
+    // Implies the processing is based on some config JSON
+    if (!m_configJsonPath.empty())
+    {
+        // Check if symlink is already there to confirm fresh boot/factory
+        // reset.
+        if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK))
+        {
+            logging::logMessage("Sym Link already present");
+            m_configJsonPath = INVENTORY_JSON_SYM_LINK;
+            m_isSymlinkPresent = true;
+        }
+
+        try
+        {
+            m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath);
+
+            // check for mandatory fields at this point itself.
+            if (!m_parsedJson.contains("frus"))
+            {
+                throw std::runtime_error("Mandatory tag(s) missing from JSON");
+            }
+        }
+        catch (const std::exception& ex)
+        {
+            throw(JsonException(ex.what(), m_configJsonPath));
+        }
+    }
+    else
+    {
+        logging::logMessage("Processing in not based on any config JSON");
+    }
+}
+
+void Worker::enableMuxChips()
+{
+    if (m_parsedJson.empty())
+    {
+        // config JSON should not be empty at this point of execution.
+        throw std::runtime_error("Config JSON is empty. Can't enable muxes");
+        return;
+    }
+
+    if (!m_parsedJson.contains("muxes"))
+    {
+        logging::logMessage("No mux defined for the system in config JSON");
+        return;
+    }
+
+    // iterate over each MUX detail and enable them.
+    for (const auto& item : m_parsedJson["muxes"])
+    {
+        if (item.contains("holdidlepath"))
+        {
+            std::string cmd = "echo 0 > ";
+            cmd += item["holdidlepath"];
+
+            logging::logMessage("Enabling mux with command = " + cmd);
+
+            commonUtility::executeCmd(cmd);
+            continue;
+        }
+
+        logging::logMessage(
+            "Mux Entry does not have hold idle path. Can't enable the mux");
+    }
+}
+
+#ifdef IBM_SYSTEM
+void Worker::primeSystemBlueprint()
+{
+    if (m_parsedJson.empty())
+    {
+        return;
+    }
+
+    const nlohmann::json& l_listOfFrus =
+        m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
+
+    for (const auto& l_itemFRUS : l_listOfFrus.items())
+    {
+        const std::string& l_vpdFilePath = l_itemFRUS.key();
+
+        if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
+        {
+            continue;
+        }
+
+        // Prime the inventry for FRUs which
+        // are not present/processing had some error.
+        if (!primeInventory(l_vpdFilePath))
+        {
+            logging::logMessage(
+                "Priming of inventory failed for FRU " + l_vpdFilePath);
+        }
+    }
+}
+
+void Worker::performInitialSetup()
+{
+    try
+    {
+        if (!dbusUtility::isChassisPowerOn())
+        {
+            logging::logMessage("Chassis is in Off state.");
+            setDeviceTreeAndJson();
+            primeSystemBlueprint();
+        }
+
+        // Enable all mux which are used for connecting to the i2c on the
+        // pcie slots for pcie cards. These are not enabled by kernel due to
+        // an issue seen with Castello cards, where the i2c line hangs on a
+        // probe.
+        enableMuxChips();
+
+        // Nothing needs to be done. Service restarted or BMC re-booted for
+        // some reason at system power on.
+        return;
+    }
+    catch (const std::exception& ex)
+    {
+        if (typeid(ex) == std::type_index(typeid(DataException)))
+        {
+            // TODO:Catch logic to be implemented once PEL code goes in.
+        }
+        else if (typeid(ex) == std::type_index(typeid(EccException)))
+        {
+            // TODO:Catch logic to be implemented once PEL code goes in.
+        }
+        else if (typeid(ex) == std::type_index(typeid(JsonException)))
+        {
+            // TODO:Catch logic to be implemented once PEL code goes in.
+        }
+
+        logging::logMessage(ex.what());
+        throw;
+    }
+}
+#endif
+
+static std::string readFitConfigValue()
+{
+    std::vector<std::string> output =
+        commonUtility::executeCmd("/sbin/fw_printenv");
+    std::string fitConfigValue;
+
+    for (const auto& entry : output)
+    {
+        auto pos = entry.find("=");
+        auto key = entry.substr(0, pos);
+        if (key != "fitconfig")
+        {
+            continue;
+        }
+
+        if (pos + 1 < entry.size())
+        {
+            fitConfigValue = entry.substr(pos + 1);
+        }
+    }
+
+    return fitConfigValue;
+}
+
+bool Worker::isSystemVPDOnDBus() const
+{
+    const std::string& mboardPath =
+        m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
+            "inventoryPath", "");
+
+    if (mboardPath.empty())
+    {
+        throw JsonException("System vpd file path missing in JSON",
+                            INVENTORY_JSON_SYM_LINK);
+    }
+
+    std::array<const char*, 1> interfaces = {
+        "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
+
+    const types::MapperGetObject& objectMap =
+        dbusUtility::getObjectMap(mboardPath, interfaces);
+
+    if (objectMap.empty())
+    {
+        return false;
+    }
+    return true;
+}
+
+std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
+{
+    if (parsedVpd.empty())
+    {
+        throw std::runtime_error("Empty VPD map. Can't Extract IM value");
+    }
+
+    const auto& itrToVSBP = parsedVpd.find("VSBP");
+    if (itrToVSBP == parsedVpd.end())
+    {
+        throw DataException("VSBP record missing.");
+    }
+
+    const auto& itrToIM = (itrToVSBP->second).find("IM");
+    if (itrToIM == (itrToVSBP->second).end())
+    {
+        throw DataException("IM keyword missing.");
+    }
+
+    types::BinaryVector imVal;
+    std::copy(itrToIM->second.begin(), itrToIM->second.end(),
+              back_inserter(imVal));
+
+    std::ostringstream imData;
+    for (auto& aByte : imVal)
+    {
+        imData << std::setw(2) << std::setfill('0') << std::hex
+               << static_cast<int>(aByte);
+    }
+
+    return imData.str();
+}
+
+std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
+{
+    if (parsedVpd.empty())
+    {
+        throw std::runtime_error("Empty VPD map. Can't Extract HW value");
+    }
+
+    const auto& itrToVINI = parsedVpd.find("VINI");
+    if (itrToVINI == parsedVpd.end())
+    {
+        throw DataException("VINI record missing.");
+    }
+
+    const auto& itrToHW = (itrToVINI->second).find("HW");
+    if (itrToHW == (itrToVINI->second).end())
+    {
+        throw DataException("HW keyword missing.");
+    }
+
+    types::BinaryVector hwVal;
+    std::copy(itrToHW->second.begin(), itrToHW->second.end(),
+              back_inserter(hwVal));
+
+    // The planar pass only comes from the LSB of the HW keyword,
+    // where as the MSB is used for other purposes such as signifying clock
+    // termination.
+    hwVal[0] = 0x00;
+
+    std::ostringstream hwString;
+    for (auto& aByte : hwVal)
+    {
+        hwString << std::setw(2) << std::setfill('0') << std::hex
+                 << static_cast<int>(aByte);
+    }
+
+    return hwString.str();
+}
+
+void Worker::fillVPDMap(const std::string& vpdFilePath,
+                        types::VPDMapVariant& vpdMap)
+{
+    logging::logMessage(std::string("Parsing file = ") + vpdFilePath);
+
+    if (vpdFilePath.empty())
+    {
+        throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
+    }
+
+    if (!std::filesystem::exists(vpdFilePath))
+    {
+        throw std::runtime_error("Can't Find physical file");
+    }
+
+    try
+    {
+        std::shared_ptr<Parser> vpdParser =
+            std::make_shared<Parser>(vpdFilePath, m_parsedJson);
+        vpdMap = vpdParser->parse();
+    }
+    catch (const std::exception& ex)
+    {
+        if (typeid(ex) == std::type_index(typeid(DataException)))
+        {
+            // TODO: Do what needs to be done in case of Data exception.
+            // Uncomment when PEL implementation goes in.
+            /* string errorMsg =
+                 "VPD file is either empty or invalid. Parser failed for [";
+             errorMsg += m_vpdFilePath;
+             errorMsg += "], with error = " + std::string(ex.what());
+
+             additionalData.emplace("DESCRIPTION", errorMsg);
+             additionalData.emplace("CALLOUT_INVENTORY_PATH",
+                                    INVENTORY_PATH + baseFruInventoryPath);
+             createPEL(additionalData, pelSeverity, errIntfForInvalidVPD,
+             nullptr);*/
+
+            // throw generic error from here to inform main caller about
+            // failure.
+            logging::logMessage(ex.what());
+            throw std::runtime_error(
+                "Data Exception occurred for file path = " + vpdFilePath);
+        }
+
+        if (typeid(ex) == std::type_index(typeid(EccException)))
+        {
+            // TODO: Do what needs to be done in case of ECC exception.
+            // Uncomment when PEL implementation goes in.
+            /* additionalData.emplace("DESCRIPTION", "ECC check failed");
+             additionalData.emplace("CALLOUT_INVENTORY_PATH",
+                                    INVENTORY_PATH + baseFruInventoryPath);
+             createPEL(additionalData, pelSeverity, errIntfForEccCheckFail,
+                       nullptr);
+             */
+
+            logging::logMessage(ex.what());
+            // Need to decide once all error handling is implemented.
+            // vpdSpecificUtility::dumpBadVpd(vpdFilePath,vpdVector);
+
+            // throw generic error from here to inform main caller about
+            // failure.
+            throw std::runtime_error(
+                "Ecc Exception occurred for file path = " + vpdFilePath);
+        }
+    }
+}
+
+void Worker::getSystemJson(std::string& systemJson,
+                           const types::VPDMapVariant& parsedVpdMap)
+{
+    if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+    {
+        std::string hwKWdValue = getHWVersion(*pVal);
+        if (hwKWdValue.empty())
+        {
+            throw DataException("HW value fetched is empty.");
+        }
+
+        const std::string& imKwdValue = getIMValue(*pVal);
+        if (imKwdValue.empty())
+        {
+            throw DataException("IM value fetched is empty.");
+        }
+
+        auto itrToIM = config::systemType.find(imKwdValue);
+        if (itrToIM == config::systemType.end())
+        {
+            throw DataException("IM keyword does not map to any system type");
+        }
+
+        const types::HWVerList hwVersionList = itrToIM->second.second;
+        if (!hwVersionList.empty())
+        {
+            transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
+                      ::toupper);
+
+            auto itrToHW =
+                std::find_if(hwVersionList.begin(), hwVersionList.end(),
+                             [&hwKWdValue](const auto& aPair) {
+                                 return aPair.first == hwKWdValue;
+                             });
+
+            if (itrToHW != hwVersionList.end())
+            {
+                if (!(*itrToHW).second.empty())
+                {
+                    systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
+                                  ".json";
+                }
+                else
+                {
+                    systemJson += (*itrToIM).first + ".json";
+                }
+                return;
+            }
+        }
+        systemJson += itrToIM->second.first + ".json";
+        return;
+    }
+
+    throw DataException("Invalid VPD type returned from Parser");
+}
+
+static void setEnvAndReboot(const std::string& key, const std::string& value)
+{
+    // set env and reboot and break.
+    commonUtility::executeCmd("/sbin/fw_setenv", key, value);
+    logging::logMessage("Rebooting BMC to pick up new device tree");
+
+    // make dbus call to reboot
+    auto bus = sdbusplus::bus::new_default_system();
+    auto method = bus.new_method_call(
+        "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
+        "org.freedesktop.systemd1.Manager", "Reboot");
+    bus.call_noreply(method);
+}
+
+void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
+{
+    std::error_code l_ec;
+    l_ec.clear();
+    if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
+    {
+        if (l_ec)
+        {
+            throw std::runtime_error(
+                "File system call to exist failed with error = " +
+                l_ec.message());
+        }
+
+        // implies it is a fresh boot/factory reset.
+        // Create the directory for hosting the symlink
+        if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
+        {
+            if (l_ec)
+            {
+                throw std::runtime_error(
+                    "File system call to create directory failed with error = " +
+                    l_ec.message());
+            }
+        }
+    }
+
+    // create a new symlink based on the system
+    std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
+                                    l_ec);
+
+    if (l_ec)
+    {
+        throw std::runtime_error(
+            "create_symlink system call failed with error: " + l_ec.message());
+    }
+
+    // If the flow is at this point implies the symlink was not present there.
+    // Considering this as factory reset.
+    m_isFactoryResetDone = true;
+}
+
+void Worker::setDeviceTreeAndJson()
+{
+    // JSON is madatory for processing of this API.
+    if (m_parsedJson.empty())
+    {
+        throw std::runtime_error("JSON is empty");
+    }
+
+    types::VPDMapVariant parsedVpdMap;
+    fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
+
+    // Implies it is default JSON.
+    std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
+
+    // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
+    // This is required to support movement from rainier to Blue Ridge on the
+    // fly.
+
+    // Do we have the entry for device tree in parsed JSON?
+    if (m_parsedJson.find("devTree") == m_parsedJson.end())
+    {
+        getSystemJson(systemJson, parsedVpdMap);
+
+        if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
+        {
+            // TODO: Log a PEL saying that "System type not supported"
+            throw DataException("Error in getting system JSON.");
+        }
+
+        // re-parse the JSON once appropriate JSON has been selected.
+        try
+        {
+            m_parsedJson = jsonUtility::getParsedJson(systemJson);
+        }
+        catch (const nlohmann::json::parse_error& ex)
+        {
+            throw(JsonException("Json parsing failed", systemJson));
+        }
+    }
+
+    std::string devTreeFromJson;
+    if (m_parsedJson.contains("devTree"))
+    {
+        devTreeFromJson = m_parsedJson["devTree"];
+
+        if (devTreeFromJson.empty())
+        {
+            // TODO:: Log a predictive PEL
+            logging::logMessage(
+                "Mandatory value for device tree missing from JSON[" +
+                std::string(INVENTORY_JSON_SYM_LINK) + "]");
+        }
+    }
+
+    auto fitConfigVal = readFitConfigValue();
+
+    if (devTreeFromJson.empty() ||
+        fitConfigVal.find(devTreeFromJson) != std::string::npos)
+    { // Skipping setting device tree as either devtree info is missing from
+      // Json or it is rightly set.
+
+        // avoid setting symlink on every reboot.
+        if (!m_isSymlinkPresent)
+        {
+            setJsonSymbolicLink(systemJson);
+        }
+
+        if (isSystemVPDOnDBus() &&
+            jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
+        {
+            performBackupAndRestore(parsedVpdMap);
+        }
+
+        // proceed to publish system VPD.
+        publishSystemVPD(parsedVpdMap);
+        return;
+    }
+
+    setEnvAndReboot("fitconfig", devTreeFromJson);
+    exit(EXIT_SUCCESS);
+}
+
+void Worker::populateIPZVPDpropertyMap(
+    types::InterfaceMap& interfacePropMap,
+    const types::IPZKwdValueMap& keyordValueMap,
+    const std::string& interfaceName)
+{
+    types::PropertyMap propertyValueMap;
+    for (const auto& kwdVal : keyordValueMap)
+    {
+        auto kwd = kwdVal.first;
+
+        if (kwd[0] == '#')
+        {
+            kwd = std::string("PD_") + kwd[1];
+        }
+        else if (isdigit(kwd[0]))
+        {
+            kwd = std::string("N_") + kwd;
+        }
+
+        types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
+        propertyValueMap.emplace(move(kwd), move(value));
+    }
+
+    if (!propertyValueMap.empty())
+    {
+        interfacePropMap.emplace(interfaceName, propertyValueMap);
+    }
+}
+
+void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
+                                       types::InterfaceMap& interfaceMap)
+{
+    for (const auto& kwdValMap : keyordVPDMap)
+    {
+        types::PropertyMap propertyValueMap;
+        auto kwd = kwdValMap.first;
+
+        if (kwd[0] == '#')
+        {
+            kwd = std::string("PD_") + kwd[1];
+        }
+        else if (isdigit(kwd[0]))
+        {
+            kwd = std::string("N_") + kwd;
+        }
+
+        if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
+        {
+            types::BinaryVector value((*keywordValue).begin(),
+                                      (*keywordValue).end());
+            propertyValueMap.emplace(move(kwd), move(value));
+        }
+        else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
+        {
+            types::BinaryVector value((*keywordValue).begin(),
+                                      (*keywordValue).end());
+            propertyValueMap.emplace(move(kwd), move(value));
+        }
+        else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
+        {
+            if (kwd == "MemorySizeInKB")
+            {
+                types::PropertyMap memProp;
+                memProp.emplace(move(kwd), ((*keywordValue)));
+                interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
+                                     move(memProp));
+                continue;
+            }
+            else
+            {
+                logging::logMessage(
+                    "Unknown Keyword =" + kwd + " found in keyword VPD map");
+                continue;
+            }
+        }
+        else
+        {
+            logging::logMessage(
+                "Unknown variant type found in keyword VPD map.");
+            continue;
+        }
+
+        if (!propertyValueMap.empty())
+        {
+            vpdSpecificUtility::insertOrMerge(
+                interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
+        }
+    }
+}
+
+void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
+                                types::InterfaceMap& interfaceMap,
+                                const types::VPDMapVariant& parsedVpdMap)
+{
+    for (const auto& interfacesPropPair : interfaceJson.items())
+    {
+        const std::string& interface = interfacesPropPair.key();
+        types::PropertyMap propertyMap;
+
+        for (const auto& propValuePair : interfacesPropPair.value().items())
+        {
+            const std::string property = propValuePair.key();
+
+            if (propValuePair.value().is_boolean())
+            {
+                propertyMap.emplace(property,
+                                    propValuePair.value().get<bool>());
+            }
+            else if (propValuePair.value().is_string())
+            {
+                if (property.compare("LocationCode") == 0 &&
+                    interface.compare("com.ibm.ipzvpd.Location") == 0)
+                {
+                    std::string value =
+                        vpdSpecificUtility::getExpandedLocationCode(
+                            propValuePair.value().get<std::string>(),
+                            parsedVpdMap);
+                    propertyMap.emplace(property, value);
+
+                    auto l_locCodeProperty = propertyMap;
+                    vpdSpecificUtility::insertOrMerge(
+                        interfaceMap,
+                        std::string(constants::xyzLocationCodeInf),
+                        move(l_locCodeProperty));
+                }
+                else
+                {
+                    propertyMap.emplace(
+                        property, propValuePair.value().get<std::string>());
+                }
+            }
+            else if (propValuePair.value().is_array())
+            {
+                try
+                {
+                    propertyMap.emplace(
+                        property,
+                        propValuePair.value().get<types::BinaryVector>());
+                }
+                catch (const nlohmann::detail::type_error& e)
+                {
+                    std::cerr << "Type exception: " << e.what() << "\n";
+                }
+            }
+            else if (propValuePair.value().is_number())
+            {
+                // For now assume the value is a size_t.  In the future it would
+                // be nice to come up with a way to get the type from the JSON.
+                propertyMap.emplace(property,
+                                    propValuePair.value().get<size_t>());
+            }
+            else if (propValuePair.value().is_object())
+            {
+                const std::string& record =
+                    propValuePair.value().value("recordName", "");
+                const std::string& keyword =
+                    propValuePair.value().value("keywordName", "");
+                const std::string& encoding =
+                    propValuePair.value().value("encoding", "");
+
+                if (auto ipzVpdMap =
+                        std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+                {
+                    if (!record.empty() && !keyword.empty() &&
+                        (*ipzVpdMap).count(record) &&
+                        (*ipzVpdMap).at(record).count(keyword))
+                    {
+                        auto encoded = vpdSpecificUtility::encodeKeyword(
+                            ((*ipzVpdMap).at(record).at(keyword)), encoding);
+                        propertyMap.emplace(property, encoded);
+                    }
+                }
+                else if (auto kwdVpdMap =
+                             std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
+                {
+                    if (!keyword.empty() && (*kwdVpdMap).count(keyword))
+                    {
+                        if (auto kwValue = std::get_if<types::BinaryVector>(
+                                &(*kwdVpdMap).at(keyword)))
+                        {
+                            auto encodedValue =
+                                vpdSpecificUtility::encodeKeyword(
+                                    std::string((*kwValue).begin(),
+                                                (*kwValue).end()),
+                                    encoding);
+
+                            propertyMap.emplace(property, encodedValue);
+                        }
+                        else if (auto kwValue = std::get_if<std::string>(
+                                     &(*kwdVpdMap).at(keyword)))
+                        {
+                            auto encodedValue =
+                                vpdSpecificUtility::encodeKeyword(
+                                    std::string((*kwValue).begin(),
+                                                (*kwValue).end()),
+                                    encoding);
+
+                            propertyMap.emplace(property, encodedValue);
+                        }
+                        else if (auto uintValue = std::get_if<size_t>(
+                                     &(*kwdVpdMap).at(keyword)))
+                        {
+                            propertyMap.emplace(property, *uintValue);
+                        }
+                        else
+                        {
+                            logging::logMessage(
+                                "Unknown keyword found, Keywrod = " + keyword);
+                        }
+                    }
+                }
+            }
+        }
+        vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
+                                          move(propertyMap));
+    }
+}
+
+bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
+{
+    const unsigned char l_io[] = {
+        0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
+        0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
+
+    // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
+    // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
+    // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
+    // IO.
+    if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
+               constants::SIZE_OF_8EQ_IN_PG) == 0)
+    {
+        return true;
+    }
+
+    // The CPU is not an IO
+    return false;
+}
+
+bool Worker::primeInventory(const std::string& i_vpdFilePath)
+{
+    if (i_vpdFilePath.empty())
+    {
+        logging::logMessage("Empty VPD file path given");
+        return false;
+    }
+
+    if (m_parsedJson.empty())
+    {
+        logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
+        return false;
+    }
+    else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
+    {
+        logging::logMessage("File " + i_vpdFilePath +
+                            ", is not found in the system config JSON file.");
+        return false;
+    }
+
+    types::ObjectMap l_objectInterfaceMap;
+    for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
+    {
+        types::InterfaceMap l_interfaces;
+        sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
+
+        if (l_Fru.contains("ccin"))
+        {
+            continue;
+        }
+
+        if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
+        {
+            continue;
+        }
+
+        // Clear data under PIM if already exists.
+        vpdSpecificUtility::resetDataUnderPIM(
+            std::string(l_Fru["inventoryPath"]), l_interfaces);
+
+        // Add extra interfaces mentioned in the Json config file
+        if (l_Fru.contains("extraInterfaces"))
+        {
+            populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
+                               std::monostate{});
+        }
+
+        types::PropertyMap l_propertyValueMap;
+        l_propertyValueMap.emplace("Present", false);
+        if (std::filesystem::exists(i_vpdFilePath))
+        {
+            l_propertyValueMap["Present"] = true;
+        }
+
+        vpdSpecificUtility::insertOrMerge(l_interfaces,
+                                          "xyz.openbmc_project.Inventory.Item",
+                                          move(l_propertyValueMap));
+
+        if (l_Fru.value("inherit", true) &&
+            m_parsedJson.contains("commonInterfaces"))
+        {
+            populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
+                               std::monostate{});
+        }
+
+        processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
+        processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
+
+        l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
+                                     std::move(l_interfaces));
+    }
+
+    // Notify PIM
+    if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
+    {
+        logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
+        return false;
+    }
+
+    return true;
+}
+
+void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
+                                               types::InterfaceMap& interfaces)
+{
+    // embedded property(true or false) says whether the subfru is embedded
+    // into the parent fru (or) not. VPD sets Present property only for
+    // embedded frus. If the subfru is not an embedded FRU, the subfru may
+    // or may not be physically present. Those non embedded frus will always
+    // have Present=false irrespective of its physical presence or absence.
+    // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
+    // Present to true for such sub frus.
+    // Eg: ethernet port is embedded into bmc card. So set Present to true
+    // for such sub frus. Also donot populate present property for embedded
+    // subfru which is synthesized. Currently there is no subfru which are
+    // both embedded and synthesized. But still the case is handled here.
+
+    // Check if its required to handle presence for this FRU.
+    if (singleFru.value("handlePresence", true))
+    {
+        types::PropertyMap presProp;
+        presProp.emplace("Present", true);
+        vpdSpecificUtility::insertOrMerge(
+            interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
+    }
+}
+
+void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
+                                    types::InterfaceMap& interfaces,
+                                    const types::VPDMapVariant& parsedVpdMap)
+{
+    populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
+    if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+    {
+        if (singleFru["extraInterfaces"].contains(
+                "xyz.openbmc_project.Inventory.Item.Cpu"))
+        {
+            auto itrToRec = (*ipzVpdMap).find("CP00");
+            if (itrToRec == (*ipzVpdMap).end())
+            {
+                return;
+            }
+
+            std::string pgKeywordValue;
+            vpdSpecificUtility::getKwVal(itrToRec->second, "PG",
+                                         pgKeywordValue);
+            if (!pgKeywordValue.empty())
+            {
+                if (isCPUIOGoodOnly(pgKeywordValue))
+                {
+                    interfaces["xyz.openbmc_project.Inventory.Item"]
+                              ["PrettyName"] = "IO Module";
+                }
+            }
+        }
+    }
+}
+
+void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
+                                   const types::VPDMapVariant& parsedVpdMap,
+                                   types::InterfaceMap& interfaces)
+{
+    if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+    {
+        for (const auto& record : singleFru["copyRecords"])
+        {
+            const std::string& recordName = record;
+            if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
+            {
+                populateIPZVPDpropertyMap(interfaces,
+                                          (*ipzVpdMap).at(recordName),
+                                          constants::ipzVpdInf + recordName);
+            }
+        }
+    }
+}
+
+void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
+                                types::InterfaceMap& interfaces)
+{
+    if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+    {
+        for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
+        {
+            populateIPZVPDpropertyMap(interfaces, kwdValueMap,
+                                      constants::ipzVpdInf + recordName);
+        }
+    }
+    else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
+    {
+        populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
+    }
+
+    if (m_parsedJson.contains("commonInterfaces"))
+    {
+        populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
+                           parsedVpdMap);
+    }
+}
+
+bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
+                                const types::VPDMapVariant& parsedVpdMap)
+{
+    if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+    {
+        auto itrToRec = (*ipzVPDMap).find("VINI");
+        if (itrToRec == (*ipzVPDMap).end())
+        {
+            return false;
+        }
+
+        std::string ccinFromVpd;
+        vpdSpecificUtility::getKwVal(itrToRec->second, "CC", ccinFromVpd);
+        if (ccinFromVpd.empty())
+        {
+            return false;
+        }
+
+        transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
+                  ::toupper);
+
+        std::vector<std::string> ccinList;
+        for (std::string ccin : singleFru["ccin"])
+        {
+            transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
+            ccinList.push_back(ccin);
+        }
+
+        if (ccinList.empty())
+        {
+            return false;
+        }
+
+        if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
+            ccinList.end())
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
+                                       types::InterfaceMap& io_interfaces)
+{
+    if (!dbusUtility::isChassisPowerOn())
+    {
+        std::array<const char*, 1> l_operationalStatusInf = {
+            constants::operationalStatusInf};
+
+        auto mapperObjectMap = dbusUtility::getObjectMap(
+            i_inventoryObjPath, l_operationalStatusInf);
+
+        // If the object has been found. Check if it is under PIM.
+        if (mapperObjectMap.size() != 0)
+        {
+            for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
+            {
+                if (l_serviceName == constants::pimServiceName)
+                {
+                    // The object is already under PIM. No need to process
+                    // again. Retain the old value.
+                    return;
+                }
+            }
+        }
+
+        // Implies value is not there in D-Bus. Populate it with default
+        // value "true".
+        types::PropertyMap l_functionalProp;
+        l_functionalProp.emplace("Functional", true);
+        vpdSpecificUtility::insertOrMerge(io_interfaces,
+                                          constants::operationalStatusInf,
+                                          move(l_functionalProp));
+    }
+
+    // if chassis is power on. Functional property should be there on D-Bus.
+    // Don't process.
+    return;
+}
+
+void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
+                                    types::InterfaceMap& io_interfaces)
+{
+    if (!dbusUtility::isChassisPowerOn())
+    {
+        std::array<const char*, 1> l_enableInf = {constants::enableInf};
+
+        auto mapperObjectMap =
+            dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
+
+        // If the object has been found. Check if it is under PIM.
+        if (mapperObjectMap.size() != 0)
+        {
+            for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
+            {
+                if (l_serviceName == constants::pimServiceName)
+                {
+                    // The object is already under PIM. No need to process
+                    // again. Retain the old value.
+                    return;
+                }
+            }
+        }
+
+        // Implies value is not there in D-Bus. Populate it with default
+        // value "true".
+        types::PropertyMap l_enabledProp;
+        l_enabledProp.emplace("Enabled", true);
+        vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
+                                          move(l_enabledProp));
+    }
+
+    // if chassis is power on. Enabled property should be there on D-Bus.
+    // Don't process.
+    return;
+}
+
+void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
+                          types::ObjectMap& objectInterfaceMap,
+                          const std::string& vpdFilePath)
+{
+    if (vpdFilePath.empty())
+    {
+        throw std::runtime_error(
+            "Invalid parameter passed to populateDbus API.");
+    }
+
+    // JSON config is mandatory for processing of "if". Add "else" for any
+    // processing without config JSON.
+    if (!m_parsedJson.empty())
+    {
+        types::InterfaceMap interfaces;
+
+        for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
+        {
+            const auto& inventoryPath = aFru["inventoryPath"];
+            sdbusplus::message::object_path fruObjectPath(inventoryPath);
+            if (aFru.contains("ccin"))
+            {
+                if (!processFruWithCCIN(aFru, parsedVpdMap))
+                {
+                    continue;
+                }
+            }
+
+            if (aFru.value("inherit", true))
+            {
+                processInheritFlag(parsedVpdMap, interfaces);
+            }
+
+            // If specific record needs to be copied.
+            if (aFru.contains("copyRecords"))
+            {
+                processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
+            }
+
+            if (aFru.contains("extraInterfaces"))
+            {
+                // Process extra interfaces w.r.t a FRU.
+                processExtraInterfaces(aFru, interfaces, parsedVpdMap);
+            }
+
+            // Process FRUS which are embedded in the parent FRU and whose VPD
+            // will be synthesized.
+            if ((aFru.value("embedded", true)) &&
+                (!aFru.value("synthesized", false)))
+            {
+                processEmbeddedAndSynthesizedFrus(aFru, interfaces);
+            }
+
+            processFunctionalProperty(inventoryPath, interfaces);
+            processEnabledProperty(inventoryPath, interfaces);
+
+            objectInterfaceMap.emplace(std::move(fruObjectPath),
+                                       std::move(interfaces));
+        }
+    }
+}
+
+std::string
+    Worker::createAssetTagString(const types::VPDMapVariant& i_parsedVpdMap)
+{
+    std::string l_assetTag;
+
+    // system VPD will be in IPZ format.
+    if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
+    {
+        auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
+        if (l_itrToVsys != (*l_parsedVpdMap).end())
+        {
+            std::string l_tmKwdValue;
+            vpdSpecificUtility::getKwVal(l_itrToVsys->second, constants::kwdTM,
+                                         l_tmKwdValue);
+
+            std::string l_seKwdValue;
+            vpdSpecificUtility::getKwVal(l_itrToVsys->second, constants::kwdSE,
+                                         l_seKwdValue);
+
+            l_assetTag = std::string{"Server-"} + l_tmKwdValue +
+                         std::string{"-"} + l_seKwdValue;
+        }
+        else
+        {
+            throw std::runtime_error(
+                "VSYS record not found in parsed VPD map to create Asset tag.");
+        }
+    }
+    else
+    {
+        throw std::runtime_error(
+            "Invalid VPD type recieved to create Asset tag.");
+    }
+
+    return l_assetTag;
+}
+
+void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
+{
+    types::ObjectMap objectInterfaceMap;
+
+    if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+    {
+        populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
+
+        try
+        {
+            if (m_isFactoryResetDone)
+            {
+                const auto& l_assetTag = createAssetTagString(parsedVpdMap);
+
+                auto l_itrToSystemPath = objectInterfaceMap.find(
+                    sdbusplus::message::object_path(constants::systemInvPath));
+                if (l_itrToSystemPath == objectInterfaceMap.end())
+                {
+                    throw std::runtime_error(
+                        "System Path not found in object map.");
+                }
+
+                types::PropertyMap l_assetTagProperty;
+                l_assetTagProperty.emplace("AssetTag", l_assetTag);
+
+                (l_itrToSystemPath->second)
+                    .emplace(constants::assetTagInf,
+                             std::move(l_assetTagProperty));
+            }
+        }
+        catch (const std::exception& l_ex)
+        {
+            EventLogger::createSyncPel(
+                types::ErrorType::InvalidVpdMessage,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                "Asset tag update failed with following error: " +
+                    std::string(l_ex.what()),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+        }
+
+        // Notify PIM
+        if (!dbusUtility::callPIM(move(objectInterfaceMap)))
+        {
+            throw std::runtime_error("Call to PIM failed for system VPD");
+        }
+    }
+    else
+    {
+        throw DataException("Invalid format of parsed VPD map.");
+    }
+}
+
+bool Worker::processPreAction(const std::string& i_vpdFilePath,
+                              const std::string& i_flagToProcess)
+{
+    if (i_vpdFilePath.empty() || i_flagToProcess.empty())
+    {
+        logging::logMessage(
+            "Invalid input parameter. Abort processing pre action");
+        return false;
+    }
+
+    if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
+                                         i_vpdFilePath, i_flagToProcess)) &&
+        (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
+    {
+        // TODO: Need a way to delete inventory object from Dbus and persisted
+        // data section in case any FRU is not present or there is any
+        // problem in collecting it. Once it has been deleted, it can be
+        // re-created in the flow of priming the inventory. This needs to be
+        // done either here or in the exception section of "parseAndPublishVPD"
+        // API. Any failure in the process of collecting FRU will land up in the
+        // excpetion of "parseAndPublishVPD".
+
+        // If the FRU is not there, clear the VINI/CCIN data.
+        // Enity manager probes for this keyword to look for this
+        // FRU, now if the data is persistent on BMC and FRU is
+        // removed this can lead to ambiguity. Hence clearing this
+        // Keyword if FRU is absent.
+        const auto& inventoryPath =
+            m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
+                                                            "");
+
+        if (!inventoryPath.empty())
+        {
+            types::ObjectMap l_pimObjMap{
+                {inventoryPath,
+                 {{constants::kwdVpdInf,
+                   {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
+
+            if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
+            {
+                logging::logMessage(
+                    "Call to PIM failed for file " + i_vpdFilePath);
+            }
+        }
+        else
+        {
+            logging::logMessage(
+                "Inventory path is empty in Json for file " + i_vpdFilePath);
+        }
+
+        return false;
+    }
+    return true;
+}
+
+bool Worker::processPostAction(
+    const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
+    const std::optional<types::VPDMapVariant> i_parsedVpd)
+{
+    if (i_vpdFruPath.empty() || i_flagToProcess.empty())
+    {
+        logging::logMessage(
+            "Invalid input parameter. Abort processing post action");
+        return false;
+    }
+
+    // Check if post action tag is to be triggered in the flow of collection
+    // based on some CCIN value?
+    if (m_parsedJson["frus"][i_vpdFruPath]
+            .at(0)["postAction"][i_flagToProcess]
+            .contains("ccin"))
+    {
+        if (!i_parsedVpd.has_value())
+        {
+            logging::logMessage("Empty VPD Map");
+            return false;
+        }
+
+        // CCIN match is required to process post action for this FRU as it
+        // contains the flag.
+        if (!vpdSpecificUtility::findCcinInVpd(
+                m_parsedJson["frus"][i_vpdFruPath].at(
+                    0)["postAction"]["collection"],
+                i_parsedVpd.value()))
+        {
+            // If CCIN is not found, implies post action processing is not
+            // required for this FRU. Let the flow continue.
+            return true;
+        }
+    }
+
+    if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
+                                        i_vpdFruPath, i_flagToProcess))
+    {
+        logging::logMessage(
+            "Execution of post action failed for path: " + i_vpdFruPath);
+
+        // If post action was required and failed only in that case return
+        // false. In all other case post action is considered passed.
+        return false;
+    }
+
+    return true;
+}
+
+types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
+{
+    if (i_vpdFilePath.empty())
+    {
+        throw std::runtime_error(
+            "Empty VPD file path passed to Worker::parseVpdFile. Abort processing");
+    }
+
+    try
+    {
+        if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
+                                          "preAction", "collection"))
+        {
+            if (!processPreAction(i_vpdFilePath, "collection"))
+            {
+                throw std::runtime_error("Pre-Action failed");
+            }
+        }
+
+        if (!std::filesystem::exists(i_vpdFilePath))
+        {
+            throw std::runtime_error(
+                "Could not find file path " + i_vpdFilePath +
+                "Skipping parser trigger for the EEPROM");
+        }
+
+        std::shared_ptr<Parser> vpdParser =
+            std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
+
+        types::VPDMapVariant l_parsedVpd = vpdParser->parse();
+
+        // Before returning, as collection is over, check if FRU qualifies for
+        // any post action in the flow of collection.
+        // Note: Don't change the order, post action needs to be processed only
+        // after collection for FRU is successfully done.
+        if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
+                                          "postAction", "collection"))
+        {
+            if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
+            {
+                // TODO: Log PEL
+                logging::logMessage("Required post action failed for path [" +
+                                    i_vpdFilePath + "]");
+            }
+        }
+
+        return l_parsedVpd;
+    }
+    catch (std::exception& l_ex)
+    {
+        // If post fail action is required, execute it.
+        if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
+                                          "PostFailAction", "collection"))
+        {
+            if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
+                                                    "collection"))
+            {
+                // TODO: Log PEL
+                throw std::runtime_error(
+                    "VPD parsing failed for " + i_vpdFilePath +
+                    " due to error: " + l_ex.what() +
+                    ". Post Fail Action also failed, aborting collection for this FRU");
+            }
+        }
+
+        // TODO: Log PEL
+        throw std::runtime_error("VPD parsing failed for " + i_vpdFilePath +
+                                 " due to error: " + l_ex.what());
+    }
+}
+
+std::tuple<bool, std::string>
+    Worker::parseAndPublishVPD(const std::string& i_vpdFilePath)
+{
+    try
+    {
+        m_semaphore.acquire();
+
+        // Thread launched.
+        m_mutex.lock();
+        m_activeCollectionThreadCount++;
+        m_mutex.unlock();
+
+        const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
+
+        types::ObjectMap objectInterfaceMap;
+        populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
+
+        // logging::logMessage("Dbus sucessfully populated for FRU " +
+        //                     i_vpdFilePath);
+
+        // Notify PIM
+        if (!dbusUtility::callPIM(move(objectInterfaceMap)))
+        {
+            throw std::runtime_error(
+                "Call to PIM failed while publishing VPD.");
+        }
+    }
+    catch (const std::exception& ex)
+    {
+        // handle all the exceptions internally. Return only true/false
+        // based on status of execution.
+        if (typeid(ex) == std::type_index(typeid(DataException)))
+        {
+            // TODO: Add custom handling
+            logging::logMessage(ex.what());
+        }
+        else if (typeid(ex) == std::type_index(typeid(EccException)))
+        {
+            // TODO: Add custom handling
+            logging::logMessage(ex.what());
+        }
+        else if (typeid(ex) == std::type_index(typeid(JsonException)))
+        {
+            // TODO: Add custom handling
+            logging::logMessage(ex.what());
+        }
+        else
+        {
+            logging::logMessage(ex.what());
+        }
+
+        // TODO: Figure out a way to clear data in case of any failure at
+        // runtime.
+        //  Prime the inventry for FRUs which
+        //  are not present/processing had some error.
+        /* if (!primeInventory(i_vpdFilePath))
+         {
+             logging::logMessage("Priming of inventory failed for FRU " +
+                                 i_vpdFilePath);
+         }*/
+        m_semaphore.release();
+        return std::make_tuple(false, i_vpdFilePath);
+    }
+    m_semaphore.release();
+    return std::make_tuple(true, i_vpdFilePath);
+}
+
+void Worker::collectFrusFromJson()
+{
+    // A parsed JSON file should be present to pick FRUs EEPROM paths
+    if (m_parsedJson.empty())
+    {
+        throw std::runtime_error(
+            "A config JSON is required for processing of FRUs");
+    }
+
+    const nlohmann::json& listOfFrus =
+        m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
+
+    for (const auto& itemFRUS : listOfFrus.items())
+    {
+        const std::string& vpdFilePath = itemFRUS.key();
+
+        // skip processing of system VPD again as it has been already collected.
+        // Also, if chassis is powered on, skip collecting FRUs which are
+        // powerOffOnly.
+        // TODO: Need to revisit for P-Future to reduce code update time.
+        if (vpdFilePath == SYSTEM_VPD_FILE_PATH ||
+            (jsonUtility::isFruPowerOffOnly(m_parsedJson, vpdFilePath) &&
+             dbusUtility::isChassisPowerOn()))
+        {
+            continue;
+        }
+
+        std::thread{[vpdFilePath, this]() {
+            auto l_futureObject =
+                std::async(&Worker::parseAndPublishVPD, this, vpdFilePath);
+
+            std::tuple<bool, std::string> l_threadInfo = l_futureObject.get();
+
+            // thread returned.
+            m_mutex.lock();
+            m_activeCollectionThreadCount--;
+            m_mutex.unlock();
+
+            if (!m_activeCollectionThreadCount)
+            {
+                m_isAllFruCollected = true;
+            }
+        }}.detach();
+    }
+}
+
+// ToDo: Move the API under IBM_SYSTEM
+void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
+{
+    try
+    {
+        std::string l_backupAndRestoreCfgFilePath =
+            m_parsedJson.value("backupRestoreConfigPath", "");
+
+        nlohmann::json l_backupAndRestoreCfgJsonObj =
+            jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
+
+        // check if either of "source" or "destination" has inventory path.
+        // this indicates that this sytem has System VPD on hardware
+        // and other copy on D-Bus (BMC cache).
+        if (!l_backupAndRestoreCfgJsonObj.empty() &&
+            ((l_backupAndRestoreCfgJsonObj.contains("source") &&
+              l_backupAndRestoreCfgJsonObj["source"].contains(
+                  "inventoryPath")) ||
+             (l_backupAndRestoreCfgJsonObj.contains("destination") &&
+              l_backupAndRestoreCfgJsonObj["destination"].contains(
+                  "inventoryPath"))))
+        {
+            BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
+            auto [l_srcVpdVariant,
+                  l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
+
+            // ToDo: Revisit is this check is required or not.
+            if (auto l_srcVpdMap =
+                    std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
+                l_srcVpdMap && !(*l_srcVpdMap).empty())
+            {
+                io_srcVpdMap = std::move(l_srcVpdVariant);
+            }
+        }
+    }
+    catch (const std::exception& l_ex)
+    {
+        EventLogger::createSyncPel(
+            types::ErrorType::InvalidVpdMessage,
+            types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+            std::string(
+                "Exception caught while backup and restore VPD keyword's.") +
+                l_ex.what(),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+}
+
+void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
+{
+    if (i_dbusObjPath.empty())
+    {
+        throw std::runtime_error("Given DBus object path is empty.");
+    }
+
+    const std::string& l_fruPath =
+        jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
+
+    try
+    {
+        auto l_presentPropValue = dbusUtility::readDbusProperty(
+            constants::pimServiceName, i_dbusObjPath,
+            constants::inventoryItemInf, "Present");
+
+        if (auto l_value = std::get_if<bool>(&l_presentPropValue))
+        {
+            if (!(*l_value))
+            {
+                throw std::runtime_error("Given FRU is not present");
+            }
+            else
+            {
+                if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
+                                                  "preAction", "deletion"))
+                {
+                    if (!processPreAction(l_fruPath, "deletion"))
+                    {
+                        throw std::runtime_error("Pre action failed");
+                    }
+                }
+
+                std::vector<std::string> l_interfaceList{
+                    constants::operationalStatusInf};
+
+                types::MapperGetSubTree l_subTreeMap =
+                    dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
+                                                  l_interfaceList);
+
+                types::ObjectMap l_objectMap;
+
+                // Updates VPD specific interfaces property value under PIM for
+                // sub FRUs.
+                for (const auto& [l_objectPath, l_serviceInterfaceMap] :
+                     l_subTreeMap)
+                {
+                    types::InterfaceMap l_interfaceMap;
+                    vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
+                                                          l_interfaceMap);
+                    l_objectMap.emplace(l_objectPath,
+                                        std::move(l_interfaceMap));
+                }
+
+                types::InterfaceMap l_interfaceMap;
+                vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
+                                                      l_interfaceMap);
+
+                l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
+
+                if (!dbusUtility::callPIM(std::move(l_objectMap)))
+                {
+                    throw std::runtime_error("Call to PIM failed.");
+                }
+
+                if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
+                                                  "postAction", "deletion"))
+                {
+                    if (!processPostAction(l_fruPath, "deletion"))
+                    {
+                        throw std::runtime_error("Post action failed");
+                    }
+                }
+            }
+        }
+        else
+        {
+            logging::logMessage(
+                "Can't process delete VPD for FRU [" + i_dbusObjPath +
+                "] as unable to read present property");
+            return;
+        }
+
+        logging::logMessage(
+            "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
+    }
+    catch (const std::exception& l_ex)
+    {
+        if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
+                                          "postFailAction", "deletion"))
+        {
+            if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
+                                                    "deletion"))
+            {
+                logging::logMessage(
+                    "Post fail action failed for: " + i_dbusObjPath);
+            }
+        }
+
+        logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
+                            " error: " + std::string(l_ex.what()));
+    }
+}
+} // namespace vpd