blob: ffab79acba7b00438b59c6b00ceb39ce75c1efaf [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 Kobylak9bab9e12021-02-24 15:32:03 -060091void PSUManager::getSystemProperties()
92{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060093
94 uint64_t maxCount;
95 try
96 {
97 util::DbusSubtree subtree =
98 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
99 auto objectIt = subtree.cbegin();
100 if (objectIt == subtree.cend())
101 {
102 throw std::runtime_error("Supported Configuration Not Found");
103 }
104 std::string objPath = objectIt->first;
105 auto serviceIt = objectIt->second.cbegin();
106 if (serviceIt != objectIt->second.cend())
107 {
108 std::string service = serviceIt->first;
109 if (!service.empty())
110 {
111 util::getProperty<uint64_t>(supportedConfIntf, maxCountProp,
112 objPath, service, bus, maxCount);
113 sysProperties.maxPowerSupplies = maxCount;
114
115 // Don't need the match anymore
116 entityManagerIfacesAddedMatch.reset();
117 }
118 }
119 }
120 catch (std::exception& e)
121 {
122 // Interface or property not found. Let the Interfaces Added callback
123 // process the information once the interfaces are added to D-Bus.
124 }
125}
126
Brandon Wyman3e429132021-03-18 18:03:14 -0500127void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600128{
129 try
130 {
131 sdbusplus::message::object_path objPath;
132 std::map<std::string, std::map<std::string, std::variant<uint64_t>>>
133 interfaces;
134 msg.read(objPath, interfaces);
135
136 auto itIntf = interfaces.find(supportedConfIntf);
137 if (itIntf == interfaces.cend())
138 {
139 return;
140 }
141
142 auto itProp = itIntf->second.find(maxCountProp);
143 if (itProp != itIntf->second.cend())
144 {
145 sysProperties.maxPowerSupplies = std::get<0>(itProp->second);
146
147 // Don't need the match anymore
148 entityManagerIfacesAddedMatch.reset();
149 }
150 }
151 catch (std::exception& e)
152 {
153 // Ignore, the property may be of a different type than expected.
154 }
155}
156
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500157void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
158{
159 int32_t state = 0;
160 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500161 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500162 msg.read(msgSensor, msgData);
163
164 // Check if it was the Present property that changed.
165 auto valPropMap = msgData.find("state");
166 if (valPropMap != msgData.end())
167 {
168 state = std::get<int32_t>(valPropMap->second);
169
170 // Power is on when state=1. Clear faults.
171 if (state)
172 {
173 powerOn = true;
174 clearFaults();
175 }
176 else
177 {
178 powerOn = false;
179 }
180 }
181}
182
Brandon Wymanb76ab242020-09-16 18:06:06 -0500183void PSUManager::createError(
184 const std::string& faultName,
185 const std::map<std::string, std::string>& additionalData)
186{
187 using namespace sdbusplus::xyz::openbmc_project;
188 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
189 constexpr auto loggingCreateInterface =
190 "xyz.openbmc_project.Logging.Create";
191
192 try
193 {
194 auto service =
195 util::getService(loggingObjectPath, loggingCreateInterface, bus);
196
197 if (service.empty())
198 {
199 log<level::ERR>("Unable to get logging manager service");
200 return;
201 }
202
203 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
204 loggingCreateInterface, "Create");
205
206 auto level = Logging::server::Entry::Level::Error;
207 method.append(faultName, level, additionalData);
208
209 auto reply = bus.call(method);
210 }
211 catch (std::exception& e)
212 {
213 log<level::ERR>(
214 fmt::format(
215 "Failed creating event log for fault {} due to error {}",
216 faultName, e.what())
217 .c_str());
218 }
219}
220
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500221void PSUManager::analyze()
222{
223 for (auto& psu : psus)
224 {
225 psu->analyze();
226 }
227
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600228 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500229 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600230 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500231 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600232 std::map<std::string, std::string> additionalData;
233 additionalData["_PID"] = std::to_string(getpid());
234 // TODO: Fault priorities #918
235 if (!psu->isFaultLogged() && !psu->isPresent())
236 {
237 // Create error for power supply missing.
238 additionalData["CALLOUT_INVENTORY_PATH"] =
239 psu->getInventoryPath();
240 additionalData["CALLOUT_PRIORITY"] = "H";
241 createError(
242 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
243 additionalData);
244 psu->setFaultLogged();
245 }
246 else if (!psu->isFaultLogged() && psu->isFaulted())
247 {
248 additionalData["STATUS_WORD"] =
249 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600250 additionalData["STATUS_MFR"] =
251 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600252 // If there are faults being reported, they possibly could be
253 // related to a bug in the firmware version running on the power
254 // supply. Capture that data into the error as well.
255 additionalData["FW_VERSION"] = psu->getFWVersion();
256
257 if ((psu->hasInputFault() || psu->hasVINUVFault()))
258 {
259 /* The power supply location might be needed if the input
260 * fault is due to a problem with the power supply itself.
261 * Include the inventory path with a call out priority of
262 * low.
263 */
264 additionalData["CALLOUT_INVENTORY_PATH"] =
265 psu->getInventoryPath();
266 additionalData["CALLOUT_PRIORITY"] = "L";
267 createError("xyz.openbmc_project.Power.PowerSupply.Error."
268 "InputFault",
269 additionalData);
270 psu->setFaultLogged();
271 }
272 else if (psu->hasMFRFault())
273 {
274 /* This can represent a variety of faults that result in
275 * calling out the power supply for replacement: Output
276 * OverCurrent, Output Under Voltage, and potentially other
277 * faults.
278 *
279 * Also plan on putting specific fault in AdditionalData,
280 * along with register names and register values
281 * (STATUS_WORD, STATUS_MFR, etc.).*/
282
283 additionalData["CALLOUT_INVENTORY_PATH"] =
284 psu->getInventoryPath();
285
286 createError(
287 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500288 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500289
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600290 psu->setFaultLogged();
291 }
292 else if (psu->hasCommFault())
293 {
294 /* Attempts to communicate with the power supply have
295 * reached there limit. Create an error. */
296 additionalData["CALLOUT_DEVICE_PATH"] =
297 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500298
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600299 createError(
300 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
301 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500302
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600303 psu->setFaultLogged();
304 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500305 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500306 }
307 }
308}
309
310} // namespace phosphor::power::manager