blob: c6d58553ed5ea4e18ecd87706af5c69a588d40b7 [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
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000157 propIt = properties.find("SupportedModel");
158 if (propIt == properties.end())
159 {
160 return;
161 }
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000162 const std::string* model = std::get_if<std::string>(&(propIt->second));
163 if (model == nullptr)
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000164 {
165 return;
166 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000167
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000168 sys_properties sys;
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000169 propIt = properties.find("RedundantCount");
Adriana Kobylake1074d82021-03-16 20:46:44 +0000170 if (propIt != properties.end())
171 {
172 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
173 if (count != nullptr)
174 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000175 sys.powerSupplyCount = *count;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000176 }
177 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000178 propIt = properties.find("InputVoltage");
179 if (propIt != properties.end())
180 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000181 const std::vector<uint64_t>* voltage =
182 std::get_if<std::vector<uint64_t>>(&(propIt->second));
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000183 if (voltage != nullptr)
184 {
185 sys.inputVoltage = *voltage;
186 }
187 }
188
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000189 supportedConfigs.emplace(*model, sys);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000190 }
191 catch (std::exception& e)
192 {
193 }
194}
195
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600196void PSUManager::getSystemProperties()
197{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600198
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600199 try
200 {
201 util::DbusSubtree subtree =
202 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000203 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600204 {
205 throw std::runtime_error("Supported Configuration Not Found");
206 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600207
Adriana Kobylake1074d82021-03-16 20:46:44 +0000208 for (const auto& [objPath, services] : subtree)
209 {
210 std::string service = services.begin()->first;
211 if (objPath.empty() || service.empty())
212 {
213 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600214 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000215 auto properties = util::getAllProperties(
216 bus, objPath, supportedConfIntf, service);
217 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600218 }
219 }
220 catch (std::exception& e)
221 {
222 // Interface or property not found. Let the Interfaces Added callback
223 // process the information once the interfaces are added to D-Bus.
224 }
225}
226
Brandon Wyman3e429132021-03-18 18:03:14 -0500227void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600228{
229 try
230 {
231 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000232 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600233 interfaces;
234 msg.read(objPath, interfaces);
235
236 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600237 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600238 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600239 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600240 }
241
Brandon Wyman510acaa2020-11-05 18:32:04 -0600242 itIntf = interfaces.find(IBMCFFPSInterface);
243 if (itIntf != interfaces.cend())
244 {
245 log<level::INFO>(
246 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
247 .c_str());
248 getPSUProperties(itIntf->second);
249 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600250 }
251 catch (std::exception& e)
252 {
253 // Ignore, the property may be of a different type than expected.
254 }
255}
256
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500257void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
258{
259 int32_t state = 0;
260 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500261 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500262 msg.read(msgSensor, msgData);
263
264 // Check if it was the Present property that changed.
265 auto valPropMap = msgData.find("state");
266 if (valPropMap != msgData.end())
267 {
268 state = std::get<int32_t>(valPropMap->second);
269
270 // Power is on when state=1. Clear faults.
271 if (state)
272 {
273 powerOn = true;
274 clearFaults();
275 }
276 else
277 {
278 powerOn = false;
279 }
280 }
281}
282
Brandon Wymanb76ab242020-09-16 18:06:06 -0500283void PSUManager::createError(
284 const std::string& faultName,
285 const std::map<std::string, std::string>& additionalData)
286{
287 using namespace sdbusplus::xyz::openbmc_project;
288 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
289 constexpr auto loggingCreateInterface =
290 "xyz.openbmc_project.Logging.Create";
291
292 try
293 {
294 auto service =
295 util::getService(loggingObjectPath, loggingCreateInterface, bus);
296
297 if (service.empty())
298 {
299 log<level::ERR>("Unable to get logging manager service");
300 return;
301 }
302
303 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
304 loggingCreateInterface, "Create");
305
306 auto level = Logging::server::Entry::Level::Error;
307 method.append(faultName, level, additionalData);
308
309 auto reply = bus.call(method);
310 }
311 catch (std::exception& e)
312 {
313 log<level::ERR>(
314 fmt::format(
315 "Failed creating event log for fault {} due to error {}",
316 faultName, e.what())
317 .c_str());
318 }
319}
320
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500321void PSUManager::analyze()
322{
323 for (auto& psu : psus)
324 {
325 psu->analyze();
326 }
327
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600328 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500329 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600330 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500331 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600332 std::map<std::string, std::string> additionalData;
333 additionalData["_PID"] = std::to_string(getpid());
334 // TODO: Fault priorities #918
335 if (!psu->isFaultLogged() && !psu->isPresent())
336 {
337 // Create error for power supply missing.
338 additionalData["CALLOUT_INVENTORY_PATH"] =
339 psu->getInventoryPath();
340 additionalData["CALLOUT_PRIORITY"] = "H";
341 createError(
342 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
343 additionalData);
344 psu->setFaultLogged();
345 }
346 else if (!psu->isFaultLogged() && psu->isFaulted())
347 {
348 additionalData["STATUS_WORD"] =
349 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600350 additionalData["STATUS_MFR"] =
351 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600352 // If there are faults being reported, they possibly could be
353 // related to a bug in the firmware version running on the power
354 // supply. Capture that data into the error as well.
355 additionalData["FW_VERSION"] = psu->getFWVersion();
356
357 if ((psu->hasInputFault() || psu->hasVINUVFault()))
358 {
359 /* The power supply location might be needed if the input
360 * fault is due to a problem with the power supply itself.
361 * Include the inventory path with a call out priority of
362 * low.
363 */
364 additionalData["CALLOUT_INVENTORY_PATH"] =
365 psu->getInventoryPath();
366 additionalData["CALLOUT_PRIORITY"] = "L";
367 createError("xyz.openbmc_project.Power.PowerSupply.Error."
368 "InputFault",
369 additionalData);
370 psu->setFaultLogged();
371 }
372 else if (psu->hasMFRFault())
373 {
374 /* This can represent a variety of faults that result in
375 * calling out the power supply for replacement: Output
376 * OverCurrent, Output Under Voltage, and potentially other
377 * faults.
378 *
379 * Also plan on putting specific fault in AdditionalData,
380 * along with register names and register values
381 * (STATUS_WORD, STATUS_MFR, etc.).*/
382
383 additionalData["CALLOUT_INVENTORY_PATH"] =
384 psu->getInventoryPath();
385
386 createError(
387 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500388 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500389
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600390 psu->setFaultLogged();
391 }
392 else if (psu->hasCommFault())
393 {
394 /* Attempts to communicate with the power supply have
395 * reached there limit. Create an error. */
396 additionalData["CALLOUT_DEVICE_PATH"] =
397 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500398
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600399 createError(
400 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
401 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500402
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600403 psu->setFaultLogged();
404 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500405 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500406 }
407 }
408}
409
410} // namespace phosphor::power::manager