PEL: Support MfgSeverity & MfgAction flags PEL reg fields

This is specifically for manufacturing mode.
Look for mfgSeverity and mfgAction flags being set in incoming PELs and
if it is then check for dbus property for QuiesceOnHwError and if it is
found then override the severity value with mfg value.

Signed-off-by: Sumit Kumar <sumit_kumar@in.ibm.com>
Change-Id: Icd590b0e2732df8aa9c2935078528dda8fd4f996
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index faf035d..2ef99f4 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -35,6 +35,7 @@
 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
 constexpr auto vpdManager = "com.ibm.VPD.Manager";
 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
+constexpr auto logSetting = "xyz.openbmc_project.Settings";
 } // namespace service_name
 
 namespace object_path
@@ -52,6 +53,7 @@
 constexpr auto enableHostPELs =
     "/xyz/openbmc_project/logging/send_event_logs_to_host";
 constexpr auto vpdManager = "/com/ibm/VPD/Manager";
+constexpr auto logSetting = "/xyz/openbmc_project/logging/settings";
 } // namespace object_path
 
 namespace interface
@@ -76,6 +78,7 @@
 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
 constexpr auto operationalStatus =
     "xyz.openbmc_project.State.Decorator.OperationalStatus";
+constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings";
 } // namespace interface
 
 using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
@@ -519,5 +522,34 @@
     return std::get<std::vector<std::string>>(names);
 }
 
