API to check powerVS configuration

The commit implements change to check for powerVS configuration.

It is a combination of system series and driver running on the system.
Once that is established, only after that VPD vpdate will take place
w.r.t powerVS system.

Change-Id: I93fefcb79ced4f89bd6f5143666b4cd487897ac8
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/include/constants.hpp b/vpd-manager/include/constants.hpp
index f7e55d0..9358f78 100644
--- a/vpd-manager/include/constants.hpp
+++ b/vpd-manager/include/constants.hpp
@@ -113,6 +113,11 @@
 // Zero based index position of first EQ in CP00's PG keyword
 static constexpr auto INDEX_OF_EQ0_IN_PG = 97;
 
+static constexpr auto HEX_VALUE_50 = 0x50;
+static constexpr auto HEX_VALUE_30 = 0x30;
+static constexpr auto HEX_VALUE_10 = 0x10;
+static constexpr auto HEX_VALUE_00 = 0x00;
+
 constexpr auto systemInvPath = "/xyz/openbmc_project/inventory/system";
 constexpr auto pimPath = "/xyz/openbmc_project/inventory";
 constexpr auto pimIntf = "xyz.openbmc_project.Inventory.Manager";
@@ -161,6 +166,19 @@
 constexpr auto hostService = "xyz.openbmc_project.State.Host";
 constexpr auto hostRunningState =
     "xyz.openbmc_project.State.Host.HostState.Running";
+constexpr auto imageUpdateService = "xyz.openbmc_project.Software.BMC.Updater";
+constexpr auto imagePrirotyInf =
+    "xyz.openbmc_project.Software.RedundancyPriority";
+constexpr auto imageExtendedVerInf =
+    "xyz.openbmc_project.Software.ExtendedVersion";
+constexpr auto functionalImageObjPath =
+    "/xyz/openbmc_project/software/functional";
+constexpr auto associationInterface = "xyz.openbmc_project.Association";
+constexpr auto powerVsImagePrefix_MY = "MY";
+constexpr auto powerVsImagePrefix_MZ = "MZ";
+constexpr auto powerVsImagePrefix_NY = "NY";
+constexpr auto powerVsImagePrefix_NZ = "NZ";
+
 static constexpr auto BD_YEAR_END = 4;
 static constexpr auto BD_MONTH_END = 7;
 static constexpr auto BD_DAY_END = 10;
diff --git a/vpd-manager/include/utility/dbus_utility.hpp b/vpd-manager/include/utility/dbus_utility.hpp
index d13e123..344a637 100644
--- a/vpd-manager/include/utility/dbus_utility.hpp
+++ b/vpd-manager/include/utility/dbus_utility.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "constants.hpp"
+#include "exceptions.hpp"
 #include "logger.hpp"
 #include "types.hpp"
 
@@ -607,5 +608,82 @@
 
     return *l_imValue;
 }
