Update primary SRC field based on callout list

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: I41f3eda5c27b8ec74d6329b8f145181f400547f8
diff --git a/analyzer/callout.hpp b/analyzer/callout.hpp
index 2e04d48..adb2fd4 100644
--- a/analyzer/callout.hpp
+++ b/analyzer/callout.hpp
@@ -72,6 +72,29 @@
     return m.at(i_priority);
 }
 
+/** These SRC subsystem values are defined by the PEL spec. */
+enum class SrcSubsystem
+{
+    // Can also reference `extensions/openpower-pels/pel_values.cpp` from
+    // `openbmc/phosphor-logging` for the full list of these values.
+
+    PROCESSOR      = 0x10,
+    PROCESSOR_UNIT = 0x13,
+    PROCESSOR_BUS  = 0x14,
+
+    MEMORY      = 0x20,
+    MEMORY_BUS  = 0x22,
+    MEMORY_DIMM = 0x23,
+
+    PHB = 0x38,
+
+    CEC_HARDWARE = 0x50,
+    CEC_CLOCKS   = 0x58,
+    CEC_TOD      = 0x5A,
+
+    OTHERS = 0x70,
+};
+
 /** @brief Container class for procedure callout service actions. */
 class Procedure
 {
@@ -85,21 +108,33 @@
      * @param i_string The string representation of the procedure used for
      *                 callouts.
      */
-    explicit Procedure(const std::string& i_string) : iv_string(i_string) {}
+    Procedure(const std::string& i_string, const SrcSubsystem i_subsystem) :
+        iv_string(i_string), iv_subsystem(i_subsystem)
+    {}
 
   private:
     /** The string representation of the procedure used for callouts. */
     const std::string iv_string;
 
+    /** The associated SRC subsystem of the procedure. */
+    const SrcSubsystem iv_subsystem;
+
   public:
     /** iv_string accessor */
     std::string getString() const
     {
         return iv_string;
     }
+
+    /** iv_subsystem accessor */
+    SrcSubsystem getSrcSubsystem() const
+    {
+        return iv_subsystem;
+    }
 };
 
-inline const Procedure Procedure::NEXTLVL{"next_level_support"};
+inline const Procedure Procedure::NEXTLVL{"next_level_support",
+                                          SrcSubsystem::OTHERS};
 
 /** @brief Container class for bus callout service actions. */
 class BusType
@@ -117,16 +152,22 @@
      * @param i_string The string representation of the procedure used for
      *                 callouts.
      */
-    explicit BusType(const std::string& i_string) : iv_string(i_string) {}
+    BusType(const std::string& i_string, const SrcSubsystem i_subsystem) :
+        iv_string(i_string), iv_subsystem(i_subsystem)
+    {}
 
   private:
     /** The string representation of the procedure used for callouts. */
     const std::string iv_string;
 
+    /** The associated SRC subsystem of the bus. */
+    const SrcSubsystem iv_subsystem;
+
   public:
     bool operator==(const BusType& r) const
     {
-        return this->iv_string == r.iv_string;
+        return ((this->iv_string == r.iv_string) &&
+                (this->iv_subsystem == r.iv_subsystem));
     }
 
     /** iv_string accessor */
@@ -134,10 +175,16 @@
     {
         return iv_string;
     }
+
+    /** iv_subsystem accessor */
+    SrcSubsystem getSrcSubsystem() const
+    {
+        return iv_subsystem;
+    }
 };
 
-inline const BusType BusType::SMP_BUS{"SMP_BUS"};
-inline const BusType BusType::OMI_BUS{"OMI_BUS"};
+inline const BusType BusType::SMP_BUS{"SMP_BUS", SrcSubsystem::PROCESSOR_BUS};
+inline const BusType BusType::OMI_BUS{"OMI_BUS", SrcSubsystem::MEMORY_BUS};
 
 /** @brief Container class for clock callout service actions. */
 class ClockType
@@ -158,23 +205,36 @@
      * @param i_string The string representation of the procedure used for
      *                 callouts.
      */
