blob: 73a974e96e12aafec4f09959939a0e717012fba4 [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";
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060022
Brandon Wyman510acaa2020-11-05 18:32:04 -060023PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
24 bus(bus)
25{
Brandon Wyman510acaa2020-11-05 18:32:04 -060026 // Subscribe to InterfacesAdded before doing a property read, otherwise
27 // the interface could be created after the read attempt but before the
28 // match is created.
29 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
30 bus,
31 sdbusplus::bus::match::rules::interfacesAdded() +
32 sdbusplus::bus::match::rules::sender(
33 "xyz.openbmc_project.EntityManager"),
34 std::bind(&PSUManager::entityManagerIfaceAdded, this,
35 std::placeholders::_1));
36 getPSUConfiguration();
37 getSystemProperties();
38
39 using namespace sdeventplus;
40 auto interval = std::chrono::milliseconds(1000);
41 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
42 e, std::bind(&PSUManager::analyze, this), interval);
43
44 // Subscribe to power state changes
45 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
46 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
47 bus,
48 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
49 POWER_IFACE),
50 [this](auto& msg) { this->powerStateChanged(msg); });
51
52 initialize();
53}
54
Brandon Wyman510acaa2020-11-05 18:32:04 -060055void PSUManager::getPSUConfiguration()
56{
57 using namespace phosphor::power::util;
58 auto depth = 0;
59 auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
60
61 psus.clear();
62
63 // I should get a map of objects back.
64 // Each object will have a path, a service, and an interface.
65 // The interface should match the one passed into this function.
66 for (const auto& [path, services] : objects)
67 {
68 auto service = services.begin()->first;
69
70 if (path.empty() || service.empty())
71 {
72 continue;
73 }
74
75 // For each object in the array of objects, I want to get properties
76 // from the service, path, and interface.
77 auto properties =
78 getAllProperties(bus, path, IBMCFFPSInterface, service);
79
80 getPSUProperties(properties);
81 }
82
83 if (psus.empty())
84 {
85 // Interface or properties not found. Let the Interfaces Added callback
86 // process the information once the interfaces are added to D-Bus.
87 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
88 }
89}
90
91void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
92{
93 // From passed in properties, I want to get: I2CBus, I2CAddress,
94 // and Name. Create a power supply object, using Name to build the inventory
95 // path.
96 const auto basePSUInvPath =
97 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
98 uint64_t* i2cbus = nullptr;
99 uint64_t* i2caddr = nullptr;
100 std::string* psuname = nullptr;
101
102 for (const auto& property : properties)
103 {
104 try
105 {
106 if (property.first == i2cBusProp)
107 {
108 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
109 }
110 else if (property.first == i2cAddressProp)
111 {
112 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
113 }
114 else if (property.first == psuNameProp)
115 {
116 psuname = std::get_if<std::string>(&properties[psuNameProp]);
117 }
118 }
119 catch (std::exception& e)
120 {
121 }
122 }
123
124 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
125 {
126 std::string invpath = basePSUInvPath;
127 invpath.push_back(psuname->back());
128
129 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
130
131 auto psu =
132 std::make_unique<PowerSupply>(bus, invpath, *i2cbus, *i2caddr);
133 psus.emplace_back(std::move(psu));
134 }
135
136 if (psus.empty())
137 {
138 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
139 }
140}
141
Adriana Kobylake1074d82021-03-16 20:46:44 +0000142void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
143{
144 try
145 {
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000146 auto propIt = properties.find("SupportedType");
147 if (propIt == properties.end())
148 {
149 return;
150 }
151 const std::string* type = std::get_if<std::string>(&(propIt->second));
152 if ((type == nullptr) || (*type != "PowerSupply"))
153 {
154 return;
155 }
156
157 std::vector<std::string> models;
158 propIt = properties.find("SupportedModel");
159 if (propIt == properties.end())
160 {
161 return;
162 }
163 const std::vector<std::string>* modelsPtr =
164 std::get_if<std::vector<std::string>>(&(propIt->second));
165 if (modelsPtr == nullptr)
166 {
167 return;
168 }
169 models = *modelsPtr;
170
171 sys_properties sys{0, 0};
172 propIt = properties.find("RedundantCount");
Adriana Kobylake1074d82021-03-16 20:46:44 +0000173 if (propIt != properties.end())
174 {
175 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
176 if (count != nullptr)
177 {
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000178 sys.maxPowerSupplies = *count;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000179 }
180 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000181 propIt = properties.find("InputVoltage");
182 if (propIt != properties.end())
183 {
184 const uint64_t* voltage = std::get_if<uint64_t>(&(propIt->second));
185 if (voltage != nullptr)
186 {
187 sys.inputVoltage = *voltage;
188 }
189 }
190
191 for (const auto& model : models)
192 {
193 supportedConfigs.insert(std::make_pair(model, sys));
194 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000195 }
196 catch (std::exception& e)
197 {
198 }
199}
200
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600201void PSUManager::getSystemProperties()
202{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600203
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600204 try
205 {
206 util::DbusSubtree subtree =
207 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000208 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600209 {
210 throw std::runtime_error("Supported Configuration Not Found");
211 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600212
Adriana Kobylake1074d82021-03-16 20:46:44 +0000213 for (const auto& [objPath, services] : subtree)
214 {
215 std::string service = services.begin()->first;
216 if (objPath.empty() || service.empty())
217 {
218 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600219 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000220 auto properties = util::getAllProperties(
221 bus, objPath, supportedConfIntf, service);
222 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600223 }
224 }
225 catch (std::exception& e)
226 {
227 // Interface or property not found. Let the Interfaces Added callback
228 // process the information once the interfaces are added to D-Bus.
229 }
230}
231
Brandon Wyman3e429132021-03-18 18:03:14 -0500232void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600233{
234 try
235 {
236 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000237 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600238 interfaces;
239 msg.read(objPath, interfaces);
240
241 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600242 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600243 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600244 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600245 }
246
Brandon Wyman510acaa2020-11-05 18:32:04 -0600247 itIntf = interfaces.find(IBMCFFPSInterface);
248 if (itIntf != interfaces.cend())
249 {
250 log<level::INFO>(
251 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
252 .c_str());
253 getPSUProperties(itIntf->second);
254 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600255 }
256 catch (std::exception& e)
257 {
258 // Ignore, the property may be of a different type than expected.
259 }
260}
261
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500262void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
263{
264 int32_t state = 0;
265 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500266 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500267 msg.read(msgSensor, msgData);
268
269 // Check if it was the Present property that changed.
270 auto valPropMap = msgData.find("state");
271 if (valPropMap != msgData.end())
272 {
273 state = std::get<int32_t>(valPropMap->second);
274
275 // Power is on when state=1. Clear faults.
276 if (state)
277 {
278 powerOn = true;
279 clearFaults();
280 }
281 else
282 {
283 powerOn = false;
284 }
285 }
286}
287
Brandon Wymanb76ab242020-09-16 18:06:06 -0500288void PSUManager::createError(
289 const std::string& faultName,
290 const std::map<std::string, std::string>& additionalData)
291{
292 using namespace sdbusplus::xyz::openbmc_project;
293 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
294 constexpr auto loggingCreateInterface =
295 "xyz.openbmc_project.Logging.Create";
296
297 try
298 {
299 auto service =
300 util::getService(loggingObjectPath, loggingCreateInterface, bus);
301
302 if (service.empty())
303 {
304 log<level::ERR>("Unable to get logging manager service");
305 return;
306 }
307
308 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
309 loggingCreateInterface, "Create");
310
311 auto level = Logging::server::Entry::Level::Error;
312 method.append(faultName, level, additionalData);
313
314 auto reply = bus.call(method);
315 }
316 catch (std::exception& e)
317 {
318 log<level::ERR>(
319 fmt::format(
320 "Failed creating event log for fault {} due to error {}",
321 faultName, e.what())
322 .c_str());
323 }
324}
325
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500326void PSUManager::analyze()
327{
328 for (auto& psu : psus)
329 {
330 psu->analyze();
331 }
332
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600333 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500334 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600335 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500336 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600337 std::map<std::string, std::string> additionalData;
338 additionalData["_PID"] = std::to_string(getpid());
339 // TODO: Fault priorities #918
340 if (!psu->isFaultLogged() && !psu->isPresent())
341 {
342 // Create error for power supply missing.
343 additionalData["CALLOUT_INVENTORY_PATH"] =
344 psu->getInventoryPath();
345 additionalData["CALLOUT_PRIORITY"] = "H";
346 createError(
347 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
348 additionalData);
349 psu->setFaultLogged();
350 }
351 else if (!psu->isFaultLogged() && psu->isFaulted())
352 {
353 additionalData["STATUS_WORD"] =
354 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600355 additionalData["STATUS_MFR"] =
356 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600357 // If there are faults being reported, they possibly could be
358 // related to a bug in the firmware version running on the power
359 // supply. Capture that data into the error as well.
360 additionalData["FW_VERSION"] = psu->getFWVersion();
361
362 if ((psu->hasInputFault() || psu->hasVINUVFault()))
363 {
364 /* The power supply location might be needed if the input
365 * fault is due to a problem with the power supply itself.
366 * Include the inventory path with a call out priority of
367 * low.
368 */
369 additionalData["CALLOUT_INVENTORY_PATH"] =
370 psu->getInventoryPath();
371 additionalData["CALLOUT_PRIORITY"] = "L";
372 createError("xyz.openbmc_project.Power.PowerSupply.Error."
373 "InputFault",
374 additionalData);
375 psu->setFaultLogged();
376 }
377 else if (psu->hasMFRFault())
378 {
379 /* This can represent a variety of faults that result in
380 * calling out the power supply for replacement: Output
381 * OverCurrent, Output Under Voltage, and potentially other
382 * faults.
383 *
384 * Also plan on putting specific fault in AdditionalData,
385 * along with register names and register values
386 * (STATUS_WORD, STATUS_MFR, etc.).*/
387
388 additionalData["CALLOUT_INVENTORY_PATH"] =
389 psu->getInventoryPath();
390
391 createError(
392 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500393 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500394
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600395 psu->setFaultLogged();
396 }
397 else if (psu->hasCommFault())
398 {
399 /* Attempts to communicate with the power supply have
400 * reached there limit. Create an error. */
401 additionalData["CALLOUT_DEVICE_PATH"] =
402 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500403
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600404 createError(
405 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
406 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500407
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600408 psu->setFaultLogged();
409 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500410 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500411 }
412 }
413}
414
415} // namespace phosphor::power::manager