plugin for LPC timeout callouts

Change-Id: I39fed3c1ba5a16283c33c5072479f24c9c69a208
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
diff --git a/analyzer/plugins/p10-plugins.cpp b/analyzer/plugins/p10-plugins.cpp
index 7e20d2d..a3ac1ca 100644
--- a/analyzer/plugins/p10-plugins.cpp
+++ b/analyzer/plugins/p10-plugins.cpp
@@ -59,9 +59,61 @@
     }
 }
 
+/**
+ * @brief Queries for an LPC timeout. If present, will callout all appropriate
+ *        hardware.
+ */
+void lpc_timeout(unsigned int, const libhei::Chip& i_chip,
+                 ServiceData& io_servData)
+{
+    auto target = util::pdbg::getTrgt(i_chip);
+    auto path   = util::pdbg::getPath(target);
+
+    if (util::pdbg::queryLpcTimeout(target))
+    {
+        trace::inf("LPC timeout detected on %s", path);
+
+        // Callout the PNOR.
+        io_servData.calloutPart(callout::PartType::PNOR,
+                                callout::Priority::MED);
+
+        // Callout the associated clock, no guard.
+        auto chipPos = util::pdbg::getChipPos(target);
+        if (0 == chipPos)
+        {
+            // Clock 0 is hardwired to proc 0.
+            io_servData.calloutClock(callout::ClockType::OSC_REF_CLOCK_0,
+                                     callout::Priority::MED, false);
+        }
+        else if (1 == chipPos)
+        {
+            // Clock 1 is hardwired to proc 1.
+            io_servData.calloutClock(callout::ClockType::OSC_REF_CLOCK_1,
+                                     callout::Priority::MED, false);
+        }
+        else
+        {
+            trace::err("LPC timeout on unexpected processor: %s", path);
+        }
+
+        // Callout the processor, no guard.
+        io_servData.calloutTarget(target, callout::Priority::MED, false);
+    }
+    else
+    {
+        trace::inf("No LPC timeout detected on %s", path);
+
+        io_servData.calloutProcedure(callout::Procedure::NEXTLVL,
+                                     callout::Priority::HIGH);
+    }
+}
+
 } // namespace P10
 
 PLUGIN_DEFINE_NS(P10_10, P10, pll_unlock);
 PLUGIN_DEFINE_NS(P10_20, P10, pll_unlock);
 
+PLUGIN_DEFINE_NS(P10_10, P10, lpc_timeout);
+PLUGIN_DEFINE_NS(P10_20, P10, lpc_timeout);
+
 } // namespace analyzer
diff --git a/test/meson.build b/test/meson.build
index 45c5749..b34dda2 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -38,6 +38,7 @@
   'bin_stream_test',
   'ffdc_file_test',
   'resolution_test',
+  'test-lpc-timeout',
   'test-pll-unlock',
 ]
 
@@ -56,11 +57,15 @@
     '../util/temporary_file.cpp',
 )
 
+sim_src = files(
+  'pdbg-sim-only.cpp',
+)
+
 foreach t : tests
     test(t,
          executable(t.underscorify(),
                     [ files(t + '.cpp'), pdbg_test_dtb,
-                      analyzer_src, util_src ],
+                      analyzer_src, util_src, sim_src ],
                     dependencies : [ libhei_dep, libpdbg_dep, gtest_dep ],
                     cpp_args : test_arg,
                     include_directories : incdir),