+bool DataInterface::getQuiesceOnError() const
+{
+    bool ret = false;
+
+    try
+    {
+        auto service =
+            getService(object_path::logSetting, interface::logSetting);
+        if (!service.empty())
+        {
+            DBusValue value;
+            getProperty(service, object_path::logSetting, interface::logSetting,
+                        "QuiesceOnHwError", value);
+
+            ret = std::get<bool>(value);
+        }
+    }
+    catch (const std::exception& e)
+    {
+        log<level::WARNING>(
+            fmt::format("Failed reading QuiesceOnHwError property from "
+                        "Interface: {} exception: {}",
+                        interface::logSetting, e.what())
+                .c_str());
+    }
+
+    return ret;
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index 3bfb5dc..d18223a 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -295,6 +295,13 @@
     virtual void setFunctional(const std::string& objectPath,
                                bool functional) const = 0;
 
+    /**
+     * @brief Returns the manufacturing QuiesceOnError property
+     *
+     * @return bool - Manufacturing QuiesceOnError property
+     */
+    virtual bool getQuiesceOnError() const = 0;
+
   protected:
     /**
      * @brief Sets the host on/off state and runs any
@@ -554,6 +561,13 @@
     void setFunctional(const std::string& objectPath,
                        bool functional) const override;
 
+    /**
+     * @brief Returns the manufacturing QuiesceOnError property
+     *
+     * @return bool - Manufacturing QuiesceOnError property
+     */
+    bool getQuiesceOnError() const override;
+
   private:
     /**
      * @brief Reads the BMC firmware version string and puts it into
diff --git a/extensions/openpower-pels/user_header.cpp b/extensions/openpower-pels/user_header.cpp
index a7d8023..a1b6194 100644
--- a/extensions/openpower-pels/user_header.cpp
+++ b/extensions/openpower-pels/user_header.cpp
@@ -61,75 +61,123 @@
     _eventScope = entry.eventScope.value_or(
         static_cast<uint8_t>(EventScope::entirePlatform));
 
-    // Get the severity from the registry if it's there, otherwise get it
-    // from the OpenBMC event log severity value.
-    if (!entry.severity)
     {
-        _eventSeverity = convertOBMCSeverityToPEL(severity);
-    }
-    else
-    {
-        // Find the severity possibly dependent on the system type.
-        auto sev = getSeverity(entry.severity.value(), dataIface);
-        if (sev)
+        bool mfgSevStatus = false;
+        bool mfgActionFlagStatus = false;
+        std::optional<uint8_t> sev = std::nullopt;
+        uint16_t val = 0;
+
+        // Get the mfg severity & action flags
+        if (entry.mfgSeverity || entry.mfgActionFlags)
         {
-            _eventSeverity = *sev;
+            if (entry.mfgSeverity)
+            {
+                // Find the mf severity possibly dependent on the system type.
+                sev = getSeverity(entry.mfgSeverity.value(), dataIface);
+            }
+
+            if (entry.mfgActionFlags)
+            {
+                // Find the mfg action flags
+                val = entry.mfgActionFlags.value();
+            }
+
+            if (sev || val)
+            {
+                bool mfgProp = dataIface.getQuiesceOnError();
+                if (mfgProp)
+                {
+                    if (sev)
+                    {
+                        _eventSeverity = *sev;
+                        mfgSevStatus = true;
+                    }
+
+                    if (val)
+                    {
+                        _actionFlags = val;
+                        mfgActionFlagStatus = true;
+                    }
+                }
+            }
+        }
+
+        if (!mfgSevStatus)
+        {
+            // Get the severity from the registry if it's there, otherwise get
+            // it from the OpenBMC event log severity value.
+            if (!entry.severity)
+            {
+                _eventSeverity = convertOBMCSeverityToPEL(severity);
+            }
+            else
+            {
+                // Find the severity possibly dependent on the system type.
+                auto sev = getSeverity(entry.severity.value(), dataIface);
+                if (sev)
+                {
+                    _eventSeverity = *sev;
+                }
+                else
+                {
+                    // Either someone  screwed up the message registry
+                    // or getSystemNames failed.
+                    std::string types;
+                    log<level::ERR>(
+                        "Failed finding the severity in the message registry",
+                        phosphor::logging::entry("ERROR=%s",
+                                                 entry.name.c_str()));
+
+                    // Have to choose something, just use informational.
+                    _eventSeverity = 0;
+                }
+            }
+        }
+
+        // Convert Critical error (0x50) to Critical Error-System Termination
+        // (0x51), if the AdditionalData is set to SYSTEM_TERM
+        auto sevLevel = additionalData.getValue("SEVERITY_DETAIL");
+        if ((_eventSeverity & 0xF0) == 0x50)
+        {
+            if (sevLevel.value_or("") == "SYSTEM_TERM")
+            {
+                // Change to Critical Error, System Termination
+                _eventSeverity = 0x51;
+            }
+        }
+
+        if (entry.eventType)
+        {
+            _eventType = *entry.eventType;
         }
         else
         {
-            // Either someone  screwed up the message registry
-            // or getSystemNames failed.
-            std::string types;
-            log<level::ERR>(
-                "Failed finding the severity in the message registry",
-                phosphor::logging::entry("ERROR=%s", entry.name.c_str()));
-
-            // Have to choose something, just use informational.
-            _eventSeverity = 0;
+            // There are different default event types for info errors
+            // vs non info ones.
+            auto sevType = static_cast<SeverityType>(_eventSeverity & 0xF0);
+            _eventType =
+                (sevType == SeverityType::nonError)
+                    ? static_cast<uint8_t>(EventType::miscInformational)
+                    : static_cast<uint8_t>(EventType::notApplicable);
         }
-    }
 
-    // Convert Critical error (0x50) to Critical Error-System Termination
-    // (0x51), if the AdditionalData is set to SYSTEM_TERM
-    auto sevLevel = additionalData.getValue("SEVERITY_DETAIL");
-    if ((_eventSeverity & 0xF0) == 0x50)
-    {
-        if (sevLevel.value_or("") == "SYSTEM_TERM")
+        _reserved4Byte1 = 0;
+
+        // No uses for problem domain or vector
+        _problemDomain = 0;
+        _problemVector = 0;
+
+        // These will be set in pel_rules::check() if they're still
+        // at the default value.
+        if (!mfgActionFlagStatus)
         {
-            // Change to Critical Error, System Termination
-            _eventSeverity = 0x51;
+            _actionFlags = entry.actionFlags.value_or(actionFlagsDefault);
         }
+
+        _states = 0;
+
+        _valid = true;
     }
-
-    // TODO: ibm-dev/dev/#1144 Handle manufacturing sev & action flags
-
-    if (entry.eventType)
-    {
-        _eventType = *entry.eventType;
-    }
-    else
-    {
-        // There are different default event types for info errors
-        // vs non info ones.
-        auto sevType = static_cast<SeverityType>(_eventSeverity & 0xF0);
-        _eventType = (sevType == SeverityType::nonError)
-                         ? static_cast<uint8_t>(EventType::miscInformational)
-                         : static_cast<uint8_t>(EventType::notApplicable);
-    }
-
-    _reserved4Byte1 = 0;
-
-    // No uses for problem domain or vector
-    _problemDomain = 0;
-    _problemVector = 0;
-
-    // These will be set in pel_rules::check() if they're still
-    // at the default value.
-    _actionFlags = entry.actionFlags.value_or(actionFlagsDefault);
-
-    _states = 0;
-
-    _valid = true;
 }
 
 UserHeader::UserHeader(Stream& pel)
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index 4196ee7..1116988 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -44,6 +44,7 @@
     MOCK_METHOD(void, setFunctional, (const std::string&, bool),
                 (const override));
     MOCK_METHOD(std::vector<uint8_t>, getSystemIMKeyword, (), (const override));
+    MOCK_METHOD(bool, getQuiesceOnError, (), (const override));
 
     void changeHostState(bool newState)
     {
diff --git a/test/openpower-pels/user_header_test.cpp b/test/openpower-pels/user_header_test.cpp
index 9c38ec1..a4bc3cb 100644
--- a/test/openpower-pels/user_header_test.cpp
+++ b/test/openpower-pels/user_header_test.cpp
@@ -275,3 +275,36 @@
     ASSERT_EQ(uh.eventType(), 0);
     ASSERT_EQ(uh.scope(), 0x03);
 }
+
+// Test that the event severity & action flags override
+// when QuiesceOnHwError is set
+TEST(UserHeaderTest, UseEventLogQuiesceOnErrorTest)
+{
+    using namespace openpower::pels::message;
+    Entry regEntry;
+
+    regEntry.name = "test";
+    regEntry.subsystem = 5;
+    regEntry.actionFlags = 0xC000;
+    regEntry.eventType = 1;
+    regEntry.eventScope = 2;
+    regEntry.severity = {{"", 0x40}, {"systemB", 0x10}, {"systemA", 0x00}};
+
+    // set the value for mfg severity and action flags
+    regEntry.mfgSeverity = {{"systemA", 0x20}};
+    regEntry.mfgActionFlags = 0xF000;
+
+    std::vector<std::string> names{"systemA"};
+
+    MockDataInterface dataIface;
+    AdditionalData ad;
+
+    EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
+    EXPECT_CALL(dataIface, getQuiesceOnError).WillOnce(Return(true));
+
+    UserHeader uh(regEntry, phosphor::logging::Entry::Level::Error, ad,
+                  dataIface);
+
+    EXPECT_EQ(uh.severity(), 0x20);
+    EXPECT_EQ(uh.actionFlags(), 0xF000);
+}