#include "psu_manager.hpp" #include "utility.hpp" #include #include #include using namespace phosphor::logging; namespace phosphor::power::manager { PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e, const std::string& configfile) : bus(bus) { // Parse out the JSON properties sys_properties properties; getJSONProperties(configfile, bus, properties, psus); using namespace sdeventplus; auto interval = std::chrono::milliseconds(1000); timer = std::make_unique>( e, std::bind(&PSUManager::analyze, this), interval); minPSUs = {properties.minPowerSupplies}; maxPSUs = {properties.maxPowerSupplies}; // Subscribe to power state changes powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus); powerOnMatch = std::make_unique( bus, sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE), [this](auto& msg) { this->powerStateChanged(msg); }); initialize(); } void PSUManager::getJSONProperties( const std::string& path, sdbusplus::bus::bus& bus, sys_properties& p, std::vector>& psus) { nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str()); if (configFileJSON == nullptr) { throw std::runtime_error("Failed to load JSON configuration file"); } if (!configFileJSON.contains("SystemProperties")) { throw std::runtime_error("Missing required SystemProperties"); } if (!configFileJSON.contains("PowerSupplies")) { throw std::runtime_error("Missing required PowerSupplies"); } auto sysProps = configFileJSON["SystemProperties"]; if (sysProps.contains("MinPowerSupplies")) { p.minPowerSupplies = sysProps["MinPowerSupplies"]; } else { p.minPowerSupplies = 0; } if (sysProps.contains("MaxPowerSupplies")) { p.maxPowerSupplies = sysProps["MaxPowerSupplies"]; } else { p.maxPowerSupplies = 0; } for (auto psuJSON : configFileJSON["PowerSupplies"]) { if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") && psuJSON.contains("Address")) { std::string invpath = psuJSON["Inventory"]; std::uint8_t i2cbus = psuJSON["Bus"]; std::string i2caddr = psuJSON["Address"]; auto psu = std::make_unique(bus, invpath, i2cbus, i2caddr); psus.emplace_back(std::move(psu)); } else { log("Insufficient PowerSupply properties"); } } if (psus.empty()) { throw std::runtime_error("No power supplies to monitor"); } } void PSUManager::powerStateChanged(sdbusplus::message::message& msg) { int32_t state = 0; std::string msgSensor; std::map> msgData; msg.read(msgSensor, msgData); // Check if it was the Present property that changed. auto valPropMap = msgData.find("state"); if (valPropMap != msgData.end()) { state = std::get(valPropMap->second); // Power is on when state=1. Clear faults. if (state) { powerOn = true; clearFaults(); } else { powerOn = false; } } } void PSUManager::createError( const std::string& faultName, const std::map& additionalData) { using namespace sdbusplus::xyz::openbmc_project; constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging"; constexpr auto loggingCreateInterface = "xyz.openbmc_project.Logging.Create"; try { auto service = util::getService(loggingObjectPath, loggingCreateInterface, bus); if (service.empty()) { log("Unable to get logging manager service"); return; } auto method = bus.new_method_call(service.c_str(), loggingObjectPath, loggingCreateInterface, "Create"); auto level = Logging::server::Entry::Level::Error; method.append(faultName, level, additionalData); auto reply = bus.call(method); } catch (std::exception& e) { log( fmt::format( "Failed creating event log for fault {} due to error {}", faultName, e.what()) .c_str()); } } void PSUManager::analyze() { for (auto& psu : psus) { psu->analyze(); } if (powerOn) { for (auto& psu : psus) { std::map additionalData; additionalData["_PID"] = std::to_string(getpid()); // TODO: Fault priorities #918 if (!psu->isFaultLogged() && !psu->isPresent()) { // Create error for power supply missing. additionalData["CALLOUT_INVENTORY_PATH"] = psu->getInventoryPath(); additionalData["CALLOUT_PRIORITY"] = "H"; createError( "xyz.openbmc_project.Power.PowerSupply.Error.Missing", additionalData); psu->setFaultLogged(); } else if (!psu->isFaultLogged() && psu->isFaulted()) { additionalData["STATUS_WORD"] = std::to_string(psu->getStatusWord()); additionalData["STATUS_MFR"] = std::to_string(psu->getMFRFault()); // If there are faults being reported, they possibly could be // related to a bug in the firmware version running on the power // supply. Capture that data into the error as well. additionalData["FW_VERSION"] = psu->getFWVersion(); if ((psu->hasInputFault() || psu->hasVINUVFault())) { /* The power supply location might be needed if the input * fault is due to a problem with the power supply itself. * Include the inventory path with a call out priority of * low. */ additionalData["CALLOUT_INVENTORY_PATH"] = psu->getInventoryPath(); additionalData["CALLOUT_PRIORITY"] = "L"; createError("xyz.openbmc_project.Power.PowerSupply.Error." "InputFault", additionalData); psu->setFaultLogged(); } else if (psu->hasMFRFault()) { /* This can represent a variety of faults that result in * calling out the power supply for replacement: Output * OverCurrent, Output Under Voltage, and potentially other * faults. * * Also plan on putting specific fault in AdditionalData, * along with register names and register values * (STATUS_WORD, STATUS_MFR, etc.).*/ additionalData["CALLOUT_INVENTORY_PATH"] = psu->getInventoryPath(); createError( "xyz.openbmc_project.Power.PowerSupply.Error.Fault", additionalData); psu->setFaultLogged(); } else if (psu->hasCommFault()) { /* Attempts to communicate with the power supply have * reached there limit. Create an error. */ additionalData["CALLOUT_DEVICE_PATH"] = psu->getDevicePath(); createError( "xyz.openbmc_project.Power.PowerSupply.Error.CommFault", additionalData); psu->setFaultLogged(); } } } } } } // namespace phosphor::power::manager