diff --git a/test/pdbg-sim-only.cpp b/test/pdbg-sim-only.cpp
new file mode 100644
index 0000000..efeee51
--- /dev/null
+++ b/test/pdbg-sim-only.cpp
@@ -0,0 +1,36 @@
+//------------------------------------------------------------------------------
+// IMPORTANT:
+// This file will ONLY be built in CI test and should be used for any functions
+// that require addition support to simulate in CI test. Any functions that will
+// work out-of-the-box in CI test with use of the fake device tree should be put
+// in `pdbg.cpp`.
+//------------------------------------------------------------------------------
+
+#include <assert.h>
+
+#include <util/pdbg.hpp>
+#include <util/trace.hpp>
+
+//------------------------------------------------------------------------------
+
+// Using this to fake the value returned from the simulation-only version of
+// util::pdbg::queryLpcTimeout().
+bool g_lpcTimeout = false;
+
+namespace util
+{
+namespace pdbg
+{
+
+// This is the simulated version of this function.
+bool queryLpcTimeout(pdbg_target* target)
+{
+    // Must be a processor target.
+    assert(TYPE_PROC == getTrgtType(target));
+
+    // Instead of the SBE chip-op, use the faked value.
+    return g_lpcTimeout;
+}
+
+} // namespace pdbg
+} // namespace util
diff --git a/test/test-lpc-timeout.cpp b/test/test-lpc-timeout.cpp
new file mode 100644
index 0000000..89d2336
--- /dev/null
+++ b/test/test-lpc-timeout.cpp
@@ -0,0 +1,121 @@
+#include <stdio.h>
+
+#include <analyzer/plugins/plugin.hpp>
+#include <analyzer/util.hpp>
+#include <util/pdbg.hpp>
+#include <util/trace.hpp>
+
+#include "gtest/gtest.h"
+
+//------------------------------------------------------------------------------
+
+using namespace analyzer;
+
+extern bool g_lpcTimeout;
+
+// Test #1: - no LPC timeout
+TEST(LpcTimeout, TestSet1)
+{
+    pdbg_targets_init(nullptr);
+
+    g_lpcTimeout = false; // no timeout
+
+    libhei::Chip chip{util::pdbg::getTrgt("/proc0"), P10_20};
+
+    auto plugin = PluginMap::getSingleton().get(chip.getType(), "lpc_timeout");
+
+    ServiceData sd{libhei::Signature{}, AnalysisType::SYSTEM_CHECKSTOP,
+                   libhei::IsolationData{}};
+
+    plugin(0, chip, sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "Priority": "H",
+        "Procedure": "next_level_support"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Procedure Callout",
+        "Priority": "high",
+        "Procedure": "next_level_support"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}
+
+// Test #2: - LPC timeout
+TEST(LpcTimeout, TestSet2)
+{
+    pdbg_targets_init(nullptr);
+
+    g_lpcTimeout = true; // force timeout
+
+    libhei::Chip chip{util::pdbg::getTrgt("/proc0"), P10_20};
+
+    auto plugin = PluginMap::getSingleton().get(chip.getType(), "lpc_timeout");
+
+    ServiceData sd{libhei::Signature{}, AnalysisType::SYSTEM_CHECKSTOP,
+                   libhei::IsolationData{}};
+
+    plugin(0, chip, sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "/bmc0",
+        "Priority": "M"
+    },
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "P0",
+        "Priority": "M"
+    },
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "/proc0",
+        "Priority": "M"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Part Callout",
+        "Part Type": "PNOR",
+        "Priority": "medium"
+    },
+    {
+        "Callout Type": "Clock Callout",
+        "Clock Type": "OSC_REF_CLOCK_0",
+        "Priority": "medium"
+    },
+    {
+        "Callout Type": "Hardware Callout",
+        "Guard": false,
+        "Priority": "medium",
+        "Target": "/proc0"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}
diff --git a/util/meson.build b/util/meson.build
index d72f515..4d99e51 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -4,6 +4,7 @@
     'dbus.cpp',
     'ffdc_file.cpp',
     'pdbg.cpp',
+    'pdbg-no-sim.cpp',
     'temporary_file.cpp',
 )
 
diff --git a/util/pdbg-no-sim.cpp b/util/pdbg-no-sim.cpp
new file mode 100644
index 0000000..8237052
--- /dev/null
+++ b/util/pdbg-no-sim.cpp
@@ -0,0 +1,49 @@
+//------------------------------------------------------------------------------
+// IMPORTANT:
+// This file will NOT be built in CI test and should be used for any functions
+// that require addition support to simulate in CI test. Any functions that will
+// work out-of-the-box in CI test with use of the fake device tree should be put
+// in `pdbg.cpp`.
+//------------------------------------------------------------------------------
+
+#include <assert.h>
+
+extern "C"
+{
+#include <libpdbg_sbe.h>
+}
+
+#include <util/pdbg.hpp>
+#include <util/trace.hpp>
+
+using namespace analyzer;
+
+namespace util
+{
+
+namespace pdbg
+{
+
+//------------------------------------------------------------------------------
+
+bool queryLpcTimeout(pdbg_target* target)
+{
+    // Must be a processor target.
+    assert(TYPE_PROC == getTrgtType(target));
+
+    uint32_t result = 0;
+    if (0 != sbe_lpc_timeout(util::pdbg::getPibTrgt(target), &result))
+    {
+        trace::err("sbe_lpc_timeout() failed: target=%s", getPath(target));
+        result = 0; // just in case
+    }
+
+    // 0 if no timeout, 1 if LPC timeout occurred.
+    return (0 != result);
+}
+
+//------------------------------------------------------------------------------
+
+} // namespace pdbg
+
+} // namespace util
diff --git a/util/pdbg.cpp b/util/pdbg.cpp
index c8c8a4f..a69d079 100644
--- a/util/pdbg.cpp
+++ b/util/pdbg.cpp
@@ -1,3 +1,10 @@
+//------------------------------------------------------------------------------
+// IMPORTANT:
+// This file will be built in CI test and should work out-of-the-box in CI test
+// with use of the fake device tree. Any functions that require addition support
+// to simulate in CI test should be put in `pdbg_no_sim.cpp`.
+//------------------------------------------------------------------------------
+
 #include <assert.h>
 #include <config.h>
 
diff --git a/util/pdbg.hpp b/util/pdbg.hpp
index 1dea6ae..b8af56f 100644
--- a/util/pdbg.hpp
+++ b/util/pdbg.hpp
@@ -145,6 +145,12 @@
  */
 std::vector<uint8_t> getPhysBinPath(pdbg_target* trgt);
 
+/**
+ * @brief  Uses an SBE chip-op to query if there has been an LPC timeout.
+ * @return True, if there was an LPC timeout. False, otherwise.
+ */
+bool queryLpcTimeout(pdbg_target* target);
+
 } // namespace pdbg
 
 } // namespace util