openpower-pels: only fail on hw callouts

After further discussion with the IBM service and manufacturing team, it
was determined that firmware should only be automatically going to
Quiesce and preventing the system boot in QuiesceOnError mode when the
PEL has a hardware callout present (vs. any type of callout).

Tested:
- New unit test case
- Verified that an unrecoverable PEL with only a symbolic FRU and a
  maintenance procedure callout did not trigger the Quiesce logic
- Verified that a BMC log with a HW callout still triggers the Quiesce
  logic
- Verified a boot in simulation with Quiesce enabled

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I8912d59fd69b699c7da5a44da887cce4e987f6d2
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index 907a43d..f3487b3 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -651,7 +651,7 @@
     }
 
     // Now check if it has any type of callout
-    if (pel->isCalloutPresent())
+    if (pel->isHwCalloutPresent())
     {
         log<level::INFO>(
             "QuiesceOnHwError enabled, PEL severity not nonError or recovered, "
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index a6f0af3..de70b53 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -21,6 +21,7 @@
 #include "extended_user_data.hpp"
 #include "extended_user_header.hpp"
 #include "failing_mtms.hpp"
+#include "fru_identity.hpp"
 #include "json_utils.hpp"
 #include "log_id.hpp"
 #include "pel_rules.hpp"
@@ -506,7 +507,7 @@
     return callouts;
 }
 
-bool PEL::isCalloutPresent() const
+bool PEL::isHwCalloutPresent() const
 {
     auto pSRC = primarySRC();
     if (!pSRC)
@@ -522,7 +523,8 @@
             if (((*i).fruIdentity()))
             {
                 auto& fruId = (*i).fruIdentity();
-                if ((*fruId).failingComponentType() != 0)
+                if ((*fruId).failingComponentType() ==
+                    src::FRUIdentity::hardwareFRU)
                 {
                     calloutPresent = true;
                     break;
diff --git a/extensions/openpower-pels/pel.hpp b/extensions/openpower-pels/pel.hpp
index 649357f..5edcf10 100644
--- a/extensions/openpower-pels/pel.hpp
+++ b/extensions/openpower-pels/pel.hpp
@@ -287,11 +287,11 @@
     }
 
     /**
-     * @brief Returns true if any callout is present in the primary SRC
+     * @brief Returns true if a hardware callout is present in the primary SRC
      *
-     * @return true if callout present, false otherwise
+     * @return true if hardware callout present, false otherwise
      */
-    bool isCalloutPresent() const;
+    bool isHwCalloutPresent() const;
 
     /**
      * @brief Updates the system info data into HB extended user
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
index 322d0ca..352e0ed 100644
--- a/test/openpower-pels/pel_test.cpp
+++ b/test/openpower-pels/pel_test.cpp
@@ -209,7 +209,7 @@
     EXPECT_EQ(mtmsCount, 1);
     EXPECT_EQ(euhCount, 1);
     EXPECT_EQ(udCount, 2); // AD section and sysInfo section
-    ASSERT_FALSE(pel.isCalloutPresent());
+    ASSERT_FALSE(pel.isHwCalloutPresent());
 
     {
         // The same thing, but without the action flags specified
@@ -894,7 +894,7 @@
         ASSERT_TRUE(pel.primarySRC().value()->callouts());
         auto& callouts = pel.primarySRC().value()->callouts()->callouts();
         ASSERT_EQ(callouts.size(), 1);
-        ASSERT_TRUE(pel.isCalloutPresent());
+        ASSERT_TRUE(pel.isHwCalloutPresent());
 
         EXPECT_EQ(callouts[0]->priority(), 'H');
         EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P1");
@@ -1031,6 +1031,7 @@
     ASSERT_TRUE(pel.primarySRC().value()->callouts());
     const auto& callouts = pel.primarySRC().value()->callouts()->callouts();
     ASSERT_EQ(callouts.size(), 2);
+    ASSERT_TRUE(pel.isHwCalloutPresent());
 
     {
         EXPECT_EQ(callouts[0]->priority(), 'H');
@@ -1053,3 +1054,62 @@
     }
     fs::remove_all(dir);
 }
+
+// Test PELs with symblic FRU callout.
+TEST_F(PELTest, CreateWithJSONSymblicCalloutTest)
+{
+    PelFFDCfile ffdcFile;
+    ffdcFile.format = UserDataFormat::json;
+    ffdcFile.subType = 0xCA; // Callout JSON
+    ffdcFile.version = 1;
+
+    // Write these callouts to a JSON file and pass it into
+    // the PEL as an FFDC file.
+    auto inputJSON = R"([
+        {
+            "Priority": "M",
+            "Procedure": "SVCDOCS"
+        }
+    ])"_json;
+
+    auto s = inputJSON.dump();
+    std::vector<uint8_t> data{s.begin(), s.end()};
+    auto dir = makeTempDir();
+    ffdcFile.fd = writeFileAndGetFD(dir, data);
+
+    PelFFDC ffdc;
+    ffdc.push_back(std::move(ffdcFile));
+
+    AdditionalData ad;
+    NiceMock<MockDataInterface> dataIface;
+
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
+    message::Entry regEntry;
+    regEntry.name = "test";
+    regEntry.subsystem = 5;
+    regEntry.actionFlags = 0xC000;
+    regEntry.src.type = 0xBD;
+    regEntry.src.reasonCode = 0x1234;
+
+    PEL pel{regEntry, 42,   5,        phosphor::logging::Entry::Level::Error,
+            ad,       ffdc, dataIface};
+
+    ASSERT_TRUE(pel.valid());
+    ASSERT_TRUE(pel.primarySRC().value()->callouts());
+    const auto& callouts = pel.primarySRC().value()->callouts()->callouts();
+    ASSERT_EQ(callouts.size(), 1);
+    ASSERT_FALSE(pel.isHwCalloutPresent());
+
+    {
+        EXPECT_EQ(callouts[0]->priority(), 'M');
+        EXPECT_EQ(callouts[0]->locationCode(), "");
+
+        auto& fru = callouts[0]->fruIdentity();
+        EXPECT_EQ(fru->getMaintProc().value(), "SVCDOCS");
+    }
+    fs::remove_all(dir);
+}