-    explicit ClockType(const std::string& i_string) : iv_string(i_string) {}
+    ClockType(const std::string& i_string, const SrcSubsystem i_subsystem) :
+        iv_string(i_string), iv_subsystem(i_subsystem)
+    {}
 
   private:
     /** The string representation of the procedure used for callouts. */
     const std::string iv_string;
 
+    /** The associated SRC subsystem of the clock. */
+    const SrcSubsystem iv_subsystem;
+
   public:
     /** iv_string accessor */
     std::string getString() const
     {
         return iv_string;
     }
+
+    /** iv_subsystem accessor */
+    SrcSubsystem getSrcSubsystem() const
+    {
+        return iv_subsystem;
+    }
 };
 
-inline const ClockType ClockType::OSC_REF_CLOCK_0{"OSC_REF_CLOCK_0"};
-inline const ClockType ClockType::OSC_REF_CLOCK_1{"OSC_REF_CLOCK_1"};
-inline const ClockType ClockType::TOD_CLOCK{"TOD_CLOCK"};
+inline const ClockType ClockType::OSC_REF_CLOCK_0{"OSC_REF_CLOCK_0",
+                                                  SrcSubsystem::CEC_CLOCKS};
+inline const ClockType ClockType::OSC_REF_CLOCK_1{"OSC_REF_CLOCK_1",
+                                                  SrcSubsystem::CEC_CLOCKS};
+inline const ClockType ClockType::TOD_CLOCK{"TOD_CLOCK", SrcSubsystem::CEC_TOD};
 
 /** @brief Container class for part callout service actions. */
 class PartType
@@ -188,16 +248,22 @@
      * @brief Constructor from components.
      * @param i_string The string representation of the part callout.
      */
-    explicit PartType(const std::string& i_string) : iv_string(i_string) {}
+    PartType(const std::string& i_string, const SrcSubsystem i_subsystem) :
+        iv_string(i_string), iv_subsystem(i_subsystem)
+    {}
 
   private:
     /** The string representation of the part callout. */
     const std::string iv_string;
 
+    /** The associated SRC subsystem of the part. */
+    const SrcSubsystem iv_subsystem;
+
   public:
     bool operator==(const PartType& r) const
     {
-        return this->iv_string == r.iv_string;
+        return ((this->iv_string == r.iv_string) &&
+                (this->iv_subsystem == r.iv_subsystem));
     }
 
     /** iv_string accessor */
@@ -205,9 +271,15 @@
     {
         return iv_string;
     }
+
+    /** iv_subsystem accessor */
+    SrcSubsystem getSrcSubsystem() const
+    {
+        return iv_subsystem;
+    }
 };
 
-inline const PartType PartType::PNOR{"PNOR"};
+inline const PartType PartType::PNOR{"PNOR", SrcSubsystem::CEC_HARDWARE};
 
 /** @brief Container class for guard service actions. */
 class GuardType
diff --git a/analyzer/create_pel.cpp b/analyzer/create_pel.cpp
index a5982ea..cb9610b 100644
--- a/analyzer/create_pel.cpp
+++ b/analyzer/create_pel.cpp
@@ -330,6 +330,9 @@
     //          until the PEL is submitted.
     std::vector<util::FFDCFile> userDataFiles;
 
+    // Set the subsystem in the primary SRC.
+    i_servData.addSrcSubsystem(logData);
+
     // Set words 6-9 of the SRC.
     __setSrc(i_servData.getRootCause(), logData);
 
diff --git a/analyzer/meson.build b/analyzer/meson.build
index 3d34630..12f5100 100644
--- a/analyzer/meson.build
+++ b/analyzer/meson.build
@@ -18,6 +18,7 @@
 # Library dependencies.
 analyzer_deps = [
     dbus_interfaces_dep,
+    fmt_dep,
     libhei_dep,
     nlohmann_json_dep,
     sdbusplus_dep,
diff --git a/analyzer/service_data.cpp b/analyzer/service_data.cpp
index c13bc8b..2a7d5f5 100644
--- a/analyzer/service_data.cpp
+++ b/analyzer/service_data.cpp
@@ -18,6 +18,7 @@
     ffdc["Priority"]     = callout::getStringFFDC(i_priority);
     ffdc["Guard"]        = i_guard;
     addCalloutFFDC(ffdc);
+    setSrcSubsystem(getTargetSubsystem(i_target), i_priority);
 }
 
 //------------------------------------------------------------------------------
