ConnectedCalloutResolution support

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: Id1b215d70ca98ae18528f91162ff4d99de9e9ce8
diff --git a/analyzer/resolution.cpp b/analyzer/resolution.cpp
index fb451aa..d6cd174 100644
--- a/analyzer/resolution.cpp
+++ b/analyzer/resolution.cpp
@@ -44,6 +44,76 @@
 
 //------------------------------------------------------------------------------
 
+// Helper function to get the connected target on the other side of the
+// given bus.
+pdbg_target* __getConnectedTarget(pdbg_target* i_rxTarget,
+                                  const callout::BusType& i_busType)
+{
+    assert(nullptr != i_rxTarget);
+
+    pdbg_target* txTarget = nullptr;
+
+    auto rxType        = util::pdbg::getTrgtType(i_rxTarget);
+    std::string rxPath = util::pdbg::getPath(i_rxTarget);
+
+    if (callout::BusType::SMP_BUS == i_busType &&
+        util::pdbg::TYPE_IOLINK == rxType)
+    {
+        // TODO: Will need to reference some sort of data that can tell us how
+        //       the processors are connected in the system. For now, return the
+        //       RX target to avoid returning a nullptr.
+        trace::inf("No support to get peer target on SMP bus");
+        txTarget = i_rxTarget;
+    }
+    else if (callout::BusType::OMI_BUS == i_busType &&
+             util::pdbg::TYPE_OMI == rxType)
+    {
+        // This is a bit clunky. The pdbg APIs only give us the ability to
+        // iterate over the children instead of just returning a list. So we'll
+        // push all the children to a list and go from there.
+        std::vector<pdbg_target*> childList;
+
+        pdbg_target* childTarget = nullptr;
+        pdbg_for_each_target("ocmb", i_rxTarget, childTarget)
+        {
+            if (nullptr != childTarget)
+            {
+                childList.push_back(childTarget);
+            }
+        }
+
+        // We know there should only be one OCMB per OMI.
+        if (1 != childList.size())
+        {
+            throw std::logic_error("Invalid child list size for " + rxPath);
+        }
+
+        // Get the connected target.
+        txTarget = childList.front();
+    }
+    else if (callout::BusType::OMI_BUS == i_busType &&
+             util::pdbg::TYPE_OCMB == rxType)
+    {
+        txTarget = pdbg_target_parent("omi", i_rxTarget);
+        if (nullptr == txTarget)
+        {
+            throw std::logic_error("No parent OMI found for " + rxPath);
+        }
+    }
+    else
+    {
+        // This would be a code bug.
+        throw std::logic_error("Unsupported config: i_rxTarget=" + rxPath +
+                               " i_busType=" + i_busType.getString());
+    }
+
+    assert(nullptr != txTarget); // just in case we missed something above
+
+    return txTarget;
+}
+
+//------------------------------------------------------------------------------
+
 void HardwareCalloutResolution::resolve(ServiceData& io_sd) const
 {
     // Get the target for the hardware callout.
@@ -73,6 +143,39 @@
 
 //------------------------------------------------------------------------------
 
+void ConnectedCalloutResolution::resolve(ServiceData& io_sd) const
+{
+    // Get the chip target from the root cause signature.
+    auto chipTarget = __getRootCauseChipTarget(io_sd);
+
+    // Get the endpoint target for the receiving side of the bus.
+    auto rxTarget = __getUnitTarget(chipTarget, iv_unitPath);
+
+    // Get the endpoint target for the transfer side of the bus.
+    auto txTarget = __getConnectedTarget(rxTarget, iv_busType);
+
+    // Callout the TX endpoint.
+    nlohmann::json txCallout;
+    txCallout["LocationCode"] = util::pdbg::getLocationCode(txTarget);
+    txCallout["Priority"]     = iv_priority.getUserDataString();
+    io_sd.addCallout(txCallout);
+
+    // Guard the TX endpoint.
+    Guard txGuard =
+        io_sd.addGuard(util::pdbg::getPhysDevPath(txTarget), iv_guard);
+
+    // Add the callout FFDC to the service data.
+    nlohmann::json ffdc;
+    ffdc["Callout Type"] = "Connected Callout";
+    ffdc["Bus Type"]     = iv_busType.getString();
+    ffdc["Target"]       = util::pdbg::getPhysDevPath(txTarget);
+    ffdc["Priority"]     = iv_priority.getRegistryString();
+    ffdc["Guard Type"]   = txGuard.getString();
+    io_sd.addCalloutFFDC(ffdc);
+}
+
+//------------------------------------------------------------------------------
+
 void ClockCalloutResolution::resolve(ServiceData& io_sd) const
 {
     // Add the callout to the service data.