Enable guard support

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: I7bc6c0cdd3aa6d513f1a87d351b99069fc005339
diff --git a/analyzer/analyzer_main.cpp b/analyzer/analyzer_main.cpp
index 4041b6a..751bb55 100644
--- a/analyzer/analyzer_main.cpp
+++ b/analyzer/analyzer_main.cpp
@@ -126,6 +126,8 @@
         // Create and commit a PEL.
         uint32_t logId = std::get<1>(createPel(isoData, servData));
 
+        trace::inf("PEL created: PLID=0x%0" PRIx32, logId);
+
         // Gather/return information needed for dump.
         // TODO: Need ID from root cause. At the moment, HUID does not exist in
         //       devtree. Will need a better ID definition.
diff --git a/analyzer/callout.hpp b/analyzer/callout.hpp
index b49f97b..8c54793 100644
--- a/analyzer/callout.hpp
+++ b/analyzer/callout.hpp
@@ -181,6 +181,48 @@
 inline const ClockType ClockType::OSC_REF_CLOCK_0{"OSC_REF_CLOCK_0"};
 inline const ClockType ClockType::OSC_REF_CLOCK_1{"OSC_REF_CLOCK_1"};
 
+/** @brief Container class for guard service actions. */
+class GuardType
+{
+  public:
+    /** Do not guard. */
+    static const GuardType NONE;
+
+    /** Guard on fatal error (cannot recover resource). */
+    static const GuardType UNRECOVERABLE;
+
+    /** Guard on non-fatal error (can recover resource). */
+    static const GuardType PREDICTIVE;
+
+  private:
+    /**
+     * @brief Constructor from components.
+     * @param i_string The string representation of the procedure used for
+     *                 callouts.
+     */
+    explicit GuardType(const std::string& i_string) : iv_string(i_string) {}
+
+  private:
+    /** The string representation of the procedure used for callouts. */
+    const std::string iv_string;
+
+  public:
+    bool operator==(const GuardType& r) const
+    {
+        return this->iv_string == r.iv_string;
+    }
+
+    /** iv_string accessor */
+    std::string getString() const
+    {
+        return iv_string;
+    }
+};
+
+inline const GuardType GuardType::NONE{"GARD_NULL"};
+inline const GuardType GuardType::UNRECOVERABLE{"GARD_Unrecoverable"};
+inline const GuardType GuardType::PREDICTIVE{"GARD_Predictive"};
+
 } // namespace callout
 
 } // namespace analyzer
diff --git a/analyzer/resolution.cpp b/analyzer/resolution.cpp
index ae4edd0..8bf8b95 100644
--- a/analyzer/resolution.cpp
+++ b/analyzer/resolution.cpp
@@ -130,7 +130,21 @@
     callout["LocationCode"] = util::pdbg::getLocationCode(i_target);
     callout["Priority"]     = i_priority.getUserDataString();
     callout["Deconfigured"] = false;
-    callout["Guarded"]      = i_guard;
+    callout["Guarded"]      = false; // default
+
+    // Check if guard info should be added.
+    if (i_guard)
+    {
+        auto guardType = io_sd.queryGuardPolicy();
+
+        if (!(callout::GuardType::NONE == guardType))
+        {
+            callout["Guarded"]    = true;
+            callout["EntityPath"] = util::pdbg::getPhysBinPath(i_target);
+            callout["GuardType"]  = guardType.getString();
+        }
+    }
+
     io_sd.addCallout(callout);
 }
 
diff --git a/analyzer/service_data.hpp b/analyzer/service_data.hpp
index 953a96d..1ce991e 100644
--- a/analyzer/service_data.hpp
+++ b/analyzer/service_data.hpp
@@ -62,6 +62,18 @@
         return iv_isCheckstop;
     }
 
