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/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