PEL: LightPath: Choose callout location codes

LightPath uses the following rules to pick the location codes to turn on
LEDs for:

* If the PEL wasn't created by the BMC or Hostboot, and doesn't have the
  Service Action action flag set, then don't even check it and don't
  turn on the System Attention Indicator.

* Choose all location codes in the first group of callouts, where a
  group can be:
  * a single medium priority callout
  * one or more high priority callouts
  * one or more medium group A priority callouts

* All callouts in that group must be hardware callouts, meaning the FRU
  identity section's failing component type flag must either be hardware
  callout or symbolic FRU callout with trusted location code.  If there
  is a callout in the group that doesn't meet this requirement, then
  nothing in that group can be used.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Ifbe7bddee14b69dc565a405e2f120fb5545f69e5
diff --git a/extensions/openpower-pels/service_indicators.cpp b/extensions/openpower-pels/service_indicators.cpp
index 97eb3fe..074251c 100644
--- a/extensions/openpower-pels/service_indicators.cpp
+++ b/extensions/openpower-pels/service_indicators.cpp
@@ -15,6 +15,7 @@
  */
 #include "service_indicators.hpp"
 
+#include <bitset>
 #include <phosphor-logging/log.hpp>
 
 namespace openpower::pels::service_indicators
@@ -30,8 +31,20 @@
 
 bool LightPath::ignore(const PEL& pel) const
 {
-    // TODO
-    return false;
+    auto creator = pel.privateHeader().creatorID();
+
+    // Don't ignore serviceable BMC or hostboot errors
+    if ((static_cast<CreatorID>(creator) == CreatorID::openBMC) ||
+        (static_cast<CreatorID>(creator) == CreatorID::hostboot))
+    {
+        std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
+        if (actionFlags.test(serviceActionFlagBit))
+        {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 void LightPath::activate(const PEL& pel)
@@ -78,8 +91,92 @@
 std::vector<std::string> LightPath::getLocationCodes(
     const std::vector<std::unique_ptr<src::Callout>>& callouts) const
 {
-    // TODO
-    return {};
+    std::vector<std::string> locCodes;
+    bool firstCallout = true;
+    uint8_t firstCalloutPriority;
+
+    // Collect location codes for the first group of callouts,
+    // where a group can be:
+    //  * a single medium priority callout
+    //  * one or more high priority callouts
+    //  * one or more medium group a priority callouts
+    //
+    // All callouts in the group must be hardware callouts.
+
+    for (const auto& callout : callouts)
+    {
+        if (firstCallout)
+        {
+            firstCallout = false;
+
+            firstCalloutPriority = callout->priority();
+
+            // If the first callout is High, Medium, or Medium
+            // group A, and is a hardware callout, then we
+            // want it.
+            if (isRequiredPriority(firstCalloutPriority) &&
+                isHardwareCallout(*callout))
+            {
+                locCodes.push_back(callout->locationCode());
+            }
+            else
+            {
+                break;
+            }
+
+            // By definition a medium priority callout can't be part
+            // of a group, so no need to look for more.
+            if (static_cast<CalloutPriority>(firstCalloutPriority) ==
+                CalloutPriority::medium)
+            {
+                break;
+            }
+        }
+        else
+        {
+            // Only continue while the callouts are the same
+            // priority as the first callout.
+            if (callout->priority() != firstCalloutPriority)
+            {
+                break;
+            }
+
+            // If any callout in the group isn't a hardware callout,
+            // then don't light up any LEDs at all.
+            if (!isHardwareCallout(*callout))
+            {
+                locCodes.clear();
+                break;
+            }
+
+            locCodes.push_back(callout->locationCode());
+        }
+    }
+
+    return locCodes;
+}
+
+bool LightPath::isRequiredPriority(uint8_t priority) const
+{
+    auto calloutPriority = static_cast<CalloutPriority>(priority);
+    return (calloutPriority == CalloutPriority::high) ||
+           (calloutPriority == CalloutPriority::medium) ||
+           (calloutPriority == CalloutPriority::mediumGroupA);
+}
+
+bool LightPath::isHardwareCallout(const src::Callout& callout) const
+{
+    const auto& fruIdentity = callout.fruIdentity();
+    if (fruIdentity)
+    {
+        return (callout.locationCodeSize() != 0) &&
+               ((fruIdentity->failingComponentType() ==
+                 src::FRUIdentity::hardwareFRU) ||
+                (fruIdentity->failingComponentType() ==
+                 src::FRUIdentity::symbolicFRUTrustedLocCode));
+    }
+
+    return false;
 }
 
 std::vector<std::string> LightPath::getLEDGroupPaths(
diff --git a/extensions/openpower-pels/service_indicators.hpp b/extensions/openpower-pels/service_indicators.hpp
index 36eacf8..292c30d 100644
--- a/extensions/openpower-pels/service_indicators.hpp
+++ b/extensions/openpower-pels/service_indicators.hpp
@@ -94,13 +94,30 @@
     void activate(const PEL& pel) override;
 
     /**
-     * @brief Description TODO
+     * @brief Returns the location codes for the FRU callouts in the
+     *        callouts list that need their LEDs turned on.
+     *
+     * This is public so it can be tested.
+     *
+     * @param[in] callouts - The Callout list from a PEL
+     *
+     * @return std::vector<std::string> - The location codes
      */
     std::vector<std::string> getLocationCodes(
         const std::vector<std::unique_ptr<src::Callout>>& callouts) const;
 
     /**
-     * @brief Description TODO
+     * @brief Function called to check if the code even needs to
+     *        bother looking in the callouts to find LEDs to turn on.
+     *
+     * It will ignore all PELs except for those created by the BMC or
+     * hostboot that have the Serviceable action flag set.
+     *
+     * This is public so it can be tested.
+     *
+     * @param[in] pel - The PEL
+     *
+     * @return bool - If the PEL should be ignored or not.
      */
     bool ignore(const PEL& pel) const;
 
@@ -115,6 +132,32 @@
      * @brief Description TODO
      */
     void assertLEDs(const std::vector<std::string>& ledGroups) const;
+
+    /**
+     * @brief Checks if the callout priority is one that the policy
+     *        may turn on an LED for.
+     *
+     * The priorities it cares about are high, medium, and medium
+     * group A.
+     *
+     * @param[in] priority - The priority value from the PEL
+     *
+     * @return bool - If LightPath cares about a callout with this
+     *                priority.
+     */
+    bool isRequiredPriority(uint8_t priority) const;
+
+    /**
+     * @brief Checks if the callout is either a normal FRU
+     *        callout or a symbolic FRU callout with a trusted
+     *        location code, which is one of the requirements for
+     *        LightPath to turn on an LED.
+     *
+     * @param[in] - callout - The Callout object
+     *
+     * @return bool - If the callout is a hardware callout
+     */
+    bool isHardwareCallout(const src::Callout& callout) const;
 };
 
 /**