blob: 743727ff5f3b4424fc41aaec63d737f209b6023e [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
3#include "chassis_state_manager.hpp"
4
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -06005#include "utils.hpp"
Andrew Geisslere426b582020-05-28 12:40:55 -05006#include "xyz/openbmc_project/Common/error.hpp"
7#include "xyz/openbmc_project/State/Shutdown/Power/error.hpp"
8
Potin Lai70f36d82022-03-15 10:25:39 +08009#include <fmt/format.h>
10
Andrew Geisslere426b582020-05-28 12:40:55 -050011#include <cereal/archives/json.hpp>
12#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050013#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060014#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070015#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070016#include <sdeventplus/event.hpp>
17#include <sdeventplus/exception.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000018#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050019
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060020#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050021#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050022
Andrew Geisslera90a31a2016-12-13 16:16:28 -060023namespace phosphor
24{
25namespace state
26{
27namespace manager
28{
29
Andrew Geissler8ffdb262021-09-20 15:25:19 -050030PHOSPHOR_LOG2_USING;
31
Andrew Geisslera90a31a2016-12-13 16:16:28 -060032// When you see server:: you know we're referencing our base class
33namespace server = sdbusplus::xyz::openbmc_project::State::server;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000034namespace decoratorServer =
35 sdbusplus::xyz::openbmc_project::State::Decorator::server;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060036
37using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070038using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050039using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050040using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080041constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
42 "obmc-chassis-poweroff@{}.target";
43constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
44 "obmc-chassis-hard-poweroff@{}.target";
45constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
46constexpr auto RESET_HOST_SENSORS_SVC_FMT =
47 "phosphor-reset-sensor-states@{}.service";
Andrew Geissler77a91832022-05-11 12:11:03 -040048constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
49 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060050constexpr auto ACTIVE_STATE = "active";
51constexpr auto ACTIVATING_STATE = "activating";
52
Andrew Geissler2cf2a262022-02-02 14:38:41 -060053// Details at https://upower.freedesktop.org/docs/Device.html
54constexpr uint TYPE_UPS = 3;
55constexpr uint STATE_FULLY_CHARGED = 4;
56constexpr uint BATTERY_LVL_FULL = 8;
57
Andrew Geissler58a18012018-01-19 19:36:05 -080058constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
59constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060060constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
61
Josh D. King697474c2017-03-02 11:15:55 -060062constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
63constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
64
Andrew Geissler8b1f8622022-01-28 16:37:07 -060065constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
66constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
67constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
68constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000069constexpr auto POWERSYSINPUTS_INTERFACE =
70 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060071constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
72
Andrew Geissler0029a5d2017-01-24 14:48:35 -060073void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060074{
Andrew Geissler3a30b052019-05-14 15:54:37 -050075 try
76 {
77 auto method = this->bus.new_method_call(
78 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050079 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050080 }
Andrew Geissler8ffdb262021-09-20 15:25:19 -050081 catch (const sdbusplus::exception::exception& e)
Andrew Geissler3a30b052019-05-14 15:54:37 -050082 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050083 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler3a30b052019-05-14 15:54:37 -050084 elog<InternalFailure>();
85 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060086
Andrew Geissler0029a5d2017-01-24 14:48:35 -060087 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060088}
89
Potin Lai70f36d82022-03-15 10:25:39 +080090void Chassis::createSystemdTargetTable()
91{
92 systemdTargetTable = {
93 // Use the hard off target to ensure we shutdown immediately
94 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
95 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)}};
96}
97
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060098// TODO - Will be rewritten once sdbusplus client bindings are in place
99// and persistent storage design is in place and sdbusplus
100// has read property function
101void Chassis::determineInitialState()
102{
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600103
104 // Monitor for any properties changed signals on UPower device path
105 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
106 bus,
107 sdbusplus::bus::match::rules::propertiesChangedNamespace(
108 "/org/freedesktop/UPower", UPOWER_INTERFACE),
109 [this](auto& msg) { this->uPowerChangeEvent(msg); });
110
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000111 // Monitor for any properties changed signals on PowerSystemInputs
112 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
113 bus,
114 sdbusplus::bus::match::rules::propertiesChangedNamespace(
115 fmt::format(
116 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
117 POWERSYSINPUTS_INTERFACE),
118 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
119
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600120 determineStatusOfPower();
121
Patrick Williams2975e262020-05-13 18:01:09 -0500122 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800123 auto method = this->bus.new_method_call(
124 "org.openbmc.control.Power", "/org/openbmc/control/power0",
125 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600126
127 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700128 try
129 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700130 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500131 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700132
Patrick Williams37413dc2020-05-13 11:29:54 -0500133 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700134 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500135 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700136 server::Chassis::currentPowerState(PowerState::On);
137 server::Chassis::requestedPowerTransition(Transition::On);
138 return;
139 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500140 else
141 {
142 // The system is off. If we think it should be on then
143 // we probably lost AC while up, so set a new state
144 // change time.
145 uint64_t lastTime;
146 PowerState lastState;
147
148 if (deserializeStateChangeTime(lastTime, lastState))
149 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600150 // If power was on before the BMC reboot and the reboot reason
151 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500152 if (lastState == PowerState::On)
153 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600154 info(
155 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600156
157 // Reset host sensors since system is off now
Potin Lai70f36d82022-03-15 10:25:39 +0800158 startUnit(fmt::format(RESET_HOST_SENSORS_SVC_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600159
Matt Spinler9eab9862018-07-11 14:13:52 -0500160 setStateChangeTime();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600161
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000162 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600163 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000164 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600165 {
166 if (standbyVoltageRegulatorFault())
167 {
168 report<Regulator>();
169 }
170 else
171 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400172 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600173 }
174 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000175 else
176 {
177 info("Pinhole reset");
178 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500179 }
180 }
181 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700182 }
Patrick Williams0a675212021-09-02 09:49:43 -0500183 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700184 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700185 // It's acceptable for the pgood state service to not be available
186 // since it will notify us of the pgood state when it comes up.
187 if (e.name() != nullptr &&
188 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
189 {
190 goto fail;
191 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600192
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700193 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500194 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700195 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600196 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700197
198fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500199 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700200 server::Chassis::currentPowerState(PowerState::Off);
201 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600202
203 return;
204}
205
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600206void Chassis::determineStatusOfPower()
207{
Andrew Geissler77a91832022-05-11 12:11:03 -0400208 auto initialPowerStatus = server::Chassis::currentPowerStatus();
209
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400210 bool powerGood = determineStatusOfUPSPower();
211 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000212 {
213 return;
214 }
215
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400216 powerGood = determineStatusOfPSUPower();
217 if (powerGood)
218 {
219 // All checks passed, set power status to good
220 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400221
222 // If power status transitioned from bad to good and chassis power is
223 // off then call Auto Power Restart to see if the system should auto
224 // power on now that power status is good
225 if ((initialPowerStatus != PowerStatus::Good) &&
226 (server::Chassis::currentPowerState() == PowerState::Off))
227 {
228 info("power status transitioned from {START_PWR_STATE} to Good and "
229 "chassis power is off, calling APR",
230 "START_PWR_STATE", initialPowerStatus);
231 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
232 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400233 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000234}
235
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400236bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000237{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600238 // Find all implementations of the UPower interface
239 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
240 MAPPER_INTERFACE, "GetSubTree");
241
242 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
243
244 std::map<std::string, std::map<std::string, std::vector<std::string>>>
245 mapperResponse;
246
247 try
248 {
249 auto mapperResponseMsg = bus.call(mapper);
250 mapperResponseMsg.read(mapperResponse);
251 }
252 catch (const sdbusplus::exception::exception& e)
253 {
254 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
255 throw;
256 }
257
258 if (mapperResponse.empty())
259 {
260 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600261 }
262
263 // Iterate through all returned Upower interfaces and look for UPS's
264 for (const auto& [path, services] : mapperResponse)
265 {
266 for (const auto& serviceIter : services)
267 {
268 const std::string& service = serviceIter.first;
269
270 try
271 {
272 auto method = bus.new_method_call(service.c_str(), path.c_str(),
273 PROPERTY_INTERFACE, "GetAll");
274 method.append(UPOWER_INTERFACE);
275
276 auto response = bus.call(method);
277 using Property = std::string;
278 using Value = std::variant<bool, uint>;
279 using PropertyMap = std::map<Property, Value>;
280 PropertyMap properties;
281 response.read(properties);
282
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600283 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
284 {
285 info("UPower device {OBJ_PATH} is not a UPS device",
286 "OBJ_PATH", path);
287 continue;
288 }
289
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600290 if (std::get<bool>(properties["IsPresent"]) != true)
291 {
292 // There is a UPS detected but it is not officially
293 // "present" yet. Monitor it for state change.
294 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
295 path);
296 continue;
297 }
298
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600299 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
300 {
301 info("UPS is fully charged");
302 }
303 else
304 {
305 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
306 std::get<uint>(properties["State"]));
307 server::Chassis::currentPowerStatus(
308 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400309 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600310 }
311
312 if (std::get<uint>(properties["BatteryLevel"]) ==
313 BATTERY_LVL_FULL)
314 {
315 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600316 // Only one UPS per system, we've found it and it's all
317 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400318 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600319 }
320 else
321 {
322 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
323 "UPS_BAT_LEVEL",
324 std::get<uint>(properties["BatteryLevel"]));
325 server::Chassis::currentPowerStatus(
326 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400327 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600328 }
329 }
330 catch (const sdbusplus::exception::exception& e)
331 {
332 error("Error reading UPS property, error: {ERROR}, "
333 "service: {SERVICE} path: {PATH}",
334 "ERROR", e, "SERVICE", service, "PATH", path);
335 throw;
336 }
337 }
338 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400339 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600340}
341
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400342bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000343{
344 // Find all implementations of the PowerSystemInputs interface
345 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
346 MAPPER_INTERFACE, "GetSubTree");
347
348 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
349
350 std::map<std::string, std::map<std::string, std::vector<std::string>>>
351 mapperResponse;
352
353 try
354 {
355 auto mapperResponseMsg = bus.call(mapper);
356 mapperResponseMsg.read(mapperResponse);
357 }
358 catch (const sdbusplus::exception::exception& e)
359 {
360 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
361 "ERROR", e);
362 throw;
363 }
364
365 for (const auto& [path, services] : mapperResponse)
366 {
367 for (const auto& serviceIter : services)
368 {
369 const std::string& service = serviceIter.first;
370
371 try
372 {
373 auto method = bus.new_method_call(service.c_str(), path.c_str(),
374 PROPERTY_INTERFACE, "GetAll");
375 method.append(POWERSYSINPUTS_INTERFACE);
376
377 auto response = bus.call(method);
378 using Property = std::string;
379 using Value = std::variant<std::string>;
380 using PropertyMap = std::map<Property, Value>;
381 PropertyMap properties;
382 response.read(properties);
383
384 auto statusStr = std::get<std::string>(properties["Status"]);
385 auto status =
386 decoratorServer::PowerSystemInputs::convertStatusFromString(
387 statusStr);
388
389 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
390 {
391 info("Power System Inputs status is in Fault state");
392 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400393 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000394 }
395 }
396 catch (const sdbusplus::exception::exception& e)
397 {
398 error(
399 "Error reading Power System Inputs property, error: {ERROR}, "
400 "service: {SERVICE} path: {PATH}",
401 "ERROR", e, "SERVICE", service, "PATH", path);
402 throw;
403 }
404 }
405 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400406 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000407}
408
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600409void Chassis::uPowerChangeEvent(sdbusplus::message::message& msg)
410{
411 debug("UPS Property Change Event Triggered");
412 std::string statusInterface;
413 std::map<std::string, std::variant<uint, bool>> msgData;
414 msg.read(statusInterface, msgData);
415
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000416 // If the change is to any of the properties we are interested in, then call
417 // determineStatusOfPower(), which looks at all the power-related
418 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600419 auto propertyMap = msgData.find("IsPresent");
420 if (propertyMap != msgData.end())
421 {
422 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
423 std::get<bool>(propertyMap->second));
424 determineStatusOfPower();
425 return;
426 }
427
428 propertyMap = msgData.find("State");
429 if (propertyMap != msgData.end())
430 {
431 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
432 std::get<uint>(propertyMap->second));
433 determineStatusOfPower();
434 return;
435 }
436
437 propertyMap = msgData.find("BatteryLevel");
438 if (propertyMap != msgData.end())
439 {
440 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
441 std::get<uint>(propertyMap->second));
442 determineStatusOfPower();
443 return;
444 }
445 return;
446}
447
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000448void Chassis::powerSysInputsChangeEvent(sdbusplus::message::message& msg)
449{
450 debug("Power System Inputs Property Change Event Triggered");
451 std::string statusInterface;
452 std::map<std::string, std::variant<std::string>> msgData;
453 msg.read(statusInterface, msgData);
454
455 // If the change is to any of the properties we are interested in, then call
456 // determineStatusOfPower(), which looks at all the power-related
457 // interfaces, to see if a power status change is needed
458 auto propertyMap = msgData.find("Status");
459 if (propertyMap != msgData.end())
460 {
461 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
462 "POWER_SYS_INPUT_STATUS",
463 std::get<std::string>(propertyMap->second));
464 determineStatusOfPower();
465 return;
466 }
467 return;
468}
469
Matthew Barthbe6efab2022-03-01 13:21:45 -0600470void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600471{
Andrew Geissler58a18012018-01-19 19:36:05 -0800472 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
473 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600474
Matthew Barthbe6efab2022-03-01 13:21:45 -0600475 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600476 method.append("replace");
477
478 this->bus.call_noreply(method);
479
480 return;
481}
482
Andrew Geissler77a91832022-05-11 12:11:03 -0400483void Chassis::restartUnit(const std::string& sysdUnit)
484{
485 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
486 SYSTEMD_INTERFACE, "RestartUnit");
487
488 method.append(sysdUnit);
489 method.append("replace");
490
491 this->bus.call_noreply(method);
492
493 return;
494}
495
Josh D. King697474c2017-03-02 11:15:55 -0600496bool Chassis::stateActive(const std::string& target)
497{
Patrick Williams2975e262020-05-13 18:01:09 -0500498 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600499 sdbusplus::message::object_path unitTargetPath;
500
Andrew Geissler58a18012018-01-19 19:36:05 -0800501 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
502 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600503
504 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600505
William A. Kennington III09568ff2018-05-11 00:03:12 -0700506 try
507 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500508 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700509 result.read(unitTargetPath);
510 }
Patrick Williams0a675212021-09-02 09:49:43 -0500511 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700512 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500513 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700514 return false;
515 }
Josh D. King697474c2017-03-02 11:15:55 -0600516
Andrew Geissler58a18012018-01-19 19:36:05 -0800517 method = this->bus.new_method_call(
518 SYSTEMD_SERVICE,
519 static_cast<const std::string&>(unitTargetPath).c_str(),
520 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600521
522 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600523
Anthony Wilson32c532e2018-10-25 21:56:07 -0500524 try
Josh D. King697474c2017-03-02 11:15:55 -0600525 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500526 auto result = this->bus.call(method);
527 result.read(currentState);
528 }
Patrick Williams0a675212021-09-02 09:49:43 -0500529 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500530 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500531 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600532 return false;
533 }
534
Patrick Williams37413dc2020-05-13 11:29:54 -0500535 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800536 return currentStateStr == ACTIVE_STATE ||
537 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600538}
539
Patrick Williams8f8ba392017-05-05 15:47:39 -0500540int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600541{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600542 sdbusplus::message::object_path newStateObjPath;
543 std::string newStateUnit{};
544 std::string newStateResult{};
545
Andrew Geissler58a18012018-01-19 19:36:05 -0800546 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700547 try
548 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500549 // newStateID is a throwaway that is needed in order to read the
550 // parameters that are useful out of the dbus message
551 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700552 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
553 }
Patrick Williams0a675212021-09-02 09:49:43 -0500554 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700555 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500556 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
557 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700558 return 0;
559 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600560
Allen.Wangc0895622022-03-23 15:46:47 +0800561 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800562 (newStateResult == "done") &&
563 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600564 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500565 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600566 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500567 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600568 }
Potin Lai70f36d82022-03-15 10:25:39 +0800569 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800570 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800571 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800572 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500573 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800574 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500575 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600576
577 // Remove temporary file which is utilized for scenarios where the
578 // BMC is rebooted while the chassis power is still on.
579 // This file is used to indicate to chassis related systemd services
580 // that the chassis is already on and they should skip running.
581 // Once the chassis state is back to on we can clear this file.
582 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
583 size++; // null
584 std::unique_ptr<char[]> chassisFile(new char[size]);
585 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
586 if (std::filesystem::exists(chassisFile.get()))
587 {
588 std::filesystem::remove(chassisFile.get());
589 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800590 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600591
592 return 0;
593}
594
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600595Chassis::Transition Chassis::requestedPowerTransition(Transition value)
596{
597
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500598 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
599 "REQ_POWER_TRAN", value);
Potin Lai70f36d82022-03-15 10:25:39 +0800600 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600601 return server::Chassis::requestedPowerTransition(value);
602}
603
604Chassis::PowerState Chassis::currentPowerState(PowerState value)
605{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500606 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500607 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
608 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500609
610 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530611 if (chassisPowerState == PowerState::On)
612 {
613 pohTimer.resetRemaining();
614 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500615 return chassisPowerState;
616}
617
Patrick Williams45a1ed72021-04-30 21:02:43 -0500618uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500619{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500620 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500621 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500622 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500623 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500624 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500625 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500626}
627
Patrick Williams45a1ed72021-04-30 21:02:43 -0500628void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700629{
630 if (ChassisInherit::currentPowerState() == PowerState::On)
631 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500632 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700633 }
634}
635
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500636void Chassis::restorePOHCounter()
637{
638 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800639 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500640 {
641 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500642 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500643 }
644 else
645 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500646 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500647 }
648}
649
Allen.Wangba182f02022-03-23 19:01:53 +0800650fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500651{
Allen.Wangba182f02022-03-23 19:01:53 +0800652 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500653 std::ofstream os(path.c_str(), std::ios::binary);
654 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500655 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500656 return path;
657}
658
Allen.Wangba182f02022-03-23 19:01:53 +0800659bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500660{
Allen.Wangba182f02022-03-23 19:01:53 +0800661 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500662 try
663 {
664 if (fs::exists(path))
665 {
666 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
667 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500668 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500669 return true;
670 }
671 return false;
672 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500673 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500674 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500675 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500676 fs::remove(path);
677 return false;
678 }
679 catch (const fs::filesystem_error& e)
680 {
681 return false;
682 }
683
684 return false;
685}
686
687void Chassis::startPOHCounter()
688{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500689 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
690 fs::create_directories(dir);
691
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500692 try
693 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700694 auto event = sdeventplus::Event::get_default();
695 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
696 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500697 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700698 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500699 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500700 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
701 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500702 phosphor::logging::commit<InternalFailure>();
703 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600704}
705
Matt Spinler9eab9862018-07-11 14:13:52 -0500706void Chassis::serializeStateChangeTime()
707{
Allen.Wangba182f02022-03-23 19:01:53 +0800708 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500709 std::ofstream os(path.c_str(), std::ios::binary);
710 cereal::JSONOutputArchive oarchive(os);
711
712 oarchive(ChassisInherit::lastStateChangeTime(),
713 ChassisInherit::currentPowerState());
714}
715
716bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
717{
Allen.Wangba182f02022-03-23 19:01:53 +0800718 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500719
720 try
721 {
722 if (fs::exists(path))
723 {
724 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
725 cereal::JSONInputArchive iarchive(is);
726 iarchive(time, state);
727 return true;
728 }
729 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500730 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500731 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500732 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500733 fs::remove(path);
734 }
735
736 return false;
737}
738
739void Chassis::restoreChassisStateChangeTime()
740{
741 uint64_t time;
742 PowerState state;
743
744 if (!deserializeStateChangeTime(time, state))
745 {
746 ChassisInherit::lastStateChangeTime(0);
747 }
748 else
749 {
750 ChassisInherit::lastStateChangeTime(time);
751 }
752}
753
754void Chassis::setStateChangeTime()
755{
756 using namespace std::chrono;
757 uint64_t lastTime;
758 PowerState lastState;
759
760 auto now =
761 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
762 .count();
763
764 // If power is on when the BMC is rebooted, this function will get called
765 // because sysStateChange() runs. Since the power state didn't change
766 // in this case, neither should the state change time, so check that
767 // the power state actually did change here.
768 if (deserializeStateChangeTime(lastTime, lastState))
769 {
770 if (lastState == ChassisInherit::currentPowerState())
771 {
772 return;
773 }
774 }
775
776 ChassisInherit::lastStateChangeTime(now);
777 serializeStateChangeTime();
778}
779
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500780bool Chassis::standbyVoltageRegulatorFault()
781{
782 bool regulatorFault = false;
783
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600784 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500785
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600786 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
787
788 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500789 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600790 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500791 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600792
793 if (1 == gpioval)
794 {
795 info("Detected standby voltage regulator fault");
796 regulatorFault = true;
797 }
798
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500799 return regulatorFault;
800}
801
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600802} // namespace manager
803} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700804} // namespace phosphor