/** * 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 #include #include 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 getPolicy(const DataInterfaceBase& dataIface) { // At the moment there is just one type of policy. return std::make_unique(dataIface); } bool LightPath::ignore(const PEL& pel) const { auto creator = pel.privateHeader().creatorID(); // Don't ignore serviceable BMC or hostboot errors if ((static_cast(creator) == CreatorID::openBMC) || (static_cast(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( fmt::format("Failed to assert platform SAI LED group: {}", e.what()) .c_str()); } } } std::vector LightPath::getLocationCodes( const std::vector>& callouts) const { std::vector 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(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(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 LightPath::getInventoryPaths( const std::vector& locationCodes) const { std::vector 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(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& inventoryPaths) const { for (const auto& path : inventoryPaths) { try { _dataIface.setFunctional(path, false); } catch (const std::exception& e) { log( fmt::format("Could not write Functional property on {} ({})", path, e.what()) .c_str()); } } } void LightPath::createCriticalAssociation( const std::vector& inventoryPaths) const { for (const auto& path : inventoryPaths) { try { _dataIface.setCriticalAssociation(path); } catch (const std::exception& e) { log( fmt::format( "Could not set critical association on object path {} ({})", path, e.what()) .c_str()); } } } } // namespace openpower::pels::service_indicators