xref: /openbmc/phosphor-logging/extensions/openpower-pels/service_indicators.cpp (revision 8a09b982ddeb0c1e13190d9cd196e06a778d6140)
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 
22 namespace openpower::pels::service_indicators
23 {
24 
25 static constexpr auto platformSaiLedGroup =
26     "/xyz/openbmc_project/led/groups/platform_system_attention_indicator";
27 
getPolicy(const DataInterfaceBase & dataIface)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 
ignore(const PEL & pel) const34 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 
activate(const PEL & pel)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 inventory paths for those location codes.
76             auto paths = getInventoryPaths(locCodes);
77             if (!paths.empty())
78             {
79                 setNotFunctional(paths);
80                 createCriticalAssociation(paths);
81                 sai = false;
82             }
83         }
84     }
85 
86     if (sai)
87     {
88         try
89         {
90             _dataIface.assertLEDGroup(platformSaiLedGroup, true);
91         }
92         catch (const std::exception& e)
93         {
94             lg2::error("Failed to assert platform SAI LED group: {EXCEPTION}",
95                        "EXCEPTION", e);
96         }
97     }
98 }
99 
getLocationCodes(const std::vector<std::unique_ptr<src::Callout>> & callouts) const100 std::vector<std::string> LightPath::getLocationCodes(
101     const std::vector<std::unique_ptr<src::Callout>>& callouts) const
102 {
103     std::vector<std::string> locCodes;
104     bool firstCallout = true;
105     uint8_t firstCalloutPriority;
106 
107     // Collect location codes for the first group of callouts,
108     // where a group can be:
109     //  * a single medium priority callout
110     //  * one or more high priority callouts
111     //  * one or more medium group a priority callouts
112     //
113     // All callouts in the group must be hardware callouts.
114 
115     for (const auto& callout : callouts)
116     {
117         if (firstCallout)
118         {
119             firstCallout = false;
120 
121             firstCalloutPriority = callout->priority();
122 
123             // If the first callout is High, Medium, or Medium
124             // group A, and is a hardware callout, then we
125             // want it.
126             if (isRequiredPriority(firstCalloutPriority) &&
127                 isHardwareCallout(*callout))
128             {
129                 locCodes.push_back(callout->locationCode());
130             }
131             else
132             {
133                 break;
134             }
135 
136             // By definition a medium priority callout can't be part
137             // of a group, so no need to look for more.
138             if (static_cast<CalloutPriority>(firstCalloutPriority) ==
139                 CalloutPriority::medium)
140             {
141                 break;
142             }
143         }
144         else
145         {
146             // Only continue while the callouts are the same
147             // priority as the first callout.
148             if (callout->priority() != firstCalloutPriority)
149             {
150                 break;
151             }
152 
153             // If any callout in the group isn't a hardware callout,
154             // then don't light up any LEDs at all.
155             if (!isHardwareCallout(*callout))
156             {
157                 locCodes.clear();
158                 break;
159             }
160 
161             locCodes.push_back(callout->locationCode());
162         }
163     }
164 
165     return locCodes;
166 }
167 
isRequiredPriority(uint8_t priority) const168 bool LightPath::isRequiredPriority(uint8_t priority) const
169 {
170     auto calloutPriority = static_cast<CalloutPriority>(priority);
171     return (calloutPriority == CalloutPriority::high) ||
172            (calloutPriority == CalloutPriority::medium) ||
173            (calloutPriority == CalloutPriority::mediumGroupA);
174 }
175 
isHardwareCallout(const src::Callout & callout) const176 bool LightPath::isHardwareCallout(const src::Callout& callout) const
177 {
178     const auto& fruIdentity = callout.fruIdentity();
179     if (fruIdentity)
180     {
181         return (callout.locationCodeSize() != 0) &&
182                ((fruIdentity->failingComponentType() ==
183                  src::FRUIdentity::hardwareFRU) ||
184                 (fruIdentity->failingComponentType() ==
185                  src::FRUIdentity::symbolicFRUTrustedLocCode));
186     }
187 
188     return false;
189 }
190 
getInventoryPaths(const std::vector<std::string> & locationCodes) const191 std::vector<std::string> LightPath::getInventoryPaths(
192     const std::vector<std::string>& locationCodes) const
193 {
194     std::vector<std::string> paths;
195 
196     for (const auto& locCode : locationCodes)
197     {
198         try
199         {
200             auto inventoryPaths =
201                 _dataIface.getInventoryFromLocCode(locCode, 0, true);
202             for (const auto& path : inventoryPaths)
203             {
204                 if (std::find(paths.begin(), paths.end(), path) == paths.end())
205                 {
206                     paths.push_back(path);
207                 }
208             }
209         }
210         catch (const std::exception& e)
211         {
212             lg2::error("Could not get inventory path for "
213                        "location code {LOCCODE} ({EXCEPTION}).",
214                        "LOCCODE", locCode, "EXCEPTION", e);
215 
216             // Unless we can set the LEDs for all FRUs, we can't turn
217             // on any of them, so clear the list and quit.
218             paths.clear();
219             break;
220         }
221     }
222 
223     return paths;
224 }
225 
setNotFunctional(const std::vector<std::string> & inventoryPaths) const226 void LightPath::setNotFunctional(
227     const std::vector<std::string>& inventoryPaths) const
228 {
229     for (const auto& path : inventoryPaths)
230     {
231         try
232         {
233             _dataIface.setFunctional(path, false);
234         }
235         catch (const std::exception& e)
236         {
237             lg2::info(
238                 "Could not write Functional property on {PATH} ({EXCEPTION})",
239                 "PATH", path, "EXCEPTION", e);
240         }
241     }
242 }
243 
createCriticalAssociation(const std::vector<std::string> & inventoryPaths) const244 void LightPath::createCriticalAssociation(
245     const std::vector<std::string>& inventoryPaths) const
246 {
247     for (const auto& path : inventoryPaths)
248     {
249         try
250         {
251             _dataIface.setCriticalAssociation(path);
252         }
253         catch (const std::exception& e)
254         {
255             lg2::info(
256                 "Could not set critical association on object path {PATH} ({EXCEPTION})",
257                 "PATH", path, "EXCEPTION", e);
258         }
259     }
260 }
261 
262 } // namespace openpower::pels::service_indicators
263