xref: /openbmc/phosphor-logging/extensions/openpower-pels/service_indicators.cpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2020 IBM Corporation
3 
4 #include "service_indicators.hpp"
5 
6 #include <phosphor-logging/lg2.hpp>
7 
8 #include <bitset>
9 
10 namespace openpower::pels::service_indicators
11 {
12 
13 static constexpr auto platformSaiLedGroup =
14     "/xyz/openbmc_project/led/groups/platform_system_attention_indicator";
15 
getPolicy(const DataInterfaceBase & dataIface)16 std::unique_ptr<Policy> getPolicy(const DataInterfaceBase& dataIface)
17 {
18     // At the moment there is just one type of policy.
19     return std::make_unique<LightPath>(dataIface);
20 }
21 
ignore(const PEL & pel) const22 bool LightPath::ignore(const PEL& pel) const
23 {
24     auto creator = pel.privateHeader().creatorID();
25 
26     // Don't ignore serviceable BMC or hostboot errors
27     if ((static_cast<CreatorID>(creator) == CreatorID::openBMC) ||
28         (static_cast<CreatorID>(creator) == CreatorID::hostboot))
29     {
30         std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
31         if (actionFlags.test(serviceActionFlagBit))
32         {
33             return false;
34         }
35     }
36 
37     return true;
38 }
39 
activate(const PEL & pel)40 void LightPath::activate(const PEL& pel)
41 {
42     if (ignore(pel))
43     {
44         return;
45     }
46 
47     // Now that we've gotten this far, we'll need to turn on
48     // the system attention indicator if we don't find other
49     // indicators to turn on.
50     bool sai = true;
51     auto src = pel.primarySRC();
52     const auto& calloutsObj = (*src)->callouts();
53 
54     if (calloutsObj && !calloutsObj->callouts().empty())
55     {
56         const auto& callouts = calloutsObj->callouts();
57 
58         // From the callouts, find the location codes whose
59         // LEDs need to be turned on.
60         auto locCodes = getLocationCodes(callouts);
61         if (!locCodes.empty())
62         {
63             // Find the inventory paths for those location codes.
64             auto paths = getInventoryPaths(locCodes);
65             if (!paths.empty())
66             {
67                 setNotFunctional(paths);
68                 createCriticalAssociation(paths);
69                 sai = false;
70             }
71         }
72     }
73 
74     if (sai)
75     {
76         try
77         {
78             _dataIface.assertLEDGroup(platformSaiLedGroup, true);
79         }
80         catch (const std::exception& e)
81         {
82             lg2::error("Failed to assert platform SAI LED group: {EXCEPTION}",
83                        "EXCEPTION", e);
84         }
85     }
86 }
87 
getLocationCodes(const std::vector<std::unique_ptr<src::Callout>> & callouts) const88 std::vector<std::string> LightPath::getLocationCodes(
89     const std::vector<std::unique_ptr<src::Callout>>& callouts) const
90 {
91     std::vector<std::string> locCodes;
92     bool firstCallout = true;
93     uint8_t firstCalloutPriority;
94 
95     // Collect location codes for the first group of callouts,
96     // where a group can be:
97     //  * a single medium priority callout
98     //  * one or more high priority callouts
99     //  * one or more medium group a priority callouts
100     //
101     // All callouts in the group must be hardware callouts.
102 
103     for (const auto& callout : callouts)
104     {
105         if (firstCallout)
106         {
107             firstCallout = false;
108 
109             firstCalloutPriority = callout->priority();
110 
111             // If the first callout is High, Medium, or Medium
112             // group A, and is a hardware callout, then we
113             // want it.
114             if (isRequiredPriority(firstCalloutPriority) &&
115                 isHardwareCallout(*callout))
116             {
117                 locCodes.push_back(callout->locationCode());
118             }
119             else
120             {
121                 break;
122             }
123 
124             // By definition a medium priority callout can't be part
125             // of a group, so no need to look for more.
126             if (static_cast<CalloutPriority>(firstCalloutPriority) ==
127                 CalloutPriority::medium)
128             {
129                 break;
130             }
131         }
132         else
133         {
134             // Only continue while the callouts are the same
135             // priority as the first callout.
136             if (callout->priority() != firstCalloutPriority)
137             {
138                 break;
139             }
140 
141             // If any callout in the group isn't a hardware callout,
142             // then don't light up any LEDs at all.
143             if (!isHardwareCallout(*callout))
144             {
145                 locCodes.clear();
146                 break;
147             }
148 
149             locCodes.push_back(callout->locationCode());
150         }
151     }
152 
153     return locCodes;
154 }
155 
isRequiredPriority(uint8_t priority) const156 bool LightPath::isRequiredPriority(uint8_t priority) const
157 {
158     auto calloutPriority = static_cast<CalloutPriority>(priority);
159     return (calloutPriority == CalloutPriority::high) ||
160            (calloutPriority == CalloutPriority::medium) ||
161            (calloutPriority == CalloutPriority::mediumGroupA);
162 }
163 
isHardwareCallout(const src::Callout & callout) const164 bool LightPath::isHardwareCallout(const src::Callout& callout) const
165 {
166     const auto& fruIdentity = callout.fruIdentity();
167     if (fruIdentity)
168     {
169         return (callout.locationCodeSize() != 0) &&
170                ((fruIdentity->failingComponentType() ==
171                  src::FRUIdentity::hardwareFRU) ||
172                 (fruIdentity->failingComponentType() ==
173                  src::FRUIdentity::symbolicFRUTrustedLocCode));
174     }
175 
176     return false;
177 }
178 
getInventoryPaths(const std::vector<std::string> & locationCodes) const179 std::vector<std::string> LightPath::getInventoryPaths(
180     const std::vector<std::string>& locationCodes) const
181 {
182     std::vector<std::string> paths;
183 
184     for (const auto& locCode : locationCodes)
185     {
186         try
187         {
188             auto inventoryPaths =
189                 _dataIface.getInventoryFromLocCode(locCode, 0, true);
190             for (const auto& path : inventoryPaths)
191             {
192                 if (std::find(paths.begin(), paths.end(), path) == paths.end())
193                 {
194                     paths.push_back(path);
195                 }
196             }
197         }
198         catch (const std::exception& e)
199         {
200             lg2::error("Could not get inventory path for "
201                        "location code {LOCCODE} ({EXCEPTION}).",
202                        "LOCCODE", locCode, "EXCEPTION", e);
203 
204             // Unless we can set the LEDs for all FRUs, we can't turn
205             // on any of them, so clear the list and quit.
206             paths.clear();
207             break;
208         }
209     }
210 
211     return paths;
212 }
213 
setNotFunctional(const std::vector<std::string> & inventoryPaths) const214 void LightPath::setNotFunctional(
215     const std::vector<std::string>& inventoryPaths) const
216 {
217     for (const auto& path : inventoryPaths)
218     {
219         try
220         {
221             _dataIface.setFunctional(path, false);
222         }
223         catch (const std::exception& e)
224         {
225             lg2::info(
226                 "Could not write Functional property on {PATH} ({EXCEPTION})",
227                 "PATH", path, "EXCEPTION", e);
228         }
229     }
230 }
231 
createCriticalAssociation(const std::vector<std::string> & inventoryPaths) const232 void LightPath::createCriticalAssociation(
233     const std::vector<std::string>& inventoryPaths) const
234 {
235     for (const auto& path : inventoryPaths)
236     {
237         try
238         {
239             _dataIface.setCriticalAssociation(path);
240         }
241         catch (const std::exception& e)
242         {
243             lg2::info(
244                 "Could not set critical association on object path {PATH} ({EXCEPTION})",
245                 "PATH", path, "EXCEPTION", e);
246         }
247     }
248 }
249 
250 } // namespace openpower::pels::service_indicators
251