blob: b83d77dbbe6f27417c812a57320bd2268214551c [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 Wymanaed1f752019-11-25 18:10:52 -060014PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
15 const std::string& configfile) :
16 bus(bus)
17{
18 // Parse out the JSON properties
19 sys_properties properties;
20 getJSONProperties(configfile, bus, properties, psus);
21
22 using namespace sdeventplus;
Jay Meyerda364552020-06-26 16:14:11 -050023 auto interval = std::chrono::milliseconds(1000);
Brandon Wymanaed1f752019-11-25 18:10:52 -060024 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
25 e, std::bind(&PSUManager::analyze, this), interval);
26
27 minPSUs = {properties.minPowerSupplies};
28 maxPSUs = {properties.maxPowerSupplies};
29
30 // Subscribe to power state changes
31 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
32 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
33 bus,
34 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
35 POWER_IFACE),
36 [this](auto& msg) { this->powerStateChanged(msg); });
37
38 initialize();
39}
40
41void PSUManager::getJSONProperties(
42 const std::string& path, sdbusplus::bus::bus& bus, sys_properties& p,
43 std::vector<std::unique_ptr<PowerSupply>>& psus)
44{
45 nlohmann::json configFileJSON = util::loadJSONFromFile(path.c_str());
46
47 if (configFileJSON == nullptr)
48 {
49 throw std::runtime_error("Failed to load JSON configuration file");
50 }
51
52 if (!configFileJSON.contains("SystemProperties"))
53 {
54 throw std::runtime_error("Missing required SystemProperties");
55 }
56
57 if (!configFileJSON.contains("PowerSupplies"))
58 {
59 throw std::runtime_error("Missing required PowerSupplies");
60 }
61
62 auto sysProps = configFileJSON["SystemProperties"];
63
Brandon Wymanaed1f752019-11-25 18:10:52 -060064 if (sysProps.contains("MinPowerSupplies"))
65 {
66 p.minPowerSupplies = sysProps["MinPowerSupplies"];
67 }
68 else
69 {
70 p.minPowerSupplies = 0;
71 }
72
73 if (sysProps.contains("MaxPowerSupplies"))
74 {
75 p.maxPowerSupplies = sysProps["MaxPowerSupplies"];
76 }
77 else
78 {
79 p.maxPowerSupplies = 0;
80 }
81
82 for (auto psuJSON : configFileJSON["PowerSupplies"])
83 {
Brandon Wymanc63941c2020-01-27 16:49:33 -060084 if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") &&
85 psuJSON.contains("Address"))
Brandon Wymanaed1f752019-11-25 18:10:52 -060086 {
87 std::string invpath = psuJSON["Inventory"];
Brandon Wymanc63941c2020-01-27 16:49:33 -060088 std::uint8_t i2cbus = psuJSON["Bus"];
89 std::string i2caddr = psuJSON["Address"];
90 auto psu =
91 std::make_unique<PowerSupply>(bus, invpath, i2cbus, i2caddr);
Brandon Wymanaed1f752019-11-25 18:10:52 -060092 psus.emplace_back(std::move(psu));
93 }
Brandon Wymanc63941c2020-01-27 16:49:33 -060094 else
95 {
96 log<level::ERR>("Insufficient PowerSupply properties");
97 }
Brandon Wymanaed1f752019-11-25 18:10:52 -060098 }
99
100 if (psus.empty())
101 {
102 throw std::runtime_error("No power supplies to monitor");
103 }
104}
105
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500106void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
107{
108 int32_t state = 0;
109 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500110 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500111 msg.read(msgSensor, msgData);
112
113 // Check if it was the Present property that changed.
114 auto valPropMap = msgData.find("state");
115 if (valPropMap != msgData.end())
116 {
117 state = std::get<int32_t>(valPropMap->second);
118
119 // Power is on when state=1. Clear faults.
120 if (state)
121 {
122 powerOn = true;
123 clearFaults();
124 }
125 else
126 {
127 powerOn = false;
128 }
129 }
130}
131
Brandon Wymanb76ab242020-09-16 18:06:06 -0500132void PSUManager::createError(
133 const std::string& faultName,
134 const std::map<std::string, std::string>& additionalData)
135{
136 using namespace sdbusplus::xyz::openbmc_project;
137 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
138 constexpr auto loggingCreateInterface =
139 "xyz.openbmc_project.Logging.Create";
140
141 try
142 {
143 auto service =
144 util::getService(loggingObjectPath, loggingCreateInterface, bus);
145
146 if (service.empty())
147 {
148 log<level::ERR>("Unable to get logging manager service");
149 return;
150 }
151
152 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
153 loggingCreateInterface, "Create");
154
155 auto level = Logging::server::Entry::Level::Error;
156 method.append(faultName, level, additionalData);
157
158 auto reply = bus.call(method);
159 }
160 catch (std::exception& e)
161 {
162 log<level::ERR>(
163 fmt::format(
164 "Failed creating event log for fault {} due to error {}",
165 faultName, e.what())
166 .c_str());
167 }
168}
169
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500170void PSUManager::analyze()
171{
172 for (auto& psu : psus)
173 {
174 psu->analyze();
175 }
176
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600177 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500178 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600179 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500180 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600181 std::map<std::string, std::string> additionalData;
182 additionalData["_PID"] = std::to_string(getpid());
183 // TODO: Fault priorities #918
184 if (!psu->isFaultLogged() && !psu->isPresent())
185 {
186 // Create error for power supply missing.
187 additionalData["CALLOUT_INVENTORY_PATH"] =
188 psu->getInventoryPath();
189 additionalData["CALLOUT_PRIORITY"] = "H";
190 createError(
191 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
192 additionalData);
193 psu->setFaultLogged();
194 }
195 else if (!psu->isFaultLogged() && psu->isFaulted())
196 {
197 additionalData["STATUS_WORD"] =
198 std::to_string(psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600199 additionalData["STATUS_MFR"] =
200 std::to_string(psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600201 // If there are faults being reported, they possibly could be
202 // related to a bug in the firmware version running on the power
203 // supply. Capture that data into the error as well.
204 additionalData["FW_VERSION"] = psu->getFWVersion();
205
206 if ((psu->hasInputFault() || psu->hasVINUVFault()))
207 {
208 /* The power supply location might be needed if the input
209 * fault is due to a problem with the power supply itself.
210 * Include the inventory path with a call out priority of
211 * low.
212 */
213 additionalData["CALLOUT_INVENTORY_PATH"] =
214 psu->getInventoryPath();
215 additionalData["CALLOUT_PRIORITY"] = "L";
216 createError("xyz.openbmc_project.Power.PowerSupply.Error."
217 "InputFault",
218 additionalData);
219 psu->setFaultLogged();
220 }
221 else if (psu->hasMFRFault())
222 {
223 /* This can represent a variety of faults that result in
224 * calling out the power supply for replacement: Output
225 * OverCurrent, Output Under Voltage, and potentially other
226 * faults.
227 *
228 * Also plan on putting specific fault in AdditionalData,
229 * along with register names and register values
230 * (STATUS_WORD, STATUS_MFR, etc.).*/
231
232 additionalData["CALLOUT_INVENTORY_PATH"] =
233 psu->getInventoryPath();
234
235 createError(
236 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500237 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500238
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600239 psu->setFaultLogged();
240 }
241 else if (psu->hasCommFault())
242 {
243 /* Attempts to communicate with the power supply have
244 * reached there limit. Create an error. */
245 additionalData["CALLOUT_DEVICE_PATH"] =
246 psu->getDevicePath();
Brandon Wymanb76ab242020-09-16 18:06:06 -0500247
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600248 createError(
249 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
250 additionalData);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500251
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600252 psu->setFaultLogged();
253 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500254 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500255 }
256 }
257}
258
259} // namespace phosphor::power::manager