@@ -41,6 +42,7 @@
     ffdc["Priority"]     = callout::getStringFFDC(i_priority);
     ffdc["Guard"]        = i_guard;
     addCalloutFFDC(ffdc);
+    setSrcSubsystem(getTargetSubsystem(txTarget), i_priority);
 }
 
 //------------------------------------------------------------------------------
@@ -71,6 +73,7 @@
     ffdc["Priority"]     = callout::getStringFFDC(i_priority);
     ffdc["Guard"]        = i_guard;
     addCalloutFFDC(ffdc);
+    setSrcSubsystem(i_busType.getSrcSubsystem(), i_priority);
 }
 
 //------------------------------------------------------------------------------
@@ -91,6 +94,7 @@
     ffdc["Clock Type"]   = i_clockType.getString();
     ffdc["Priority"]     = callout::getStringFFDC(i_priority);
     addCalloutFFDC(ffdc);
+    setSrcSubsystem(i_clockType.getSrcSubsystem(), i_priority);
 }
 
 //------------------------------------------------------------------------------
@@ -110,6 +114,7 @@
     ffdc["Procedure"]    = i_procedure.getString();
     ffdc["Priority"]     = callout::getStringFFDC(i_priority);
     addCalloutFFDC(ffdc);
+    setSrcSubsystem(i_procedure.getSrcSubsystem(), i_priority);
 }
 
 //------------------------------------------------------------------------------
@@ -135,6 +140,7 @@
     ffdc["Part Type"]    = i_part.getString();
     ffdc["Priority"]     = callout::getStringFFDC(i_priority);
     addCalloutFFDC(ffdc);
+    setSrcSubsystem(i_part.getSrcSubsystem(), i_priority);
 }
 
 //------------------------------------------------------------------------------
@@ -235,4 +241,78 @@
 
 //------------------------------------------------------------------------------
 
+void ServiceData::setSrcSubsystem(callout::SrcSubsystem i_subsystem,
+                                  callout::Priority i_priority)
+{
+    // clang-format off
+    static const std::map<callout::Priority, unsigned int> m =
+    {
+        // Note that all medium priorities, including groups A, B, and C, are
+        // the same priority.
+        {callout::Priority::HIGH,  3},
+        {callout::Priority::MED,   2},
+        {callout::Priority::MED_A, 2},
+        {callout::Priority::MED_B, 2},
+        {callout::Priority::MED_C, 2},
+        {callout::Priority::LOW,   1},
+    };
+    // clang-format on
+
+    // The default subsystem is CEC_HARDWARE with LOW priority. Change the
+    // subsystem if the given subsystem has a higher priority or if the stored
+    // subsystem is still the default.
+    if (m.at(iv_srcSubsystem.second) < m.at(i_priority) ||
+        (callout::SrcSubsystem::CEC_HARDWARE == iv_srcSubsystem.first &&
+         callout::Priority::LOW == iv_srcSubsystem.second))
+    {
+        iv_srcSubsystem.first  = i_subsystem;
+        iv_srcSubsystem.second = i_priority;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+callout::SrcSubsystem ServiceData::getTargetSubsystem(pdbg_target* i_target)
+{
+    using TargetType_t = util::pdbg::TargetType_t;
+
+    // Default the subsystem to CEC_HARDWARE
+    callout::SrcSubsystem o_subSys = callout::SrcSubsystem::CEC_HARDWARE;
+
+    // clang-format off
+    static const std::map<uint8_t, callout::SrcSubsystem> subSysMap =
+    {
+        {TargetType_t::TYPE_DIMM,     callout::SrcSubsystem::MEMORY_DIMM   },
+        {TargetType_t::TYPE_PROC,     callout::SrcSubsystem::PROCESSOR     },
+        {TargetType_t::TYPE_CORE,     callout::SrcSubsystem::PROCESSOR_UNIT},
+        {TargetType_t::TYPE_EQ,       callout::SrcSubsystem::PROCESSOR     },
+        {TargetType_t::TYPE_PEC,      callout::SrcSubsystem::PHB           },
+        {TargetType_t::TYPE_PHB,      callout::SrcSubsystem::PHB           },
+        {TargetType_t::TYPE_MC,       callout::SrcSubsystem::MEMORY        },
+        {TargetType_t::TYPE_IOLINK,   callout::SrcSubsystem::CEC_HARDWARE  },
+        {TargetType_t::TYPE_OMI,      callout::SrcSubsystem::MEMORY        },
+        {TargetType_t::TYPE_MCC,      callout::SrcSubsystem::MEMORY        },
+        {TargetType_t::TYPE_OMIC,     callout::SrcSubsystem::MEMORY        },
+        {TargetType_t::TYPE_OCMB,     callout::SrcSubsystem::MEMORY        },
+        {TargetType_t::TYPE_MEM_PORT, callout::SrcSubsystem::MEMORY        },
+        {TargetType_t::TYPE_NMMU,     callout::SrcSubsystem::PROCESSOR     },
+        {TargetType_t::TYPE_PAU,      callout::SrcSubsystem::PROCESSOR_BUS },
+        {TargetType_t::TYPE_IOHS,     callout::SrcSubsystem::PROCESSOR_BUS },
+        {TargetType_t::TYPE_PAUC,     callout::SrcSubsystem::PROCESSOR     },
+    };
+    // clang-format on
+
+    auto targetType = util::pdbg::getTrgtType(i_target);
+
+    // If the type of the input target exists in the map, update the output
+    if (subSysMap.count(targetType) > 0)
+    {
+        o_subSys = subSysMap.at(targetType);
+    }
+
+    return o_subSys;
+}
+
+//------------------------------------------------------------------------------
+
 } // namespace analyzer
diff --git a/analyzer/service_data.hpp b/analyzer/service_data.hpp
index 90ebf2f..c1f681e 100644
--- a/analyzer/service_data.hpp
+++ b/analyzer/service_data.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <fmt/format.h>
+
 #include <analyzer/analyzer_main.hpp>
 #include <analyzer/callout.hpp>
 #include <hei_main.hpp>
@@ -156,6 +158,23 @@
         return iv_calloutFFDC;
     }
 
