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