1 /**
2  * Copyright © 2020 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "service_indicators.hpp"
17 
18 #include <fmt/format.h>
19 
20 #include <bitset>
21 #include <phosphor-logging/log.hpp>
22 
23 namespace openpower::pels::service_indicators
24 {
25 
26 using namespace phosphor::logging;
27 
28 std::unique_ptr<Policy> getPolicy(const DataInterfaceBase& dataIface)
29 {
30     // At the moment there is just one type of policy.
31     return std::make_unique<LightPath>(dataIface);
32 }
33 
34 bool LightPath::ignore(const PEL& pel) const
35 {
36     auto creator = pel.privateHeader().creatorID();
37 
38     // Don't ignore serviceable BMC or hostboot errors
39     if ((static_cast<CreatorID>(creator) == CreatorID::openBMC) ||
40         (static_cast<CreatorID>(creator) == CreatorID::hostboot))
41     {
42         std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
43         if (actionFlags.test(serviceActionFlagBit))
44         {
45             return false;
46         }
47     }
48 
49     return true;
50 }
51 
52 void LightPath::activate(const PEL& pel)
53 {
54     if (ignore(pel))
55     {
56         return;
57     }
58 
59     // Now that we've gotten this far, we'll need to turn on
60     // the system attention indicator if we don't find other
61     // indicators to turn on.
62     bool sai = true;
63     auto src = pel.primarySRC();
64     const auto& calloutsObj = (*src)->callouts();
65 
66     if (calloutsObj && !calloutsObj->callouts().empty())
67     {
68         const auto& callouts = calloutsObj->callouts();
69 
70         // From the callouts, find the location codes whose
71         // LEDs need to be turned on.
72         auto locCodes = getLocationCodes(callouts);
73         if (!locCodes.empty())
74         {
75             // Find the LED groups for those location codes.
76             auto ledPaths = getLEDGroupPaths(locCodes);
77             if (!ledPaths.empty())
78             {
79                 // Tell the LED groups to assert their LEDs.
80                 assertLEDs(ledPaths);
81                 sai = false;
82             }
83         }
84     }
85 
86     if (sai)
87     {
88         log<level::INFO>("The System Attention Indicator needs to be turned "
89                          "on, when available");
90     }
91 }
92 
93 std::vector<std::string> LightPath::getLocationCodes(
94     const std::vector<std::unique_ptr<src::Callout>>& callouts) const
95 {
96     std::vector<std::string> locCodes;
97     bool firstCallout = true;
98     uint8_t firstCalloutPriority;
99 
100     // Collect location codes for the first group of callouts,
101     // where a group can be:
102     //  * a single medium priority callout
103     //  * one or more high priority callouts
104     //  * one or more medium group a priority callouts
105     //
106     // All callouts in the group must be hardware callouts.
107 
108     for (const auto& callout : callouts)
109     {
110         if (firstCallout)
111         {
112             firstCallout = false;
113 
114             firstCalloutPriority = callout->priority();
115 
116             // If the first callout is High, Medium, or Medium
117             // group A, and is a hardware callout, then we
118             // want it.
119             if (isRequiredPriority(firstCalloutPriority) &&
120                 isHardwareCallout(*callout))
121             {
122                 locCodes.push_back(callout->locationCode());
123             }
124             else
125             {
126                 break;
127             }
128 
129             // By definition a medium priority callout can't be part
130             // of a group, so no need to look for more.
131             if (static_cast<CalloutPriority>(firstCalloutPriority) ==
132                 CalloutPriority::medium)
133             {
134                 break;
135             }
136         }
137         else
138         {
139             // Only continue while the callouts are the same
140             // priority as the first callout.
141             if (callout->priority() != firstCalloutPriority)
142             {
143                 break;
144             }
145 
146             // If any callout in the group isn't a hardware callout,
147             // then don't light up any LEDs at all.
148             if (!isHardwareCallout(*callout))
149             {
150                 locCodes.clear();
151                 break;
152             }
153 
154             locCodes.push_back(callout->locationCode());
155         }
156     }
157 
158     return locCodes;
159 }
160 
161 bool LightPath::isRequiredPriority(uint8_t priority) const
162 {
163     auto calloutPriority = static_cast<CalloutPriority>(priority);
164     return (calloutPriority == CalloutPriority::high) ||
165            (calloutPriority == CalloutPriority::medium) ||
166            (calloutPriority == CalloutPriority::mediumGroupA);
167 }
168 
169 bool LightPath::isHardwareCallout(const src::Callout& callout) const
170 {
171     const auto& fruIdentity = callout.fruIdentity();
172     if (fruIdentity)
173     {
174         return (callout.locationCodeSize() != 0) &&
175                ((fruIdentity->failingComponentType() ==
176                  src::FRUIdentity::hardwareFRU) ||
177                 (fruIdentity->failingComponentType() ==
178                  src::FRUIdentity::symbolicFRUTrustedLocCode));
179     }
180 
181     return false;
182 }
183 
184 std::vector<std::string> LightPath::getLEDGroupPaths(
185     const std::vector<std::string>& locationCodes) const
186 {
187     std::vector<std::string> ledGroups;
188     std::string inventoryPath;
189 
190     for (const auto& locCode : locationCodes)
191     {
192         try
193         {
194             inventoryPath =
195                 _dataIface.getInventoryFromLocCode(locCode, 0, true);
196         }
197         catch (const std::exception& e)
198         {
199             log<level::ERR>(fmt::format("Could not get inventory path for "
200                                         "location code {} ({}).",
201                                         locCode, e.what())
202                                 .c_str());
203 
204             // Unless we can get the LEDs for all FRUs, we can't turn
205             // on any of them, so clear the list and quit.
206             ledGroups.clear();
207             break;
208         }
209 
210         try
211         {
212             ledGroups.push_back(_dataIface.getFaultLEDGroup(inventoryPath));
213         }
214         catch (const std::exception& e)
215         {
216             log<level::ERR>(fmt::format("Could not get LED group path for "
217                                         "inventory path {} ({}).",
218                                         inventoryPath, e.what())
219                                 .c_str());
220             ledGroups.clear();
221             break;
222         }
223     }
224 
225     return ledGroups;
226 }
227 
228 void LightPath::assertLEDs(const std::vector<std::string>& ledGroups) const
229 {
230     for (const auto& ledGroup : ledGroups)
231     {
232         try
233         {
234             _dataIface.assertLEDGroup(ledGroup, true);
235         }
236         catch (const std::exception& e)
237         {
238             log<level::ERR>(fmt::format("Failed to assert LED group {} ({})",
239                                         ledGroup, e.what())
240                                 .c_str());
241         }
242     }
243 }
244 
245 } // namespace openpower::pels::service_indicators
246