manager: Sync pvm_clear_nvram BIOS Attribute

This commit adds support to synchronize the pvm_clear_nvram BIOS
attribute to bit 2 in the UTIL/D1 keyword of the motherboard VPD.

The attribute will be used by PHYP to determine when to clear their
NVRAM content.

A value of "Disabled" in the BIOS attribute maps to the D1:2 bit being 0
and a value of "Enabled: maps to the D1:2 bit being 1.

Signed-off-by: Santosh Puranik <santosh.puranik@in.ibm.com>
Change-Id: Ia0f3cfa92a98a2a1a95dd67ca598770459b9b7f2
diff --git a/vpd-manager/bios_handler.cpp b/vpd-manager/bios_handler.cpp
index 1f0306c..ff8ad37 100644
--- a/vpd-manager/bios_handler.cpp
+++ b/vpd-manager/bios_handler.cpp
@@ -155,6 +155,15 @@
                         saveCreateDefaultLparToVPD(*val);
                     }
                 }
+                else if (attributeName == "pvm_clear_nvram")
+                {
+                    auto attrValue = std::get<5>(std::get<1>(item));
+                    auto val = std::get_if<std::string>(&attrValue);
+                    if (val)
+                    {
+                        saveClearNVRAMToVPD(*val);
+                    }
+                }
             }
         }
     }
@@ -200,7 +209,7 @@
 
     if (mirrorMode != "Enabled" && mirrorMode != "Disabled")
     {
-        std::cerr << "Bad value for Mirror mode BIOS arttribute: " << mirrorMode
+        std::cerr << "Bad value for Mirror mode BIOS attribute: " << mirrorMode
                   << std::endl;
         return;
     }
@@ -238,7 +247,7 @@
 
     if (keepAndClear != "Enabled" && keepAndClear != "Disabled")
     {
-        std::cerr << "Bad value for keep and clear BIOS arttribute: "
+        std::cerr << "Bad value for keep and clear BIOS attribute: "
                   << keepAndClear << std::endl;
         return;
     }
@@ -277,7 +286,7 @@
 
     if (createDefaultLpar != "Enabled" && createDefaultLpar != "Disabled")
     {
-        std::cerr << "Bad value for create default lpar BIOS arttribute: "
+        std::cerr << "Bad value for create default lpar BIOS attribute: "
                   << createDefaultLpar << std::endl;
         return;
     }
@@ -301,6 +310,44 @@
     }
 }
 
+void BiosHandler::saveClearNVRAMToVPD(const std::string& clearNVRAM)
+{
+    Binary vpdVal;
+    auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
+
+    if (valInVPD.size() != 1)
+    {
+        std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
+                  << std::endl;
+        return;
+    }
+
+    if (clearNVRAM != "Enabled" && clearNVRAM != "Disabled")
+    {
+        std::cerr << "Bad value for clear NVRAM BIOS attribute: " << clearNVRAM
+                  << std::endl;
+        return;
+    }
+
+    // Write to VPD only if the value is not already what we want to write.
+    if (clearNVRAM == "Enabled" && ((valInVPD.at(0) & 0x04) != 0x04))
+    {
+        vpdVal.emplace_back(valInVPD.at(0) | 0x04);
+    }
+    else if (clearNVRAM == "Disabled" && ((valInVPD.at(0) & 0x04) != 0))
+    {
+        vpdVal.emplace_back(valInVPD.at(0) & ~(0x04));
+    }
+
+    if (!vpdVal.empty())
+    {
+        std::cout << "Writing clear NVRAM to VPD: "
+                  << static_cast<int>(vpdVal.at(0)) << std::endl;
+        manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
+                             "UTIL", "D1", vpdVal);
+    }
+}
+
 int64_t BiosHandler::readBIOSFCO()
 {
     int64_t fcoVal = -1;
@@ -365,6 +412,22 @@
     return createDefaultLpar;
 }
 
