manager: Add some attributes to BIOS restore

This commit adds support to synchronize the pvm_keep_and_clear
and pvm_create_default_lpar BIOS attributes to the UTIL/D1
keyword in the motherboard VPD.

The use-case for doing this is the same as the other attributes we
already handle - that to restore them post a factory reset.

pvm_keep_and_clear - Backed up to UTIL/D1, bit 0
pvm_create_default_lpar - Backed up to UTIL/D1, bit 1

Signed-off-by: Santosh Puranik <santosh.puranik@in.ibm.com>
Change-Id: I8a2c08a06a17d15ed9a607a482a2c8a88173fddd
Signed-off-by: Santosh Puranik <santosh.puranik@in.ibm.com>
diff --git a/vpd-manager/bios_handler.cpp b/vpd-manager/bios_handler.cpp
index 417852e..1f0306c 100644
--- a/vpd-manager/bios_handler.cpp
+++ b/vpd-manager/bios_handler.cpp
@@ -137,6 +137,24 @@
                         saveFCOToVPD(*val);
                     }
                 }
+                else if (attributeName == "pvm_keep_and_clear")
+                {
+                    auto attrValue = std::get<5>(std::get<1>(item));
+                    auto val = std::get_if<std::string>(&attrValue);
+                    if (val)
+                    {
+                        saveKeepAndClearToVPD(*val);
+                    }
+                }
+                else if (attributeName == "pvm_create_default_lpar")
+                {
+                    auto attrValue = std::get<5>(std::get<1>(item));
+                    auto val = std::get_if<std::string>(&attrValue);
+                    if (val)
+                    {
+                        saveCreateDefaultLparToVPD(*val);
+                    }
+                }
             }
         }
     }
@@ -206,6 +224,83 @@
     }
 }
 
+void BiosHandler::saveKeepAndClearToVPD(const std::string& keepAndClear)
+{
+    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 (keepAndClear != "Enabled" && keepAndClear != "Disabled")
+    {
+        std::cerr << "Bad value for keep and clear BIOS arttribute: "
+                  << keepAndClear << std::endl;
+        return;
+    }
+
+    // Write to VPD only if the value is not already what we want to write.
+    if (keepAndClear == "Enabled" && ((valInVPD.at(0) & 0x01) != 0x01))
+    {
+        vpdVal.emplace_back(valInVPD.at(0) | 0x01);
+    }
+    else if (keepAndClear == "Disabled" && ((valInVPD.at(0) & 0x01) != 0))
+    {
+        vpdVal.emplace_back(valInVPD.at(0) & ~(0x01));
+    }
+
+    if (!vpdVal.empty())
+    {
+        std::cout << "Writing Keep and Clear to VPD: "
+                  << static_cast<int>(vpdVal.at(0)) << std::endl;
+        manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
+                             "UTIL", "D1", vpdVal);
+    }
+}
+
+void BiosHandler::saveCreateDefaultLparToVPD(
+    const std::string& createDefaultLpar)
+{
+    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 (createDefaultLpar != "Enabled" && createDefaultLpar != "Disabled")
+    {
+        std::cerr << "Bad value for create default lpar BIOS arttribute: "
+                  << createDefaultLpar << std::endl;
+        return;
+    }
+
+    // Write to VPD only if the value is not already what we want to write.
+    if (createDefaultLpar == "Enabled" && ((valInVPD.at(0) & 0x02) != 0x02))
+    {
+        vpdVal.emplace_back(valInVPD.at(0) | 0x02);
+    }
+    else if (createDefaultLpar == "Disabled" && ((valInVPD.at(0) & 0x02) != 0))
+    {
+        vpdVal.emplace_back(valInVPD.at(0) & ~(0x02));
+    }
+
+    if (!vpdVal.empty())
+    {
+        std::cout << "Writing create default lpar 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;
@@ -238,6 +333,38 @@
     return ammVal;
 }
 
+std::string BiosHandler::readBIOSKeepAndClear()
+{
+    std::string keepAndClear{};
+    auto val = readBIOSAttribute("pvm_keep_and_clear");
+
+    if (auto pVal = std::get_if<std::string>(&val))
+    {
+        keepAndClear = *pVal;
+    }
+    else
+    {
+        std::cerr << "Keep and clear is not a string" << std::endl;
+    }
+    return keepAndClear;
+}
+
+std::string BiosHandler::readBIOSCreateDefaultLpar()
+{
+    std::string createDefaultLpar{};
+    auto val = readBIOSAttribute("pvm_create_default_lpar");
+
+    if (auto pVal = std::get_if<std::string>(&val))
+    {
+        createDefaultLpar = *pVal;
+    }
+    else
+    {
+        std::cerr << "Create default LPAR is not a string" << std::endl;
+    }
+    return createDefaultLpar;
+}
+
 void BiosHandler::saveFCOToBIOS(const std::string& fcoVal, int64_t fcoInBIOS)
 {
     if (fcoVal.size() != 4)
@@ -313,21 +440,97 @@
         biosAttrs);
 }
 
