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 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 35 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 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 101 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 169 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 177 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 192 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 = 202 _dataIface.getInventoryFromLocCode(locCode, 0, 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 227 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 245 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