blob: 1f578aa4bfcac5da0d9e2d990402653ad5cc3e7b [file] [log] [blame]
Brandon Wymana0f33ce2019-10-17 18:32:29 -05001#include "psu_manager.hpp"
2
3#include "utility.hpp"
4
Brandon Wymanb76ab242020-09-16 18:06:06 -05005#include <fmt/format.h>
6#include <sys/types.h>
7#include <unistd.h>
8
Brandon Wymanaed1f752019-11-25 18:10:52 -06009using namespace phosphor::logging;
10
Brandon Wyman63ea78b2020-09-24 16:49:09 -050011namespace phosphor::power::manager
Brandon Wymana0f33ce2019-10-17 18:32:29 -050012{
13
Brandon Wymanaed1f752019-11-25 18:10:52 -060014PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
15 const std::string& configfile) :
16 bus(bus)
17{
18 // Parse out the JSON properties
Adriana Kobylak169975c2021-03-06 15:19:55 +000019 sysProperties = {0};
20 getJSONProperties(configfile);
Brandon Wymanaed1f752019-11-25 18:10:52 -060021
22 using namespace sdeventplus;
Jay Meyerda364552020-06-26 16:14:11 -050023 auto interval = std::chrono::milliseconds(1000);
Brandon Wymanaed1f752019-11-25 18:10:52 -060024 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
25 e, std::bind(&PSUManager::analyze, this), interval);
26
Brandon Wymanaed1f752019-11-25 18:10:52 -060027 // Subscribe to power state changes
28 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
29 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
30 bus,
31 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
32 POWER_IFACE),
33 [this](auto& msg) { this->powerStateChanged(msg); });
34
35 initialize();
36}
37
Adriana Kobylak169975c2021-03-06 15:19:55 +000038void PSUManager::getJSONProperties(const std::string& path)
Brandon Wymanaed1f752019-11-25 18:10:52 -060039{
40 nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
41
42 if (configFileJSON == nullptr)
43 {
44 throw std::runtime_error("Failed to load JSON configuration file");
45 }
46
47 if (!configFileJSON.contains("SystemProperties"))
48 {
49 throw std::runtime_error("Missing required SystemProperties");
50 }
51
52 if (!configFileJSON.contains("PowerSupplies"))
53 {
54 throw std::runtime_error("Missing required PowerSupplies");
55 }
56
57 auto sysProps = configFileJSON["SystemProperties"];
58
Brandon Wymanaed1f752019-11-25 18:10:52 -060059 if (sysProps.contains("MaxPowerSupplies"))
60 {
Adriana Kobylak169975c2021-03-06 15:19:55 +000061 sysProperties.maxPowerSupplies = sysProps["MaxPowerSupplies"];
Brandon Wymanaed1f752019-11-25 18:10:52 -060062 }
63
64 for (auto psuJSON : configFileJSON["PowerSupplies"])
65 {
Brandon Wymanc63941c2020-01-27 16:49:33 -060066 if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") &&
67 psuJSON.contains("Address"))
Brandon Wymanaed1f752019-11-25 18:10:52 -060068 {
69 std::string invpath = psuJSON["Inventory"];
Brandon Wymanc63941c2020-01-27 16:49:33 -060070 std::uint8_t i2cbus = psuJSON["Bus"];
71 std::string i2caddr = psuJSON["Address"];
72 auto psu =
73 std::make_unique<PowerSupply>(bus, invpath, i2cbus, i2caddr);
Brandon Wymanaed1f752019-11-25 18:10:52 -060074 psus.emplace_back(std::move(psu));
75 }
Brandon Wymanc63941c2020-01-27 16:49:33 -060076 else
77 {
78 log<level::ERR>("Insufficient PowerSupply properties");
79 }
Brandon Wymanaed1f752019-11-25 18:10:52 -060080 }
81
82 if (psus.empty())
83 {
84 throw std::runtime_error("No power supplies to monitor");
85 }
86}
87
Brandon Wymana0f33ce2019-10-17 18:32:29 -050088void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
89{
90 int32_t state = 0;
91 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -050092 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -050093 msg.read(msgSensor, msgData);
94
95 // Check if it was the Present property that changed.
96 auto valPropMap = msgData.find("state");
97 if (valPropMap != msgData.end())
98 {
99 state = std::get<int32_t>(valPropMap->second);
100
101 // Power is on when state=1. Clear faults.
102 if (state)
103 {
104 powerOn = true;
105 clearFaults();
106 }
107 else
108 {
109 powerOn = false;
110 }
111 }
112}
113
Brandon Wymanb76ab242020-09-16 18:06:06 -0500114void PSUManager::createError(
115 const std::string& faultName,
116 const std::map<std::string, std::string>& additionalData)
117{
118 using namespace sdbusplus::xyz::openbmc_project;
119 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
120 constexpr auto loggingCreateInterface =
121 "xyz.openbmc_project.Logging.Create";
122
123 try
124 {
125 auto service =
126 util::getService(loggingObjectPath, loggingCreateInterface, bus);
127
128 if (service.empty())
129 {
130 log<level::ERR>("Unable to get logging manager service");
131 return;
132 }
133
134 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
135 loggingCreateInterface, "Create");
136
137 auto level = Logging::server::Entry::Level::Error;
138 method.append(faultName, level, additionalData);
139
140 auto reply = bus.call(method);
141 }
142 catch (std::exception& e)
143 {
144 log<level::ERR>(
145 fmt::format(
146 "Failed creating event log for fault {} due to error {}",
147 faultName, e.what())
148 .c_str());
149 }
150}
151
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500152void PSUManager::analyze()
153{
154 for (auto& psu : psus)
155 {
156 psu->analyze();
157 }
158
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600159 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500160 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600161 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500162 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600163 std::map<std::string, std::string> additionalData;
164 additionalData["_PID"] = std::to_string(getpid());
165 // TODO: Fault priorities #918
166 if (!psu->isFaultLogged() && !psu->isPresent())
167 {
168 // Create error for power supply missing.
169 additionalData["CALLOUT_INVENTORY_PATH"] =
170 psu->getInventoryPath();
171 additionalData["CALLOUT_PRIORITY"] = "H";
172 createError(
173 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
174 additionalData);
175 psu->setFaultLogged();
176 }
177 else if (!psu->isFaultLogged() && psu->isFaulted())
178 {
179 additionalData["STATUS_WORD"] =
180 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600181 additionalData["STATUS_MFR"] =
182 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600183 // If there are faults being reported, they possibly could be
184 // related to a bug in the firmware version running on the power
185 // supply. Capture that data into the error as well.
186 additionalData["FW_VERSION"] = psu->getFWVersion();
187
188 if ((psu->hasInputFault() || psu->hasVINUVFault()))
189 {
190 /* The power supply location might be needed if the input
191 * fault is due to a problem with the power supply itself.
192 * Include the inventory path with a call out priority of
193 * low.
194 */
195 additionalData["CALLOUT_INVENTORY_PATH"] =
196 psu->getInventoryPath();
197 additionalData["CALLOUT_PRIORITY"] = "L";
198 createError("xyz.openbmc_project.Power.PowerSupply.Error."
199 "InputFault",
200 additionalData);
201 psu->setFaultLogged();
202 }
203 else if (psu->hasMFRFault())
204 {
205 /* This can represent a variety of faults that result in
206 * calling out the power supply for replacement: Output
207 * OverCurrent, Output Under Voltage, and potentially other
208 * faults.
209 *
210 * Also plan on putting specific fault in AdditionalData,
211 * along with register names and register values
212 * (STATUS_WORD, STATUS_MFR, etc.).*/
213
214 additionalData["CALLOUT_INVENTORY_PATH"] =
215 psu->getInventoryPath();
216
217 createError(
218 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500219 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500220
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600221 psu->setFaultLogged();
222 }
223 else if (psu->hasCommFault())
224 {
225 /* Attempts to communicate with the power supply have
226 * reached there limit. Create an error. */
227 additionalData["CALLOUT_DEVICE_PATH"] =
228 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500229
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600230 createError(
231 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
232 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500233
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600234 psu->setFaultLogged();
235 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500236 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500237 }
238 }
239}
240
241} // namespace phosphor::power::manager