+std::string BiosHandler::readBIOSClearNVRAM()
+{
+    std::string clearNVRAM{};
+    auto val = readBIOSAttribute("pvm_clear_nvram");
+
+    if (auto pVal = std::get_if<std::string>(&val))
+    {
+        clearNVRAM = *pVal;
+    }
+    else
+    {
+        std::cerr << "Clear NVRAM is not a string" << std::endl;
+    }
+    return clearNVRAM;
+}
+
 void BiosHandler::saveFCOToBIOS(const std::string& fcoVal, int64_t fcoInBIOS)
 {
     if (fcoVal.size() != 4)
@@ -512,17 +575,52 @@
         biosAttrs);
 }
 
+void BiosHandler::saveClearNVRAMToBIOS(const std::string& clearNVRAM,
+                                       const std::string& clearNVRAMInBIOS)
+{
+    if (clearNVRAM.size() != 1)
+    {
+        std::cerr << "Bad size for Clear NVRAM in VPD: " << clearNVRAM.size()
+                  << std::endl;
+        return;
+    }
+
+    // Need to write?
+    std::string toWrite = (clearNVRAM.at(0) & 0x04) ? "Enabled" : "Disabled";
+    if (clearNVRAMInBIOS == toWrite)
+    {
+        std::cout << "Skip Clear NVRAM BIOS write, value is already: "
+                  << toWrite << std::endl;
+        return;
+    }
+
+    PendingBIOSAttrsType biosAttrs;
+    biosAttrs.push_back(
+        std::make_pair("pvm_clear_nvram",
+                       std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                       "AttributeType.Enumeration",
+                                       toWrite)));
+
+    std::cout << "Set pvm_clear_nvram to: " << toWrite << std::endl;
+
+    setBusProperty<PendingBIOSAttrsType>(
+        "xyz.openbmc_project.BIOSConfigManager",
+        "/xyz/openbmc_project/bios_config/manager",
+        "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
+        biosAttrs);
+}
+
 void BiosHandler::restoreBIOSAttribs()
 {
     // TODO: We could make this slightly more scalable by defining a table of
     // attributes and their corresponding VPD keywords. However, that needs much
     // more thought.
     std::cout << "Attempting BIOS attribute reset" << std::endl;
-    // Check if the VPD contains valid data for FCO, AMM, Keep and Clear and
-    // Create default LPAR *and* that it differs from the data already in the
-    // attributes. If so, set the BIOS attributes as per the value in the VPD.
-    // If the VPD contains default data, then initialize the VPD keywords with
-    // data taken from the BIOS.
+    // Check if the VPD contains valid data for FCO, AMM, Keep and Clear,
+    // Create default LPAR and Clear NVRAM *and* that it differs from the data
+    // already in the attributes. If so, set the BIOS attributes as per the
+    // value in the VPD. If the VPD contains default data, then initialize the
+    // VPD keywords with data taken from the BIOS.
     auto fcoInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
     auto ammInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
     auto keepAndClearInVPD =
@@ -531,6 +629,7 @@
     auto ammInBIOS = readBIOSAMM();
     auto keepAndClearInBIOS = readBIOSKeepAndClear();
     auto createDefaultLparInBIOS = readBIOSCreateDefaultLpar();
+    auto clearNVRAMInBIOS = readBIOSClearNVRAM();
 
     if (fcoInVPD == "    ")
     {
@@ -550,8 +649,9 @@
         saveAMMToBIOS(ammInVPD, ammInBIOS);
     }
 
-    // No uninitialized handling needed for keep and clear and create default
-    // lpar attributes. Their defaults in VPD are 0's which is what we want.
+    // No uninitialized handling needed for keep and clear, create default
+    // lpar and clear nvram attributes. Their defaults in VPD are 0's which is
+    // what we want.
     saveKeepAndClearToBIOS(keepAndClearInVPD, keepAndClearInBIOS);
     // Have to read D1 again because two attributes are stored in the same
     // keyword.
@@ -560,6 +660,10 @@
     saveCreateDefaultLparToBIOS(createDefaultLparInVPD,
                                 createDefaultLparInBIOS);
 
+    auto clearNVRAMInVPD =
+        readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
+    saveClearNVRAMToBIOS(clearNVRAMInVPD, clearNVRAMInBIOS);
+
     // Start listener now that we have done the restore
     listenBiosAttribs();
 }