+    /** @return Returns the guard type based on current analysis policies. */
+    callout::GuardType queryGuardPolicy() const
+    {
+        // TODO: Manual execution of the analyzer (i.e. from the command line),
+        //       will eventually require the ability to not guard. This is
+        //       useful when we simply want to check for attentions in the
+        //       hardware with no service action.
+
+        return queryCheckstop() ? callout::GuardType::UNRECOVERABLE
+                                : callout::GuardType::PREDICTIVE;
+    }
+
     /**
      * @brief Add callout information to the callout list.
      * @param The JSON object for this callout.
diff --git a/test/resolution_test.cpp b/test/resolution_test.cpp
index df82b4a..82eb370 100644
--- a/test/resolution_test.cpp
+++ b/test/resolution_test.cpp
@@ -98,13 +98,28 @@
 //------------------------------------------------------------------------------
 
 void __calloutTarget(ServiceData& io_sd, const std::string& i_locCode,
-                     const callout::Priority& i_priority, bool i_guard)
+                     const callout::Priority& i_priority, bool i_guard,
+                     const std::string& i_guardPath)
 {
     nlohmann::json callout;
     callout["LocationCode"] = i_locCode;
     callout["Priority"]     = i_priority.getUserDataString();
     callout["Deconfigured"] = false;
-    callout["Guarded"]      = i_guard;
+    callout["Guarded"]      = false; // default
+
+    // Check if guard info should be added.
+    if (i_guard)
+    {
+        auto guardType = io_sd.queryGuardPolicy();
+
+        if (!(callout::GuardType::NONE == guardType))
+        {
+            callout["Guarded"]    = true;
+            callout["Guard Path"] = i_guardPath;
+            callout["Guard Type"] = guardType.getString();
+        }
+    }
+
     io_sd.addCallout(callout);
 }
 
@@ -133,7 +148,7 @@
     auto entityPath = __getUnitPath(locCode, iv_unitPath);
 
     // Add the actual callout to the service data.
-    __calloutTarget(io_sd, locCode, iv_priority, iv_guard);
+    __calloutTarget(io_sd, locCode, iv_priority, iv_guard, entityPath);
 
     // Add the callout FFDC to the service data.
     nlohmann::json ffdc;
@@ -158,7 +173,8 @@
     auto txPath = __getConnectedPath(rxPath, iv_busType);
 
     // Callout the TX endpoint.
-    __calloutTarget(io_sd, std::get<1>(txPath), iv_priority, iv_guard);
+    __calloutTarget(io_sd, std::get<1>(txPath), iv_priority, iv_guard,
+                    std::get<0>(txPath));
 
     // Add the callout FFDC to the service data.
     nlohmann::json ffdc;
@@ -184,10 +200,11 @@
     auto txPath = __getConnectedPath(rxPath, iv_busType);
 
     // Callout the RX endpoint.
-    __calloutTarget(io_sd, chipPath, iv_priority, iv_guard);
+    __calloutTarget(io_sd, chipPath, iv_priority, iv_guard, rxPath);
 
     // Callout the TX endpoint.
-    __calloutTarget(io_sd, std::get<1>(txPath), iv_priority, iv_guard);
+    __calloutTarget(io_sd, std::get<1>(txPath), iv_priority, iv_guard,
+                    std::get<0>(txPath));
 
     // Callout everything else in between.
     // TODO: For P10 (OMI bus and XBUS), the callout is simply the backplane.
@@ -316,6 +333,8 @@
     },
     {
         "Deconfigured": false,
+        "Guard Path": "/proc0/pib/perv39/eq7/fc1/core1",
+        "Guard Type": "GARD_Predictive",
         "Guarded": true,
         "LocationCode": "/proc0",
         "Priority": "H"
@@ -349,6 +368,8 @@
     s = R"([
     {
         "Deconfigured": false,
+        "Guard Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0",
+        "Guard Type": "GARD_Unrecoverable",
         "Guarded": true,
         "LocationCode": "/proc0",
         "Priority": "A"
@@ -396,18 +417,24 @@
     s = R"([
     {
         "Deconfigured": false,
+        "Guard Path": "/proc1/pib/perv24/pauc0/iohs0/smpgroup0",
+        "Guard Type": "GARD_Unrecoverable",
         "Guarded": true,
         "LocationCode": "/proc1",
         "Priority": "A"
     },
     {
         "Deconfigured": false,
+        "Guard Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0",
+        "Guard Type": "GARD_Unrecoverable",
         "Guarded": true,
         "LocationCode": "/proc0",
         "Priority": "B"
     },
     {
         "Deconfigured": false,
+        "Guard Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0/ocmb0",
+        "Guard Type": "GARD_Unrecoverable",
         "Guarded": true,
         "LocationCode": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0/ocmb0",
         "Priority": "C"
@@ -470,12 +497,16 @@
     s = R"([
     {
         "Deconfigured": false,
+        "Guard Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0",
+        "Guard Type": "GARD_Unrecoverable",
         "Guarded": true,
         "LocationCode": "/proc0",
         "Priority": "A"
     },
     {
         "Deconfigured": false,
+        "Guard Path": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0/ocmb0",
+        "Guard Type": "GARD_Unrecoverable",
         "Guarded": true,
         "LocationCode": "/proc0/pib/perv12/mc0/mi0/mcc0/omi0/ocmb0",
         "Priority": "A"
diff --git a/util/pdbg.cpp b/util/pdbg.cpp
index 2e370c1..fb36a8e 100644
--- a/util/pdbg.cpp
+++ b/util/pdbg.cpp
@@ -296,6 +296,38 @@
 
 //------------------------------------------------------------------------------
 
+std::vector<uint8_t> getPhysBinPath(pdbg_target* target)
+{
+    std::vector<uint8_t> binPath;
+
+    if (nullptr != target)
+    {
+#ifdef CONFIG_PHAL_API
+
+        ATTR_PHYS_BIN_PATH_Type value;
+        if (DT_GET_PROP(ATTR_PHYS_BIN_PATH, target, value))
+        {
+            // The attrirbute for this target does not exist. Get the immediate
+            // parent in the devtree path and try again. Note that if there is
+            // no parent target, nullptr will be returned and that will be
+            // checked above.
+            return getPhysBinPath(pdbg_target_parent(nullptr, target));
+        }
+
+        // Attribute was found. Copy the attribute array to the returned
+        // vector. Note that the reason we return the vector instead of just
+        // returning the array is because the array type and details only
+        // exists in this specific configuration.
+        binPath.insert(binPath.end(), value, value + sizeof(value));
+
+#endif
+    }
+
+    return binPath;
+}
+
+//------------------------------------------------------------------------------
+
 } // namespace pdbg
 
 } // namespace util
diff --git a/util/pdbg.hpp b/util/pdbg.hpp
index ec2f795..2fabcf3 100644
--- a/util/pdbg.hpp
+++ b/util/pdbg.hpp
@@ -110,6 +110,16 @@
  */
 std::string getPhysDevPath(pdbg_target* trgt);
 
+/**
+ * @return A vector of bytes representing the numerical values of the physical
+ *         device path (entity path) of the given target. An empty vector
+ *         indicates the target was null or the attribute does not exist for
+ *         this target or any parent targets along the device tree path.
+ * @note   This function requires PHAL APIs that are only available in certain
+ *         environments. If they do not exist, an empty vector is returned.
+ */
+std::vector<uint8_t> getPhysBinPath(pdbg_target* trgt);
+
 } // namespace pdbg
 
 } // namespace util