pel-quiesce: quiesce on error pels with callout

The PEL requirement is that if a nonInfo host PEL is logged, it has a
callout of any type within it, and the QuiesceOnHwError is enabled,
then the boot block / quiesce functionality should be executed.

Tested:

1) Verified nothing occurs when QuiesceOnHwError is not enabled

2) Injected info PEL with no callout and unrecoverable error with no
   callout with QuiesceOnHwError enabled and verified no actions were
   taken.

3) Injected Error PEL with callout and QuiesceOnHwError enabled
   and verified boot block logic was enabled.

Error inject which will create PEL with callout:
busctl call xyz.openbmc_project.Logging /xyz/openbmc_project/logging xyz.openbmc_project.Logging.Create Create ssa{ss} org.open_power.Logging.Error.TestError1 xyz.openbmc_project.Logging.Entry.Level.Error 0

Journal from fail:
Sep 09 14:03:30 w56 phosphor-log-manager[264]: Created PEL 0x50000266 (BMC ID 340) with SRC BD802003
Sep 09 14:03:30 w56 phosphor-log-manager[264]: QuiesceOnHwError enabled, PEL severity not nonError, and callout is present
Sep 09 14:03:30 w56 phosphor-log-manager[264]: QuiesceOnError set and callout present
Sep 09 14:03:30 w56 systemd[1]: Reached target Quiesce Target.

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I09e3ced608ffd2661d9cf015a24d4e0d8a6a84bd
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index fa6bc59..3c9b2cb 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -156,6 +156,9 @@
             log<level::ERR>("Unable to add PEL to Repository",
                             entry("PEL_ID=0x%X", pel->id()));
         }
+
+        // Check if firmware should quiesce system due to error
+        checkPelAndQuiesce(pel);
     }
     else
     {
@@ -349,6 +352,9 @@
     // Activate any resulting service indicators if necessary
     auto policy = service_indicators::getPolicy(*_dataIface);
     policy->activate(*pel);
+
+    // Check if firmware should quiesce system due to error
+    checkPelAndQuiesce(pel);
 }
 
 sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID)
@@ -580,5 +586,29 @@
     return {0, 0};
 }
 
+void Manager::checkPelAndQuiesce(std::unique_ptr<openpower::pels::PEL>& pel)
+{
+    if (pel->userHeader().severity() ==
+        static_cast<uint8_t>(SeverityType::nonError))
+    {
+        log<level::DEBUG>("PEL severity informational. no quiesce needed");
+        return;
+    }
+    if (!_logManager.isQuiesceOnErrorEnabled())
+    {
+        log<level::DEBUG>("QuiesceOnHwError not enabled, no quiesce needed");
+        return;
+    }
+
+    // Now check if it has any type of callout
+    if (pel->isCalloutPresent())
+    {
+        log<level::INFO>("QuiesceOnHwError enabled, PEL severity not nonError, "
+                         "and callout is present");
+
+        _logManager.quiesceOnError(pel->obmcLogID());
+    }
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/manager.hpp b/extensions/openpower-pels/manager.hpp
index 59c2e0d..e726812 100644
--- a/extensions/openpower-pels/manager.hpp
+++ b/extensions/openpower-pels/manager.hpp
@@ -301,6 +301,17 @@
     void pelFileDeleted(sdeventplus::source::IO& io, int fd, uint32_t revents);
 
     /**
+     * @brief Check if the input PEL should cause a quiesce of the system
+     *
+     * If QuiesceOnHwError is enabled within phosphor-settings and the PEL
+     * from the host has a severity which is not SeverityType::nonError then
+     * execute the quiesce and boot block logic.
+     *
+     * @param[in] pel - The PEL to check
+     */
+    void checkPelAndQuiesce(std::unique_ptr<openpower::pels::PEL>& pel);
+
+    /**
      * @brief Reference to phosphor-logging's Manager class
      */
     phosphor::logging::internal::Manager& _logManager;
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 64b0f59..f66d985 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -464,6 +464,34 @@
     return callouts;
 }
 
+bool PEL::isCalloutPresent() const
+{
+    auto pSRC = primarySRC();
+    if (!pSRC)
+    {
+        return false;
+    }
+
+    bool calloutPresent = false;
+    if ((*pSRC)->callouts())
+    {
+        for (auto& i : (*pSRC)->callouts()->callouts())
+        {
+            if (((*i).fruIdentity()))
+            {
+                auto& fruId = (*i).fruIdentity();
+                if ((*fruId).failingComponentType() != 0)
+                {
+                    calloutPresent = true;
+                    break;
+                }
+            }
+        }
+    }
+
+    return calloutPresent;
+}
+
 namespace util
 {
 
diff --git a/extensions/openpower-pels/pel.hpp b/extensions/openpower-pels/pel.hpp
index 2cfa8f3..d48b346 100644
--- a/extensions/openpower-pels/pel.hpp
+++ b/extensions/openpower-pels/pel.hpp
@@ -284,6 +284,13 @@
         return static_cast<TransmissionState>(_uh->hmcTransmissionState());
     }
 
+    /**
+     * @brief Returns true if any callout is present in the primary SRC
+     *
+     * @return true if callout present, false otherwise
+     */
+    bool isCalloutPresent() const;
+
   private:
     /**
      * @brief Builds the section objects from a PEL data buffer
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
index 0680185..e1697ac 100644
--- a/test/openpower-pels/pel_test.cpp
+++ b/test/openpower-pels/pel_test.cpp
@@ -204,6 +204,7 @@
     EXPECT_EQ(mtmsCount, 1);
     EXPECT_EQ(euhCount, 1);
     EXPECT_EQ(udCount, 2); // AD section and sysInfo section
+    ASSERT_FALSE(pel.isCalloutPresent());
 
     {
         // The same thing, but without the action flags specified
@@ -860,6 +861,7 @@
         ASSERT_TRUE(pel.primarySRC().value()->callouts());
         auto& callouts = pel.primarySRC().value()->callouts()->callouts();
         ASSERT_EQ(callouts.size(), 1);
+        ASSERT_TRUE(pel.isCalloutPresent());
 
         EXPECT_EQ(callouts[0]->priority(), 'H');
         EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P1");