+void BiosHandler::saveKeepAndClearToBIOS(const std::string& keepAndClear,
+                                         const std::string& keepAndClearInBIOS)
+{
+    if (keepAndClear.size() != 1)
+    {
+        std::cerr << "Bad size for Keep and Clear in VPD: "
+                  << keepAndClear.size() << std::endl;
+        return;
+    }
+
+    // Need to write?
+    std::string toWrite = (keepAndClear.at(0) & 0x01) ? "Enabled" : "Disabled";
+    if (keepAndClearInBIOS == toWrite)
+    {
+        std::cout << "Skip Keep and Clear BIOS write, value is already: "
+                  << toWrite << std::endl;
+        return;
+    }
+
+    PendingBIOSAttrsType biosAttrs;
+    biosAttrs.push_back(
+        std::make_pair("pvm_keep_and_clear",
+                       std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                       "AttributeType.Enumeration",
+                                       toWrite)));
+
+    std::cout << "Set pvm_keep_and_clear 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::saveCreateDefaultLparToBIOS(
+    const std::string& createDefaultLpar,
+    const std::string& createDefaultLparInBIOS)
+{
+    if (createDefaultLpar.size() != 1)
+    {
+        std::cerr << "Bad size for Create default LPAR in VPD: "
+                  << createDefaultLpar.size() << std::endl;
+        return;
+    }
+
+    // Need to write?
+    std::string toWrite =
+        (createDefaultLpar.at(0) & 0x02) ? "Enabled" : "Disabled";
+    if (createDefaultLparInBIOS == toWrite)
+    {
+        std::cout << "Skip Create default LPAR BIOS write, value is already: "
+                  << toWrite << std::endl;
+        return;
+    }
+
+    PendingBIOSAttrsType biosAttrs;
+    biosAttrs.push_back(
+        std::make_pair("pvm_create_default_lpar",
+                       std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                       "AttributeType.Enumeration",
+                                       toWrite)));
+
+    std::cout << "Set pvm_create_default_lpar 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 and AMM *and* that it
-    // differs from the data already in the attributes. If so, set the BIOS
-    // attributes as per the value in the VPD.
+    // 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.
     auto fcoInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
     auto ammInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
+    auto keepAndClearInVPD =
+        readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
     auto fcoInBIOS = readBIOSFCO();
     auto ammInBIOS = readBIOSAMM();
+    auto keepAndClearInBIOS = readBIOSKeepAndClear();
+    auto createDefaultLparInBIOS = readBIOSCreateDefaultLpar();
 
     if (fcoInVPD == "    ")
     {
@@ -347,6 +550,16 @@
         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.
+    saveKeepAndClearToBIOS(keepAndClearInVPD, keepAndClearInBIOS);
+    // Have to read D1 again because two attributes are stored in the same
+    // keyword.
+    auto createDefaultLparInVPD =
+        readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
+    saveCreateDefaultLparToBIOS(createDefaultLparInVPD,
+                                createDefaultLparInBIOS);
+
     // Start listener now that we have done the restore
     listenBiosAttribs();
 }