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