blob: 5bb080759baaa710306c21c4380ad6d322fd6a87 [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 Wyman510acaa2020-11-05 18:32:04 -060014constexpr auto IBMCFFPSInterface =
15 "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
16constexpr auto i2cBusProp = "I2CBus";
17constexpr auto i2cAddressProp = "I2CAddress";
18constexpr auto psuNameProp = "Name";
19
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060020constexpr auto supportedConfIntf =
21 "xyz.openbmc_project.Configuration.SupportedConfiguration";
22constexpr auto maxCountProp = "MaxCount";
23
Brandon Wymanaed1f752019-11-25 18:10:52 -060024PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
25 const std::string& configfile) :
26 bus(bus)
27{
Adriana Kobylak169975c2021-03-06 15:19:55 +000028 sysProperties = {0};
Brandon Wyman510acaa2020-11-05 18:32:04 -060029 // Parse out the JSON properties
Adriana Kobylak169975c2021-03-06 15:19:55 +000030 getJSONProperties(configfile);
Brandon Wyman31649012021-03-18 16:55:01 -050031 // Subscribe to InterfacesAdded before doing a property read, otherwise
32 // the interface could be created after the read attempt but before the
33 // match is created.
34 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
35 bus,
36 sdbusplus::bus::match::rules::interfacesAdded() +
37 sdbusplus::bus::match::rules::sender(
38 "xyz.openbmc_project.EntityManager"),
Brandon Wyman3e429132021-03-18 18:03:14 -050039 std::bind(&PSUManager::entityManagerIfaceAdded, this,
Brandon Wyman31649012021-03-18 16:55:01 -050040 std::placeholders::_1));
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060041 getSystemProperties();
Brandon Wymanaed1f752019-11-25 18:10:52 -060042
43 using namespace sdeventplus;
Jay Meyerda364552020-06-26 16:14:11 -050044 auto interval = std::chrono::milliseconds(1000);
Brandon Wymanaed1f752019-11-25 18:10:52 -060045 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
46 e, std::bind(&PSUManager::analyze, this), interval);
47
Brandon Wymanaed1f752019-11-25 18:10:52 -060048 // Subscribe to power state changes
49 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
50 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
51 bus,
52 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
53 POWER_IFACE),
54 [this](auto& msg) { this->powerStateChanged(msg); });
55
56 initialize();
57}
58
Brandon Wyman510acaa2020-11-05 18:32:04 -060059PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
60 bus(bus)
61{
62 sysProperties = {0};
63 // Subscribe to InterfacesAdded before doing a property read, otherwise
64 // the interface could be created after the read attempt but before the
65 // match is created.
66 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
67 bus,
68 sdbusplus::bus::match::rules::interfacesAdded() +
69 sdbusplus::bus::match::rules::sender(
70 "xyz.openbmc_project.EntityManager"),
71 std::bind(&PSUManager::entityManagerIfaceAdded, this,
72 std::placeholders::_1));
73 getPSUConfiguration();
74 getSystemProperties();
75
76 using namespace sdeventplus;
77 auto interval = std::chrono::milliseconds(1000);
78 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
79 e, std::bind(&PSUManager::analyze, this), interval);
80
81 // Subscribe to power state changes
82 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
83 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
84 bus,
85 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
86 POWER_IFACE),
87 [this](auto& msg) { this->powerStateChanged(msg); });
88
89 initialize();
90}
91
Adriana Kobylak169975c2021-03-06 15:19:55 +000092void PSUManager::getJSONProperties(const std::string& path)
Brandon Wymanaed1f752019-11-25 18:10:52 -060093{
94 nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
95
96 if (configFileJSON == nullptr)
97 {
98 throw std::runtime_error("Failed to load JSON configuration file");
99 }
100
Brandon Wymanaed1f752019-11-25 18:10:52 -0600101 if (!configFileJSON.contains("PowerSupplies"))
102 {
103 throw std::runtime_error("Missing required PowerSupplies");
104 }
105
Brandon Wymanaed1f752019-11-25 18:10:52 -0600106 for (auto psuJSON : configFileJSON["PowerSupplies"])
107 {
Brandon Wymanc63941c2020-01-27 16:49:33 -0600108 if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") &&
109 psuJSON.contains("Address"))
Brandon Wymanaed1f752019-11-25 18:10:52 -0600110 {
111 std::string invpath = psuJSON["Inventory"];
Brandon Wymanc63941c2020-01-27 16:49:33 -0600112 std::uint8_t i2cbus = psuJSON["Bus"];
Brandon Wyman510acaa2020-11-05 18:32:04 -0600113 std::uint16_t i2caddr = static_cast<uint16_t>(psuJSON["Address"]);
Brandon Wymanc63941c2020-01-27 16:49:33 -0600114 auto psu =
115 std::make_unique<PowerSupply>(bus, invpath, i2cbus, i2caddr);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600116 psus.emplace_back(std::move(psu));
117 }
Brandon Wymanc63941c2020-01-27 16:49:33 -0600118 else
119 {
120 log<level::ERR>("Insufficient PowerSupply properties");
121 }
Brandon Wymanaed1f752019-11-25 18:10:52 -0600122 }
123
124 if (psus.empty())
125 {
126 throw std::runtime_error("No power supplies to monitor");
127 }
128}
129
Brandon Wyman510acaa2020-11-05 18:32:04 -0600130void PSUManager::getPSUConfiguration()
131{
132 using namespace phosphor::power::util;
133 auto depth = 0;
134 auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
135
136 psus.clear();
137
138 // I should get a map of objects back.
139 // Each object will have a path, a service, and an interface.
140 // The interface should match the one passed into this function.
141 for (const auto& [path, services] : objects)
142 {
143 auto service = services.begin()->first;
144
145 if (path.empty() || service.empty())
146 {
147 continue;
148 }
149
150 // For each object in the array of objects, I want to get properties
151 // from the service, path, and interface.
152 auto properties =
153 getAllProperties(bus, path, IBMCFFPSInterface, service);
154
155 getPSUProperties(properties);
156 }
157
158 if (psus.empty())
159 {
160 // Interface or properties not found. Let the Interfaces Added callback
161 // process the information once the interfaces are added to D-Bus.
162 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
163 }
164}
165
166void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
167{
168 // From passed in properties, I want to get: I2CBus, I2CAddress,
169 // and Name. Create a power supply object, using Name to build the inventory
170 // path.
171 const auto basePSUInvPath =
172 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
173 uint64_t* i2cbus = nullptr;
174 uint64_t* i2caddr = nullptr;
175 std::string* psuname = nullptr;
176
177 for (const auto& property : properties)
178 {
179 try
180 {
181 if (property.first == i2cBusProp)
182 {
183 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
184 }
185 else if (property.first == i2cAddressProp)
186 {
187 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
188 }
189 else if (property.first == psuNameProp)
190 {
191 psuname = std::get_if<std::string>(&properties[psuNameProp]);
192 }
193 }
194 catch (std::exception& e)
195 {
196 }
197 }
198
199 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
200 {
201 std::string invpath = basePSUInvPath;
202 invpath.push_back(psuname->back());
203
204 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
205
206 auto psu =
207 std::make_unique<PowerSupply>(bus, invpath, *i2cbus, *i2caddr);
208 psus.emplace_back(std::move(psu));
209 }
210
211 if (psus.empty())
212 {
213 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
214 }
215}
216
Adriana Kobylake1074d82021-03-16 20:46:44 +0000217void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
218{
219 try
220 {
221 auto propIt = properties.find(maxCountProp);
222 if (propIt != properties.end())
223 {
224 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
225 if (count != nullptr)
226 {
227 sysProperties.maxPowerSupplies = *count;
228 }
229 }
230 }
231 catch (std::exception& e)
232 {
233 }
234}
235
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600236void PSUManager::getSystemProperties()
237{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600238
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600239 try
240 {
241 util::DbusSubtree subtree =
242 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000243 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600244 {
245 throw std::runtime_error("Supported Configuration Not Found");
246 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600247
Adriana Kobylake1074d82021-03-16 20:46:44 +0000248 for (const auto& [objPath, services] : subtree)
249 {
250 std::string service = services.begin()->first;
251 if (objPath.empty() || service.empty())
252 {
253 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600254 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000255 auto properties = util::getAllProperties(
256 bus, objPath, supportedConfIntf, service);
257 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600258 }
259 }
260 catch (std::exception& e)
261 {
262 // Interface or property not found. Let the Interfaces Added callback
263 // process the information once the interfaces are added to D-Bus.
264 }
265}
266
Brandon Wyman3e429132021-03-18 18:03:14 -0500267void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600268{
269 try
270 {
271 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000272 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600273 interfaces;
274 msg.read(objPath, interfaces);
275
276 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600277 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600278 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600279 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600280 }
281
Brandon Wyman510acaa2020-11-05 18:32:04 -0600282 itIntf = interfaces.find(IBMCFFPSInterface);
283 if (itIntf != interfaces.cend())
284 {
285 log<level::INFO>(
286 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
287 .c_str());
288 getPSUProperties(itIntf->second);
289 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600290 }
291 catch (std::exception& e)
292 {
293 // Ignore, the property may be of a different type than expected.
294 }
295}
296
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500297void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
298{
299 int32_t state = 0;
300 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500301 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500302 msg.read(msgSensor, msgData);
303
304 // Check if it was the Present property that changed.
305 auto valPropMap = msgData.find("state");
306 if (valPropMap != msgData.end())
307 {
308 state = std::get<int32_t>(valPropMap->second);
309
310 // Power is on when state=1. Clear faults.
311 if (state)
312 {
313 powerOn = true;
314 clearFaults();
315 }
316 else
317 {
318 powerOn = false;
319 }
320 }
321}
322
Brandon Wymanb76ab242020-09-16 18:06:06 -0500323void PSUManager::createError(
324 const std::string& faultName,
325 const std::map<std::string, std::string>& additionalData)
326{
327 using namespace sdbusplus::xyz::openbmc_project;
328 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
329 constexpr auto loggingCreateInterface =
330 "xyz.openbmc_project.Logging.Create";
331
332 try
333 {
334 auto service =
335 util::getService(loggingObjectPath, loggingCreateInterface, bus);
336
337 if (service.empty())
338 {
339 log<level::ERR>("Unable to get logging manager service");
340 return;
341 }
342
343 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
344 loggingCreateInterface, "Create");
345
346 auto level = Logging::server::Entry::Level::Error;
347 method.append(faultName, level, additionalData);
348
349 auto reply = bus.call(method);
350 }
351 catch (std::exception& e)
352 {
353 log<level::ERR>(
354 fmt::format(
355 "Failed creating event log for fault {} due to error {}",
356 faultName, e.what())
357 .c_str());
358 }
359}
360
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500361void PSUManager::analyze()
362{
363 for (auto& psu : psus)
364 {
365 psu->analyze();
366 }
367
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600368 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500369 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600370 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500371 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600372 std::map<std::string, std::string> additionalData;
373 additionalData["_PID"] = std::to_string(getpid());
374 // TODO: Fault priorities #918
375 if (!psu->isFaultLogged() && !psu->isPresent())
376 {
377 // Create error for power supply missing.
378 additionalData["CALLOUT_INVENTORY_PATH"] =
379 psu->getInventoryPath();
380 additionalData["CALLOUT_PRIORITY"] = "H";
381 createError(
382 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
383 additionalData);
384 psu->setFaultLogged();
385 }
386 else if (!psu->isFaultLogged() && psu->isFaulted())
387 {
388 additionalData["STATUS_WORD"] =
389 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600390 additionalData["STATUS_MFR"] =
391 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600392 // If there are faults being reported, they possibly could be
393 // related to a bug in the firmware version running on the power
394 // supply. Capture that data into the error as well.
395 additionalData["FW_VERSION"] = psu->getFWVersion();
396
397 if ((psu->hasInputFault() || psu->hasVINUVFault()))
398 {
399 /* The power supply location might be needed if the input
400 * fault is due to a problem with the power supply itself.
401 * Include the inventory path with a call out priority of
402 * low.
403 */
404 additionalData["CALLOUT_INVENTORY_PATH"] =
405 psu->getInventoryPath();
406 additionalData["CALLOUT_PRIORITY"] = "L";
407 createError("xyz.openbmc_project.Power.PowerSupply.Error."
408 "InputFault",
409 additionalData);
410 psu->setFaultLogged();
411 }
412 else if (psu->hasMFRFault())
413 {
414 /* This can represent a variety of faults that result in
415 * calling out the power supply for replacement: Output
416 * OverCurrent, Output Under Voltage, and potentially other
417 * faults.
418 *
419 * Also plan on putting specific fault in AdditionalData,
420 * along with register names and register values
421 * (STATUS_WORD, STATUS_MFR, etc.).*/
422
423 additionalData["CALLOUT_INVENTORY_PATH"] =
424 psu->getInventoryPath();
425
426 createError(
427 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500428 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500429
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600430 psu->setFaultLogged();
431 }
432 else if (psu->hasCommFault())
433 {
434 /* Attempts to communicate with the power supply have
435 * reached there limit. Create an error. */
436 additionalData["CALLOUT_DEVICE_PATH"] =
437 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500438
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600439 createError(
440 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
441 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500442
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600443 psu->setFaultLogged();
444 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500445 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500446 }
447 }
448}
449
450} // namespace phosphor::power::manager