blob: 8045a4153a57a6e6ee942997030f3e63c15b700d [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 Wyman510acaa2020-11-05 18:32:04 -060024PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
25 bus(bus)
26{
27 sysProperties = {0};
28 // Subscribe to InterfacesAdded before doing a property read, otherwise
29 // the interface could be created after the read attempt but before the
30 // match is created.
31 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
32 bus,
33 sdbusplus::bus::match::rules::interfacesAdded() +
34 sdbusplus::bus::match::rules::sender(
35 "xyz.openbmc_project.EntityManager"),
36 std::bind(&PSUManager::entityManagerIfaceAdded, this,
37 std::placeholders::_1));
38 getPSUConfiguration();
39 getSystemProperties();
40
41 using namespace sdeventplus;
42 auto interval = std::chrono::milliseconds(1000);
43 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
44 e, std::bind(&PSUManager::analyze, this), interval);
45
46 // Subscribe to power state changes
47 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
48 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
49 bus,
50 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
51 POWER_IFACE),
52 [this](auto& msg) { this->powerStateChanged(msg); });
53
54 initialize();
55}
56
Brandon Wyman510acaa2020-11-05 18:32:04 -060057void PSUManager::getPSUConfiguration()
58{
59 using namespace phosphor::power::util;
60 auto depth = 0;
61 auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
62
63 psus.clear();
64
65 // I should get a map of objects back.
66 // Each object will have a path, a service, and an interface.
67 // The interface should match the one passed into this function.
68 for (const auto& [path, services] : objects)
69 {
70 auto service = services.begin()->first;
71
72 if (path.empty() || service.empty())
73 {
74 continue;
75 }
76
77 // For each object in the array of objects, I want to get properties
78 // from the service, path, and interface.
79 auto properties =
80 getAllProperties(bus, path, IBMCFFPSInterface, service);
81
82 getPSUProperties(properties);
83 }
84
85 if (psus.empty())
86 {
87 // Interface or properties not found. Let the Interfaces Added callback
88 // process the information once the interfaces are added to D-Bus.
89 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
90 }
91}
92
93void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
94{
95 // From passed in properties, I want to get: I2CBus, I2CAddress,
96 // and Name. Create a power supply object, using Name to build the inventory
97 // path.
98 const auto basePSUInvPath =
99 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
100 uint64_t* i2cbus = nullptr;
101 uint64_t* i2caddr = nullptr;
102 std::string* psuname = nullptr;
103
104 for (const auto& property : properties)
105 {
106 try
107 {
108 if (property.first == i2cBusProp)
109 {
110 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
111 }
112 else if (property.first == i2cAddressProp)
113 {
114 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
115 }
116 else if (property.first == psuNameProp)
117 {
118 psuname = std::get_if<std::string>(&properties[psuNameProp]);
119 }
120 }
121 catch (std::exception& e)
122 {
123 }
124 }
125
126 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
127 {
128 std::string invpath = basePSUInvPath;
129 invpath.push_back(psuname->back());
130
131 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
132
133 auto psu =
134 std::make_unique<PowerSupply>(bus, invpath, *i2cbus, *i2caddr);
135 psus.emplace_back(std::move(psu));
136 }
137
138 if (psus.empty())
139 {
140 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
141 }
142}
143
Adriana Kobylake1074d82021-03-16 20:46:44 +0000144void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
145{
146 try
147 {
148 auto propIt = properties.find(maxCountProp);
149 if (propIt != properties.end())
150 {
151 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
152 if (count != nullptr)
153 {
154 sysProperties.maxPowerSupplies = *count;
155 }
156 }
157 }
158 catch (std::exception& e)
159 {
160 }
161}
162
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600163void PSUManager::getSystemProperties()
164{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600165
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600166 try
167 {
168 util::DbusSubtree subtree =
169 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000170 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600171 {
172 throw std::runtime_error("Supported Configuration Not Found");
173 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600174
Adriana Kobylake1074d82021-03-16 20:46:44 +0000175 for (const auto& [objPath, services] : subtree)
176 {
177 std::string service = services.begin()->first;
178 if (objPath.empty() || service.empty())
179 {
180 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600181 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000182 auto properties = util::getAllProperties(
183 bus, objPath, supportedConfIntf, service);
184 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600185 }
186 }
187 catch (std::exception& e)
188 {
189 // Interface or property not found. Let the Interfaces Added callback
190 // process the information once the interfaces are added to D-Bus.
191 }
192}
193
Brandon Wyman3e429132021-03-18 18:03:14 -0500194void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600195{
196 try
197 {
198 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000199 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600200 interfaces;
201 msg.read(objPath, interfaces);
202
203 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600204 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600205 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600206 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600207 }
208
Brandon Wyman510acaa2020-11-05 18:32:04 -0600209 itIntf = interfaces.find(IBMCFFPSInterface);
210 if (itIntf != interfaces.cend())
211 {
212 log<level::INFO>(
213 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
214 .c_str());
215 getPSUProperties(itIntf->second);
216 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600217 }
218 catch (std::exception& e)
219 {
220 // Ignore, the property may be of a different type than expected.
221 }
222}
223
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500224void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
225{
226 int32_t state = 0;
227 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500228 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500229 msg.read(msgSensor, msgData);
230
231 // Check if it was the Present property that changed.
232 auto valPropMap = msgData.find("state");
233 if (valPropMap != msgData.end())
234 {
235 state = std::get<int32_t>(valPropMap->second);
236
237 // Power is on when state=1. Clear faults.
238 if (state)
239 {
240 powerOn = true;
241 clearFaults();
242 }
243 else
244 {
245 powerOn = false;
246 }
247 }
248}
249
Brandon Wymanb76ab242020-09-16 18:06:06 -0500250void PSUManager::createError(
251 const std::string& faultName,
252 const std::map<std::string, std::string>& additionalData)
253{
254 using namespace sdbusplus::xyz::openbmc_project;
255 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
256 constexpr auto loggingCreateInterface =
257 "xyz.openbmc_project.Logging.Create";
258
259 try
260 {
261 auto service =
262 util::getService(loggingObjectPath, loggingCreateInterface, bus);
263
264 if (service.empty())
265 {
266 log<level::ERR>("Unable to get logging manager service");
267 return;
268 }
269
270 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
271 loggingCreateInterface, "Create");
272
273 auto level = Logging::server::Entry::Level::Error;
274 method.append(faultName, level, additionalData);
275
276 auto reply = bus.call(method);
277 }
278 catch (std::exception& e)
279 {
280 log<level::ERR>(
281 fmt::format(
282 "Failed creating event log for fault {} due to error {}",
283 faultName, e.what())
284 .c_str());
285 }
286}
287
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500288void PSUManager::analyze()
289{
290 for (auto& psu : psus)
291 {
292 psu->analyze();
293 }
294
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600295 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500296 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600297 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500298 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600299 std::map<std::string, std::string> additionalData;
300 additionalData["_PID"] = std::to_string(getpid());
301 // TODO: Fault priorities #918
302 if (!psu->isFaultLogged() && !psu->isPresent())
303 {
304 // Create error for power supply missing.
305 additionalData["CALLOUT_INVENTORY_PATH"] =
306 psu->getInventoryPath();
307 additionalData["CALLOUT_PRIORITY"] = "H";
308 createError(
309 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
310 additionalData);
311 psu->setFaultLogged();
312 }
313 else if (!psu->isFaultLogged() && psu->isFaulted())
314 {
315 additionalData["STATUS_WORD"] =
316 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600317 additionalData["STATUS_MFR"] =
318 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600319 // If there are faults being reported, they possibly could be
320 // related to a bug in the firmware version running on the power
321 // supply. Capture that data into the error as well.
322 additionalData["FW_VERSION"] = psu->getFWVersion();
323
324 if ((psu->hasInputFault() || psu->hasVINUVFault()))
325 {
326 /* The power supply location might be needed if the input
327 * fault is due to a problem with the power supply itself.
328 * Include the inventory path with a call out priority of
329 * low.
330 */
331 additionalData["CALLOUT_INVENTORY_PATH"] =
332 psu->getInventoryPath();
333 additionalData["CALLOUT_PRIORITY"] = "L";
334 createError("xyz.openbmc_project.Power.PowerSupply.Error."
335 "InputFault",
336 additionalData);
337 psu->setFaultLogged();
338 }
339 else if (psu->hasMFRFault())
340 {
341 /* This can represent a variety of faults that result in
342 * calling out the power supply for replacement: Output
343 * OverCurrent, Output Under Voltage, and potentially other
344 * faults.
345 *
346 * Also plan on putting specific fault in AdditionalData,
347 * along with register names and register values
348 * (STATUS_WORD, STATUS_MFR, etc.).*/
349
350 additionalData["CALLOUT_INVENTORY_PATH"] =
351 psu->getInventoryPath();
352
353 createError(
354 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500355 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500356
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600357 psu->setFaultLogged();
358 }
359 else if (psu->hasCommFault())
360 {
361 /* Attempts to communicate with the power supply have
362 * reached there limit. Create an error. */
363 additionalData["CALLOUT_DEVICE_PATH"] =
364 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500365
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600366 createError(
367 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
368 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500369
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600370 psu->setFaultLogged();
371 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500372 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500373 }
374 }
375}
376
377} // namespace phosphor::power::manager