Infrastructure to support single FAB feature

This commit implements infrastructure to support single FAB feature.

Single FAB feature updates the IM value of the system planar or creates
PEL based on the IM value read from the planar and cache, considering
the system mode and type.

Change-Id: I77fe927de9e37e0ba70e0f4d661a25c586911433
Signed-off-by: Rekha Aparna <vrekhaaparna@ibm.com>
diff --git a/vpd-manager/include/single_fab.hpp b/vpd-manager/include/single_fab.hpp
index c5623c4..5e4fb76 100644
--- a/vpd-manager/include/single_fab.hpp
+++ b/vpd-manager/include/single_fab.hpp
@@ -1,5 +1,8 @@
 #pragma once
 
+#include "constants.hpp"
+#include "event_logger.hpp"
+
 #include <string>
 
 namespace vpd
@@ -12,8 +15,20 @@
  */
 class SingleFab
 {
-    // ToDo: public API to be implemented, which can be called by the user to
-    // support single FAB.
+  public:
+    /**
+     * @brief API to support single FAB feature.
+     *
+     * This API updates the IM value to the P11 series or creates PEL in invalid
+     * case based on the IM value read from the cache and planar, considering
+     * the system mode and image.
+     *
+     * System mode can be of field mode or lab mode and system image can be
+     * special or normal image.
+     *
+     * @return 0 on success, -1 in case of failure.
+     */
+    int singleFabImOverride() const noexcept;
 
   private:
     /**
@@ -50,10 +65,80 @@
      * @brief API to update IM value on system planar EEPROM path to P11 series.
      *
      * @param[in] i_currentImValuePlanar - current IM value in planar EEPROM.
-     *
-     * @return true if IM value is updated successfully, otherwise false.
      */
-    bool updateSystemImValueInVpdToP11Series(
+    void updateSystemImValueInVpdToP11Series(
         std::string i_currentImValuePlanar) const noexcept;
+
+    /**
+     * @brief API to check if it is a P10 system.
+     *
+     * @param[in] i_imValue - IM value of the system.
+     *
+     * @return true, if P10 system. Otherwise false.
+     */
+    inline bool isP10System(const std::string& i_imValue) const noexcept
+    {
+        try
+        {
+            return !(i_imValue.compare(constants::VALUE_0, constants::VALUE_4,
+                                       POWER10_IM_SERIES));
+        }
+        catch (const std::exception& l_ex)
+        {
+            EventLogger::createSyncPel(
+                types::ErrorType::InternalFailure,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                std::string(
+                    "Failed to check if system is of P10 series. Error : ") +
+                    l_ex.what(),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+            return false;
+        }
+    }
+
+    /**
+     * @brief API to check if it is a P11 system.
+     *
+     * @param[in] i_imValue - IM value of the system.
+     *
+     * @return true, if P11 system. Otherwise false.
+     */
+    inline bool isP11System(const std::string& i_imValue) const noexcept
+    {
+        try
+        {
+            return !(i_imValue.compare(constants::VALUE_0, constants::VALUE_4,
+                                       POWER11_IM_SERIES));
+        }
+        catch (const std::exception& l_ex)
+        {
+            EventLogger::createSyncPel(
+                types::ErrorType::InternalFailure,
+                types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+                std::string(
+                    "Failed to check if system is of P11 series. Error : ") +
+                    l_ex.what(),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+            return false;
+        }
+    }
+
+    /**
+     * @brief API to check if it is a valid IM series.
+     *
+     * This API checks if the provided IM value is of either P10 or P11 series.
+     *
+     * @param[in] i_imValue - IM value of the system.
+     *
+     * @return true, if valid IM series. Otherwise false.
+     */
+    inline bool isValidImSeries(const std::string& l_imValue) const noexcept
+    {
+        return (isP10System(l_imValue) || isP11System(l_imValue));
+    }
+
+    // valid IM series.
+    static constexpr auto POWER10_IM_SERIES = "5000";
+    static constexpr auto POWER11_IM_SERIES = "6000";
 };
 } // namespace vpd
diff --git a/vpd-manager/include/types.hpp b/vpd-manager/include/types.hpp
index 7dacb26..51ec239 100644
--- a/vpd-manager/include/types.hpp
+++ b/vpd-manager/include/types.hpp
@@ -184,7 +184,9 @@
     EssentialFru,
     GpioError,
     InternalFailure, /* Should be used for any generic firmware failure */
-    FruMissing /* Should be used in case of presence failure */
+    FruMissing, /* Should be used in case of presence failure */
+    SystemTypeMismatch,
+    UnknownSystemSettings
 };
 
 using InventoryCalloutData = std::tuple<std::string, CalloutPriority>;
diff --git a/vpd-manager/src/event_logger.cpp b/vpd-manager/src/event_logger.cpp
index 77de7c5..5eecc62 100644
--- a/vpd-manager/src/event_logger.cpp
+++ b/vpd-manager/src/event_logger.cpp
@@ -43,7 +43,11 @@
         {types::ErrorType::GpioError, "com.ibm.VPD.Error.GPIOError"},
         {types::ErrorType::InternalFailure,
          "xyz.openbmc_project.Common.Error.InternalFailure"},
-        {types::ErrorType::FruMissing, "com.ibm.VPD.Error.RequiredFRUMissing"}};
+        {types::ErrorType::FruMissing, "com.ibm.VPD.Error.RequiredFRUMissing"},
+        {types::ErrorType::SystemTypeMismatch,
+         "com.ibm.VPD.Error.SystemTypeMismatch"},
+        {types::ErrorType::UnknownSystemSettings,
+         "com.ibm.VPD.Error.UnknownSystemSettings"}};
 
 const std::unordered_map<types::CalloutPriority, std::string>
     EventLogger::m_priorityMap = {
diff --git a/vpd-manager/src/manager.cpp b/vpd-manager/src/manager.cpp
index 030f54e..be62094 100644
--- a/vpd-manager/src/manager.cpp
+++ b/vpd-manager/src/manager.cpp
@@ -9,6 +9,7 @@
 #include "parser.hpp"
 #include "parser_factory.hpp"
 #include "parser_interface.hpp"
+#include "single_fab.hpp"
 #include "types.hpp"
 #include "utility/dbus_utility.hpp"
 #include "utility/json_utility.hpp"
@@ -26,6 +27,21 @@
     const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
     m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
 {
+#ifdef IBM_SYSTEM
+    if (!dbusUtility::isChassisPowerOn())
+    {
+        SingleFab l_singleFab;
+        const int& l_rc = l_singleFab.singleFabImOverride();
+
+        if (l_rc == constants::FAILURE)
+        {
+            throw std::runtime_error(
+                std::string(__FUNCTION__) +
+                " : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced.");
+        }
+    }
+#endif
+
     try
     {
 #ifdef IBM_SYSTEM
diff --git a/vpd-manager/src/single_fab.cpp b/vpd-manager/src/single_fab.cpp
index 12197f2..ca07a1a 100644
--- a/vpd-manager/src/single_fab.cpp
+++ b/vpd-manager/src/single_fab.cpp
@@ -3,12 +3,14 @@
 #include "single_fab.hpp"
 
 #include "constants.hpp"
+#include "event_logger.hpp"
 #include "parser.hpp"
 #include "types.hpp"
 
 #include <nlohmann/json.hpp>
 #include <utility/common_utility.hpp>
 #include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
 
 namespace vpd
 {
@@ -136,7 +138,7 @@
     return false;
 }
 
-bool SingleFab::updateSystemImValueInVpdToP11Series(
+void SingleFab::updateSystemImValueInVpdToP11Series(
     std::string i_currentImValuePlanar) const noexcept
 {
     bool l_retVal{false};
@@ -148,6 +150,144 @@
             constants::VALUE_0, constants::VALUE_1,
             std::to_string(constants::VALUE_6)));
     }
-    return l_retVal;
+
+    if (!l_retVal)
+    {
+        EventLogger::createSyncPel(
+            types::ErrorType::InternalFailure,
+            types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+            std::string("Failed to update IM value to P11 series."),
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+    }
+}
+
+int SingleFab::singleFabImOverride() const noexcept
+{
+    const std::string& l_planarImValue = getImFromPlanar();
+    const std::string& l_eBmcImValue = getImFromPersistedLocation();
+    const bool& l_isFieldModeEnabled = isFieldModeEnabled();
+    const bool& l_isLabModeEnabled =
+        !l_isFieldModeEnabled;                       // used for understanding
+    const bool& l_isPowerVsImage = vpdSpecificUtility::isPowerVsImage();
+    const bool& l_isNormalImage = !l_isPowerVsImage; // used for understanding
+
+    if (!isValidImSeries(l_planarImValue))
+    {
+        // Create Errorlog for invalid IM series encountered
+        EventLogger::createSyncPel(
+            types::ErrorType::InvalidSystem, types::SeverityType::Error,
+            __FILE__, __FUNCTION__, 0,
+            std::string("Invalid IM found on the system planar, IM value : ") +
+                l_planarImValue,
+            std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+        return constants::SUCCESS;
+    }
+
+    if (!l_eBmcImValue.empty())
+    {
+        if (isP10System(l_eBmcImValue))
+        {
+            if (isP10System(l_planarImValue))
+            {
+                if (l_isFieldModeEnabled && l_isNormalImage)
+                {
+                    EventLogger::createSyncPel(
+                        types::ErrorType::SystemTypeMismatch,
+                        types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
+                        std::string("Mismatch in IM value found eBMC IM [") +
+                            l_eBmcImValue + std::string("] planar IM [") +
+                            l_planarImValue +
+                            std::string("] Field mode enabled [") +
+                            ((l_isFieldModeEnabled) ? "true" : "false") +
+                            std::string("]"),
+                        std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+                    return constants::FAILURE;
+                }
+            }
+            else if (isP11System(l_planarImValue))
+            {
+                if (!(l_isLabModeEnabled && l_isNormalImage))
+                {
+                    EventLogger::createSyncPel(
+                        types::ErrorType::SystemTypeMismatch,
+                        types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
+                        std::string("Mismatch in IM value found eBMC IM [") +
+                            l_eBmcImValue + std::string("] planar IM [") +
+                            l_planarImValue +
+                            std::string("] Field mode enabled [") +
+                            ((l_isFieldModeEnabled) ? "true" : "false") +
+                            std::string("]"),
+                        std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+                    return constants::FAILURE;
+                }
+            }
+        }
+        else if (isP11System(l_eBmcImValue))
+        {
+            if (l_isPowerVsImage)
+            {
+                EventLogger::createSyncPel(
+                    types::ErrorType::SystemTypeMismatch,
+                    types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
+                    std::string("Mismatch in IM value found eBMC IM [") +
+                        l_eBmcImValue + std::string("] planar IM [") +
+                        l_planarImValue +
+                        std::string("] Field mode enabled [") +
+                        ((l_isFieldModeEnabled) ? "true" : "false") +
+                        std::string("]"),
+                    std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+                return constants::FAILURE;
+            }
+            else
+            {
+                if (isP10System(l_planarImValue))
+                {
+                    updateSystemImValueInVpdToP11Series(l_planarImValue);
+                }
+            }
+        }
+    }
+    else
+    {
+        if (isP11System(l_planarImValue) && l_isPowerVsImage)
+        {
+            EventLogger::createSyncPel(
+                types::ErrorType::SystemTypeMismatch,
+                types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
+                std::string("Mismatch in IM value found eBMC IM [") +
+                    l_eBmcImValue + std::string("] planar IM [") +
+                    l_planarImValue + std::string("] Field mode enabled [") +
+                    ((l_isFieldModeEnabled) ? "true" : "false") +
+                    std::string("]"),
+                std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+            return constants::FAILURE;
+        }
+        else if (isP10System(l_planarImValue) && l_isNormalImage)
+        {
+            if (l_isLabModeEnabled)
+            {
+                EventLogger::createSyncPel(
+                    types::ErrorType::UnknownSystemSettings,
+                    types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
+                    std::string("Mismatch in IM value found eBMC IM [") +
+                        l_eBmcImValue + std::string("] planar IM [") +
+                        l_planarImValue +
+                        std::string("] Field mode enabled [") +
+                        ((l_isFieldModeEnabled) ? "true" : "false") +
+                        std::string("]"),
+                    std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+            }
+            else
+            {
+                updateSystemImValueInVpdToP11Series(l_planarImValue);
+            }
+        }
+    }
+    return constants::SUCCESS;
 }
 } // namespace vpd