blob: fa1294a60760ddd90e867cd140935683c8c4f04d [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
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060014constexpr auto supportedConfIntf =
15 "xyz.openbmc_project.Configuration.SupportedConfiguration";
16constexpr auto maxCountProp = "MaxCount";
17
Brandon Wymanaed1f752019-11-25 18:10:52 -060018PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
19 const std::string& configfile) :
20 bus(bus)
21{
22 // Parse out the JSON properties
Adriana Kobylak169975c2021-03-06 15:19:55 +000023 sysProperties = {0};
24 getJSONProperties(configfile);
Brandon Wyman31649012021-03-18 16:55:01 -050025 // Subscribe to InterfacesAdded before doing a property read, otherwise
26 // the interface could be created after the read attempt but before the
27 // match is created.
28 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
29 bus,
30 sdbusplus::bus::match::rules::interfacesAdded() +
31 sdbusplus::bus::match::rules::sender(
32 "xyz.openbmc_project.EntityManager"),
Brandon Wyman3e429132021-03-18 18:03:14 -050033 std::bind(&PSUManager::entityManagerIfaceAdded, this,
Brandon Wyman31649012021-03-18 16:55:01 -050034 std::placeholders::_1));
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060035 getSystemProperties();
Brandon Wymanaed1f752019-11-25 18:10:52 -060036
37 using namespace sdeventplus;
Jay Meyerda364552020-06-26 16:14:11 -050038 auto interval = std::chrono::milliseconds(1000);
Brandon Wymanaed1f752019-11-25 18:10:52 -060039 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
40 e, std::bind(&PSUManager::analyze, this), interval);
41
Brandon Wymanaed1f752019-11-25 18:10:52 -060042 // Subscribe to power state changes
43 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
44 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
45 bus,
46 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
47 POWER_IFACE),
48 [this](auto& msg) { this->powerStateChanged(msg); });
49
50 initialize();
51}
52
Adriana Kobylak169975c2021-03-06 15:19:55 +000053void PSUManager::getJSONProperties(const std::string& path)
Brandon Wymanaed1f752019-11-25 18:10:52 -060054{
55 nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
56
57 if (configFileJSON == nullptr)
58 {
59 throw std::runtime_error("Failed to load JSON configuration file");
60 }
61
Brandon Wymanaed1f752019-11-25 18:10:52 -060062 if (!configFileJSON.contains("PowerSupplies"))
63 {
64 throw std::runtime_error("Missing required PowerSupplies");
65 }
66
Brandon Wymanaed1f752019-11-25 18:10:52 -060067 for (auto psuJSON : configFileJSON["PowerSupplies"])
68 {
Brandon Wymanc63941c2020-01-27 16:49:33 -060069 if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") &&
70 psuJSON.contains("Address"))
Brandon Wymanaed1f752019-11-25 18:10:52 -060071 {
72 std::string invpath = psuJSON["Inventory"];
Brandon Wymanc63941c2020-01-27 16:49:33 -060073 std::uint8_t i2cbus = psuJSON["Bus"];
74 std::string i2caddr = psuJSON["Address"];
75 auto psu =
76 std::make_unique<PowerSupply>(bus, invpath, i2cbus, i2caddr);
Brandon Wymanaed1f752019-11-25 18:10:52 -060077 psus.emplace_back(std::move(psu));
78 }
Brandon Wymanc63941c2020-01-27 16:49:33 -060079 else
80 {
81 log<level::ERR>("Insufficient PowerSupply properties");
82 }
Brandon Wymanaed1f752019-11-25 18:10:52 -060083 }
84
85 if (psus.empty())
86 {
87 throw std::runtime_error("No power supplies to monitor");
88 }
89}
90
Adriana Kobylake1074d82021-03-16 20:46:44 +000091void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
92{
93 try
94 {
95 auto propIt = properties.find(maxCountProp);
96 if (propIt != properties.end())
97 {
98 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
99 if (count != nullptr)
100 {
101 sysProperties.maxPowerSupplies = *count;
102 }
103 }
104 }
105 catch (std::exception& e)
106 {
107 }
108}
109
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600110void PSUManager::getSystemProperties()
111{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600112
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600113 try
114 {
115 util::DbusSubtree subtree =
116 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000117 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600118 {
119 throw std::runtime_error("Supported Configuration Not Found");
120 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600121
Adriana Kobylake1074d82021-03-16 20:46:44 +0000122 for (const auto& [objPath, services] : subtree)
123 {
124 std::string service = services.begin()->first;
125 if (objPath.empty() || service.empty())
126 {
127 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600128 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000129 auto properties = util::getAllProperties(
130 bus, objPath, supportedConfIntf, service);
131 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600132 }
133 }
134 catch (std::exception& e)
135 {
136 // Interface or property not found. Let the Interfaces Added callback
137 // process the information once the interfaces are added to D-Bus.
138 }
139}
140
Brandon Wyman3e429132021-03-18 18:03:14 -0500141void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600142{
143 try
144 {
145 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000146 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600147 interfaces;
148 msg.read(objPath, interfaces);
149
150 auto itIntf = interfaces.find(supportedConfIntf);
151 if (itIntf == interfaces.cend())
152 {
153 return;
154 }
155
Adriana Kobylake1074d82021-03-16 20:46:44 +0000156 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600157 }
158 catch (std::exception& e)
159 {
160 // Ignore, the property may be of a different type than expected.
161 }
162}
163
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500164void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
165{
166 int32_t state = 0;
167 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500168 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500169 msg.read(msgSensor, msgData);
170
171 // Check if it was the Present property that changed.
172 auto valPropMap = msgData.find("state");
173 if (valPropMap != msgData.end())
174 {
175 state = std::get<int32_t>(valPropMap->second);
176
177 // Power is on when state=1. Clear faults.
178 if (state)
179 {
180 powerOn = true;
181 clearFaults();
182 }
183 else
184 {
185 powerOn = false;
186 }
187 }
188}
189
Brandon Wymanb76ab242020-09-16 18:06:06 -0500190void PSUManager::createError(
191 const std::string& faultName,
192 const std::map<std::string, std::string>& additionalData)
193{
194 using namespace sdbusplus::xyz::openbmc_project;
195 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
196 constexpr auto loggingCreateInterface =
197 "xyz.openbmc_project.Logging.Create";
198
199 try
200 {
201 auto service =
202 util::getService(loggingObjectPath, loggingCreateInterface, bus);
203
204 if (service.empty())
205 {
206 log<level::ERR>("Unable to get logging manager service");
207 return;
208 }
209
210 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
211 loggingCreateInterface, "Create");
212
213 auto level = Logging::server::Entry::Level::Error;
214 method.append(faultName, level, additionalData);
215
216 auto reply = bus.call(method);
217 }
218 catch (std::exception& e)
219 {
220 log<level::ERR>(
221 fmt::format(
222 "Failed creating event log for fault {} due to error {}",
223 faultName, e.what())
224 .c_str());
225 }
226}
227
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500228void PSUManager::analyze()
229{
230 for (auto& psu : psus)
231 {
232 psu->analyze();
233 }
234
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600235 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500236 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600237 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500238 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600239 std::map<std::string, std::string> additionalData;
240 additionalData["_PID"] = std::to_string(getpid());
241 // TODO: Fault priorities #918
242 if (!psu->isFaultLogged() && !psu->isPresent())
243 {
244 // Create error for power supply missing.
245 additionalData["CALLOUT_INVENTORY_PATH"] =
246 psu->getInventoryPath();
247 additionalData["CALLOUT_PRIORITY"] = "H";
248 createError(
249 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
250 additionalData);
251 psu->setFaultLogged();
252 }
253 else if (!psu->isFaultLogged() && psu->isFaulted())
254 {
255 additionalData["STATUS_WORD"] =
256 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600257 additionalData["STATUS_MFR"] =
258 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600259 // If there are faults being reported, they possibly could be
260 // related to a bug in the firmware version running on the power
261 // supply. Capture that data into the error as well.
262 additionalData["FW_VERSION"] = psu->getFWVersion();
263
264 if ((psu->hasInputFault() || psu->hasVINUVFault()))
265 {
266 /* The power supply location might be needed if the input
267 * fault is due to a problem with the power supply itself.
268 * Include the inventory path with a call out priority of
269 * low.
270 */
271 additionalData["CALLOUT_INVENTORY_PATH"] =
272 psu->getInventoryPath();
273 additionalData["CALLOUT_PRIORITY"] = "L";
274 createError("xyz.openbmc_project.Power.PowerSupply.Error."
275 "InputFault",
276 additionalData);
277 psu->setFaultLogged();
278 }
279 else if (psu->hasMFRFault())
280 {
281 /* This can represent a variety of faults that result in
282 * calling out the power supply for replacement: Output
283 * OverCurrent, Output Under Voltage, and potentially other
284 * faults.
285 *
286 * Also plan on putting specific fault in AdditionalData,
287 * along with register names and register values
288 * (STATUS_WORD, STATUS_MFR, etc.).*/
289
290 additionalData["CALLOUT_INVENTORY_PATH"] =
291 psu->getInventoryPath();
292
293 createError(
294 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500295 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500296
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600297 psu->setFaultLogged();
298 }
299 else if (psu->hasCommFault())
300 {
301 /* Attempts to communicate with the power supply have
302 * reached there limit. Create an error. */
303 additionalData["CALLOUT_DEVICE_PATH"] =
304 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500305
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600306 createError(
307 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
308 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500309
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600310 psu->setFaultLogged();
311 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500312 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500313 }
314 }
315}
316
317} // namespace phosphor::power::manager