+    /**
+     * @brief Adds the SRC subsystem to the given additional PEL data.
+     * @param io_additionalData The additional PEL data.
+     */
+    void addSrcSubsystem(
+        std::map<std::string, std::string>& io_additionalData) const
+    {
+        io_additionalData["PEL_SUBSYSTEM"] =
+            fmt::format("0x{:02x}", iv_srcSubsystem.first);
+    }
+
+    /** @brief Accessor to iv_srcSubsystem. */
+    const std::pair<callout::SrcSubsystem, callout::Priority> getSubsys() const
+    {
+        return iv_srcSubsystem;
+    }
+
   private:
     /**
      * @brief Add callout information to the callout list.
@@ -189,6 +208,31 @@
      * @param i_priority The callout priority.
      */
     void addBackplaneCallout(callout::Priority i_priority);
+
+  private:
+    /**
+     * @brief Compares the current SRC subsystem type with the given SRC
+     *        subsystem type and stores the highest priority callout subsystem.
+     *        If the two subsystems are of equal priority. The stored subsystem
+     *        is used.
+     * @param i_subsystem The given subsystem type.
+     * @param i_priority  The callout priority associated with the given
+     *                    subsystem.
+     */
+    void setSrcSubsystem(callout::SrcSubsystem i_subsystem,
+                         callout::Priority i_priority);
+
+    /**
+     * @brief Returns the appropriate SRC subsystem based on the input target.
+     * @param i_trgt The given pdbg target.
+     */
+    callout::SrcSubsystem getTargetSubsystem(pdbg_target* i_target);
+
+    /** The SRC subsystem field (2nd byte of the primary SRC) is based on the
+     *  callouts the PEL. As callouts are to the service data, we'll need to
+     *  keep track of the highest priority callout subsystem. */
+    std::pair<callout::SrcSubsystem, callout::Priority> iv_srcSubsystem = {
+        callout::SrcSubsystem::CEC_HARDWARE, callout::Priority::LOW};
 };
 
 } // namespace analyzer