diff --git a/vpd-manager/bios_handler.hpp b/vpd-manager/bios_handler.hpp
index 86e3318..985daa7 100644
--- a/vpd-manager/bios_handler.hpp
+++ b/vpd-manager/bios_handler.hpp
@@ -135,13 +135,24 @@
     void saveCreateDefaultLparToVPD(const std::string& createDefaultLpar);
 
     /**
+     * @brief Persistently saves the Clear NVRAM setting
+     *
+     * Create default LPAR setting is saved to the UTIL/D1 keyword's 2nd bit in
+     * the motherboard VPD. If the clear NVRAM in BIOS is "Disabled",
+     * set D1:2 to 0, if "Enabled" set D1:2 to 1
+     *
+     * @param[in] createDefaultLpar - The mirror mode BIOS attribute.
+     */
+    void saveClearNVRAMToVPD(const std::string& clearNVRAM);
+
+    /**
      * @brief Writes Memory mirror mode to BIOS
      *
      * Writes to the hb_memory_mirror_mode BIOS attribute, if the value is
      * not already the same as we are trying to write.
      *
      * @param[in] ammVal - The mirror mode as read from VPD.
-     * @param[in] ammInBIOS - The mirror more in the BIOS table.
+     * @param[in] ammInBIOS - The mirror mode in the BIOS table.
      */
     void saveAMMToBIOS(const std::string& ammVal, const std::string& ammInBIOS);
 
@@ -162,8 +173,8 @@
      * Writes to the pvm_keep_and_clear BIOS attribute, if the value is
      * not already the same as we are trying to write.
      *
-     * @param[in] keepAndClear - The mirror mode as read from VPD.
-     * @param[in] keepAndClearInBIOS - The mirror more in the BIOS table.
+     * @param[in] keepAndClear - The keep and clear as read from VPD.
+     * @param[in] keepAndClearInBIOS - The keep and clear in the BIOS table.
      */
     void saveKeepAndClearToBIOS(const std::string& keepAndClear,
                                 const std::string& keepAndClearInBIOS);
@@ -174,14 +185,27 @@
      * Writes to the pvm_create_default_lpar BIOS attribute, if the value is
      * not already the same as we are trying to write.
      *
-     * @param[in] createDefaultLpar - The mirror mode as read from VPD.
-     * @param[in] createDefaultLparInBIOS - The mirror more in the BIOS table.
+     * @param[in] createDefaultLpar - The create default LPAR as read from VPD.
+     * @param[in] createDefaultLparInBIOS - The create default LPAR in the BIOS
+     * table.
      */
     void
         saveCreateDefaultLparToBIOS(const std::string& createDefaultLpar,
                                     const std::string& createDefaultLparInBIOS);
 
     /**
+     * @brief Writes Clear NVRAM setting to BIOS
+     *
+     * Writes to the pvm_clear_nvram BIOS attribute, if the value is
+     * not already the same as we are trying to write.
+     *
+     * @param[in] clearNVRAM - The clear NVRAM as read from VPD.
+     * @param[in] clearNVRAMInBIOS - The clear NVRAM in the BIOS table.
+     */
+    void saveClearNVRAMToBIOS(const std::string& clearNVRAM,
+                              const std::string& clearNVRAMInBIOS);
+
+    /**
      * @brief Reads the hb_memory_mirror_mode attribute
      *
      * @return std::string - The AMM BIOS attribute. Empty string on failure.
@@ -212,10 +236,18 @@
     std::string readBIOSCreateDefaultLpar();
 
     /**
+     * @brief Reads the pvm_clear_nvram attribute
+     *
+     * @return std::string - The Clear NVRAM BIOS attribute. Empty
+     * string on failure.
+     */
+    std::string readBIOSClearNVRAM();
+
+    /**
      * @brief Restore BIOS attributes
      *
      * This function checks if we are coming out of a factory reset. If yes,
-     * it checks the VPD cache for valid backed up copy of the FCO and AMM
+     * it checks the VPD cache for valid backed up copy of the applicable
      * BIOS attributes. If valid values are found in the VPD, it will apply
      * those to the BIOS attributes.
      */