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(