| /** |
| * Copyright © 2020 IBM Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "service_indicators.hpp" |
| |
| #include <fmt/format.h> |
| |
| #include <bitset> |
| #include <phosphor-logging/log.hpp> |
| |
| namespace openpower::pels::service_indicators |
| { |
| |
| using namespace phosphor::logging; |
| |
| static constexpr auto platformSaiLedGroup = |
| "/xyz/openbmc_project/led/groups/platform_system_attention_indicator"; |
| |
| std::unique_ptr<Policy> getPolicy(const DataInterfaceBase& dataIface) |
| { |
| // At the moment there is just one type of policy. |
| return std::make_unique<LightPath>(dataIface); |
| } |
| |
| bool LightPath::ignore(const PEL& pel) const |
| { |
| auto creator = pel.privateHeader().creatorID(); |
| |
| // Don't ignore serviceable BMC or hostboot errors |
| if ((static_cast<CreatorID>(creator) == CreatorID::openBMC) || |
| (static_cast<CreatorID>(creator) == CreatorID::hostboot)) |
| { |
| std::bitset<16> actionFlags{pel.userHeader().actionFlags()}; |
| if (actionFlags.test(serviceActionFlagBit)) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void LightPath::activate(const PEL& pel) |
| { |
| if (ignore(pel)) |
| { |
| return; |
| } |
| |
| // Now that we've gotten this far, we'll need to turn on |
| // the system attention indicator if we don't find other |
| // indicators to turn on. |
| bool sai = true; |
| auto src = pel.primarySRC(); |
| const auto& calloutsObj = (*src)->callouts(); |
| |
| if (calloutsObj && !calloutsObj->callouts().empty()) |
| { |
| const auto& callouts = calloutsObj->callouts(); |
| |
| // From the callouts, find the location codes whose |
| // LEDs need to be turned on. |
| auto locCodes = getLocationCodes(callouts); |
| if (!locCodes.empty()) |
| { |
| // Find the inventory paths for those location codes. |
| auto paths = getInventoryPaths(locCodes); |
| if (!paths.empty()) |
| { |
| setNotFunctional(paths); |
| createCriticalAssociation(paths); |
| sai = false; |
| } |
| } |
| } |
| |
| if (sai) |
| { |
| try |
| { |
| _dataIface.assertLEDGroup(platformSaiLedGroup, true); |
| } |
| catch (const std::exception& e) |
| { |
| log<level::ERR>( |
| fmt::format("Failed to assert platform SAI LED group: {}", |
| e.what()) |
| .c_str()); |
| } |
| } |
| } |
| |
| std::vector<std::string> LightPath::getLocationCodes( |
| const std::vector<std::unique_ptr<src::Callout>>& callouts) const |
| { |
| std::vector<std::string> locCodes; |
| bool firstCallout = true; |
| uint8_t firstCalloutPriority; |
| |
| // Collect location codes for the first group of callouts, |
| // where a group can be: |
| // * a single medium priority callout |
| // * one or more high priority callouts |
| // * one or more medium group a priority callouts |
| // |
| // All callouts in the group must be hardware callouts. |
| |
| for (const auto& callout : callouts) |
| { |
| if (firstCallout) |
| { |
| firstCallout = false; |
| |
| firstCalloutPriority = callout->priority(); |
| |
| // If the first callout is High, Medium, or Medium |
| // group A, and is a hardware callout, then we |
| // want it. |
| if (isRequiredPriority(firstCalloutPriority) && |
| isHardwareCallout(*callout)) |
| { |
| locCodes.push_back(callout->locationCode()); |
| } |
| else |
| { |
| break; |
| } |
| |
| // By definition a medium priority callout can't be part |
| // of a group, so no need to look for more. |
| if (static_cast<CalloutPriority>(firstCalloutPriority) == |
| CalloutPriority::medium) |
| { |
| break; |
| } |
| } |
| else |
| { |
| // Only continue while the callouts are the same |
| // priority as the first callout. |
| if (callout->priority() != firstCalloutPriority) |
| { |
| break; |
| } |
| |
| // If any callout in the group isn't a hardware callout, |
| // then don't light up any LEDs at all. |
| if (!isHardwareCallout(*callout)) |
| { |
| locCodes.clear(); |
| break; |
| } |
| |
| locCodes.push_back(callout->locationCode()); |
| } |
| } |
| |
| return locCodes; |
| } |
| |
| bool LightPath::isRequiredPriority(uint8_t priority) const |
| { |
| auto calloutPriority = static_cast<CalloutPriority>(priority); |
| return (calloutPriority == CalloutPriority::high) || |
| (calloutPriority == CalloutPriority::medium) || |
| (calloutPriority == CalloutPriority::mediumGroupA); |
| } |
| |
| bool LightPath::isHardwareCallout(const src::Callout& callout) const |
| { |
| const auto& fruIdentity = callout.fruIdentity(); |
| if (fruIdentity) |
| { |
| return (callout.locationCodeSize() != 0) && |
| ((fruIdentity->failingComponentType() == |
| src::FRUIdentity::hardwareFRU) || |
| (fruIdentity->failingComponentType() == |
| src::FRUIdentity::symbolicFRUTrustedLocCode)); |
| } |
| |
| return false; |
| } |
| |
| std::vector<std::string> LightPath::getInventoryPaths( |
| const std::vector<std::string>& locationCodes) const |
| { |
| std::vector<std::string> paths; |
| |
| for (const auto& locCode : locationCodes) |
| { |
| try |
| { |
| auto inventoryPath = |
| _dataIface.getInventoryFromLocCode(locCode, 0, true); |
| paths.push_back(std::move(inventoryPath)); |
| } |
| catch (const std::exception& e) |
| { |
| log<level::ERR>(fmt::format("Could not get inventory path for " |
| "location code {} ({}).", |
| locCode, e.what()) |
| .c_str()); |
| |
| // Unless we can set the LEDs for all FRUs, we can't turn |
| // on any of them, so clear the list and quit. |
| paths.clear(); |
| break; |
| } |
| } |
| |
| return paths; |
| } |
| |
| void LightPath::setNotFunctional( |
| const std::vector<std::string>& inventoryPaths) const |
| { |
| for (const auto& path : inventoryPaths) |
| { |
| try |
| { |
| _dataIface.setFunctional(path, false); |
| } |
| catch (const std::exception& e) |
| { |
| log<level::INFO>( |
| fmt::format("Could not write Functional property on {} ({})", |
| path, e.what()) |
| .c_str()); |
| } |
| } |
| } |
| |
| void LightPath::createCriticalAssociation( |
| const std::vector<std::string>& inventoryPaths) const |
| { |
| for (const auto& path : inventoryPaths) |
| { |
| try |
| { |
| _dataIface.setCriticalAssociation(path); |
| } |
| catch (const std::exception& e) |
| { |
| log<level::INFO>( |
| fmt::format( |
| "Could not set critical association on object path {} ({})", |
| path, e.what()) |
| .c_str()); |
| } |
| } |
| } |
| |
| } // namespace openpower::pels::service_indicators |