+
+/**
+ * @brief API to return prefix of functional firmware image.
+ *
+ * Every functional image belongs to a series which is denoted by the first two
+ * characters of the image name. The API extracts that and returns it to the
+ * caller.
+ *
+ * @return Two character string, empty string in case of any error.
+ */
+inline std::string getImagePrefix()
+{
+    try
+    {
+        types::DbusVariantType l_retVal = readDbusProperty(
+            constants::objectMapperService, constants::functionalImageObjPath,
+            constants::associationInterface, "endpoints");
+
+        auto l_listOfFunctionalPath =
+            std::get_if<std::vector<std::string>>(&l_retVal);
+
+        if (!l_listOfFunctionalPath || (*l_listOfFunctionalPath).empty())
+        {
+            throw DbusException("failed to get functional image path.");
+        }
+
+        for (const auto& l_imagePath : *l_listOfFunctionalPath)
+        {
+            types::DbusVariantType l_retValPriority =
+                readDbusProperty(constants::imageUpdateService, l_imagePath,
+                                 constants::imagePrirotyInf, "Priority");
+
+            auto l_imagePriority = std::get_if<uint8_t>(&l_retValPriority);
+            if (!l_imagePriority)
+            {
+                throw DbusException(
+                    "failed to read functional image priority for path [" +
+                    l_imagePath + "]");
+            }
+
+            // only interested in running image.
+            if (*l_imagePriority != constants::VALUE_0)
+            {
+                continue;
+            }
+
+            types::DbusVariantType l_retExVer = readDbusProperty(
+                constants::imageUpdateService, l_imagePath,
+                constants::imageExtendedVerInf, "ExtendedVersion");
+
+            auto l_imageExtendedVersion = std::get_if<std::string>(&l_retExVer);
+            if (!l_imageExtendedVersion)
+            {
+                throw DbusException(
+                    "Unable to read extended version for the functional image [" +
+                    l_imagePath + "]");
+            }
+
+            if ((*l_imageExtendedVersion).empty() ||
+                (*l_imageExtendedVersion).length() <= constants::VALUE_2)
+            {
+                throw DbusException("Invalid extended version read for path [" +
+                                    l_imagePath + "]");
+            }
+
+            // return first two character from image name.
+            return (*l_imageExtendedVersion)
+                .substr(constants::VALUE_0, constants::VALUE_2);
+        }
+        throw std::runtime_error("No Image found with required priority.");
+    }
+    catch (const std::exception& l_ex)
+    {
+        logging::logMessage(l_ex.what());
+        return std::string{};
+    }
+}
 } // namespace dbusUtility
 } // namespace vpd
diff --git a/vpd-manager/include/utility/vpd_specific_utility.hpp b/vpd-manager/include/utility/vpd_specific_utility.hpp
index 83db199..4fc553d 100644
--- a/vpd-manager/include/utility/vpd_specific_utility.hpp
+++ b/vpd-manager/include/utility/vpd_specific_utility.hpp
@@ -687,5 +687,45 @@
 
     return l_rc;
 }
+
+/**
+ * @brief API to detect if system configuration is that of PowerVS system.
+ *
+ * @param[in] i_imValue - IM value of the system.
+ * @return true if it is PowerVS configuration, false otherwise.
+ */
+inline bool isPowerVsConfiguration(const types::BinaryVector& i_imValue)
+{
+    if (i_imValue.empty() || i_imValue.size() != constants::VALUE_4)
+    {
+        return false;
+    }
+
+    // Should be a 0x5000XX series system.
+    if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
+        i_imValue.at(1) == constants::HEX_VALUE_00)
+    {
+        std::string l_imagePrefix = dbusUtility::getImagePrefix();
+
+        // Check image for 0x500030XX series.
+        if ((i_imValue.at(2) == constants::HEX_VALUE_30) &&
+            ((l_imagePrefix == constants::powerVsImagePrefix_MY) ||
+             (l_imagePrefix == constants::powerVsImagePrefix_NY)))
+        {
+            logging::logMessage("PowerVS configuration");
+            return true;
+        }
+
+        // Check image for 0X500010XX series.
+        if ((i_imValue.at(2) == constants::HEX_VALUE_10) &&
+            ((l_imagePrefix == constants::powerVsImagePrefix_MZ) ||
+             (l_imagePrefix == constants::powerVsImagePrefix_NZ)))
+        {
+            logging::logMessage("PowerVS configuration");
+            return true;
+        }
+    }
+    return false;
+}
 } // namespace vpdSpecificUtility
 } // namespace vpd
diff --git a/vpd-manager/src/manager.cpp b/vpd-manager/src/manager.cpp
index eca1588..b608e0b 100644
--- a/vpd-manager/src/manager.cpp
+++ b/vpd-manager/src/manager.cpp
@@ -335,6 +335,13 @@
         {
             throw DbusException("Invalid IM value read from Dbus");
         }
+
+        if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
+        {
+            // TODO: Should booting be blocked in case of some
+            // misconfigurations?
+            return;
+        }
     }
     catch (const std::exception& l_ex)
     {