blob: 71accee33d0cab2355b63af3dd774dc652b09611 [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>
NodeMan97bcbee4a2022-05-27 15:28:45 -050010#include <fmt/printf.h>
Potin Lai70f36d82022-03-15 10:25:39 +080011
Andrew Geisslere426b582020-05-28 12:40:55 -050012#include <cereal/archives/json.hpp>
13#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050014#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060015#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070016#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070017#include <sdeventplus/event.hpp>
18#include <sdeventplus/exception.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000019#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050020
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060021#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050022#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050023
Andrew Geisslera90a31a2016-12-13 16:16:28 -060024namespace phosphor
25{
26namespace state
27{
28namespace manager
29{
30
Andrew Geissler8ffdb262021-09-20 15:25:19 -050031PHOSPHOR_LOG2_USING;
32
Andrew Geisslera90a31a2016-12-13 16:16:28 -060033// When you see server:: you know we're referencing our base class
34namespace server = sdbusplus::xyz::openbmc_project::State::server;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000035namespace decoratorServer =
36 sdbusplus::xyz::openbmc_project::State::Decorator::server;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060037
38using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070039using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050040using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050041using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080042constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
43 "obmc-chassis-poweroff@{}.target";
44constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
45 "obmc-chassis-hard-poweroff@{}.target";
46constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -050047constexpr auto CHASSIS_BLACKOUT_TGT_FMT = "obmc-chassis-blackout@{}.target";
DelphineCCChiu06881322022-11-09 08:41:25 +080048constexpr auto CHASSIS_STATE_POWERCYCLE_TGT_FMT =
49 "obmc-chassis-powercycle@{}.target";
Andrew Geissler77a91832022-05-11 12:11:03 -040050constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
51 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060052constexpr auto ACTIVE_STATE = "active";
53constexpr auto ACTIVATING_STATE = "activating";
54
Andrew Geissler2cf2a262022-02-02 14:38:41 -060055// Details at https://upower.freedesktop.org/docs/Device.html
56constexpr uint TYPE_UPS = 3;
57constexpr uint STATE_FULLY_CHARGED = 4;
58constexpr uint BATTERY_LVL_FULL = 8;
59
Andrew Geissler58a18012018-01-19 19:36:05 -080060constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
61constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060062constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
63
Josh D. King697474c2017-03-02 11:15:55 -060064constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
65constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
66
Andrew Geissler8b1f8622022-01-28 16:37:07 -060067constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
68constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
69constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
70constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000071constexpr auto POWERSYSINPUTS_INTERFACE =
72 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060073constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
74
Potin Lai70f36d82022-03-15 10:25:39 +080075void Chassis::createSystemdTargetTable()
76{
77 systemdTargetTable = {
78 // Use the hard off target to ensure we shutdown immediately
79 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
DelphineCCChiu06881322022-11-09 08:41:25 +080080 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)},
81 {Transition::PowerCycle,
82 fmt::format(CHASSIS_STATE_POWERCYCLE_TGT_FMT, id)}};
Potin Lai70f36d82022-03-15 10:25:39 +080083}
84
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060085// TODO - Will be rewritten once sdbusplus client bindings are in place
86// and persistent storage design is in place and sdbusplus
87// has read property function
88void Chassis::determineInitialState()
89{
Andrew Geissler2cf2a262022-02-02 14:38:41 -060090 // Monitor for any properties changed signals on UPower device path
91 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
92 bus,
93 sdbusplus::bus::match::rules::propertiesChangedNamespace(
94 "/org/freedesktop/UPower", UPOWER_INTERFACE),
95 [this](auto& msg) { this->uPowerChangeEvent(msg); });
96
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000097 // Monitor for any properties changed signals on PowerSystemInputs
98 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
99 bus,
100 sdbusplus::bus::match::rules::propertiesChangedNamespace(
101 fmt::format(
102 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
103 POWERSYSINPUTS_INTERFACE),
104 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
105
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600106 determineStatusOfPower();
107
Patrick Williams2975e262020-05-13 18:01:09 -0500108 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800109 auto method = this->bus.new_method_call(
110 "org.openbmc.control.Power", "/org/openbmc/control/power0",
111 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600112
113 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700114 try
115 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700116 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500117 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700118
Patrick Williams37413dc2020-05-13 11:29:54 -0500119 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700120 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500121 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700122 server::Chassis::currentPowerState(PowerState::On);
123 server::Chassis::requestedPowerTransition(Transition::On);
124 return;
125 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500126 else
127 {
128 // The system is off. If we think it should be on then
129 // we probably lost AC while up, so set a new state
130 // change time.
131 uint64_t lastTime;
132 PowerState lastState;
133
134 if (deserializeStateChangeTime(lastTime, lastState))
135 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600136 // If power was on before the BMC reboot and the reboot reason
137 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500138 if (lastState == PowerState::On)
139 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600140 info(
141 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600142
143 // Reset host sensors since system is off now
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -0500144 // Ensure Power Leds are off.
145 startUnit(fmt::format(CHASSIS_BLACKOUT_TGT_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600146
Matt Spinler9eab9862018-07-11 14:13:52 -0500147 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500148 // Generate file indicating AC loss occurred
149 std::string chassisLostPowerFileFmt =
150 fmt::sprintf(CHASSIS_LOST_POWER_FILE, id);
151 fs::create_directories(BASE_FILE_DIR);
152 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
153 std::ofstream outfile(chassisPowerLossFile);
154 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600155
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000156 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600157 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000158 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600159 {
160 if (standbyVoltageRegulatorFault())
161 {
162 report<Regulator>();
163 }
164 else
165 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400166 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600167 }
168 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000169 else
170 {
171 info("Pinhole reset");
172 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500173 }
174 }
175 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700176 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500177 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700178 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700179 // It's acceptable for the pgood state service to not be available
180 // since it will notify us of the pgood state when it comes up.
181 if (e.name() != nullptr &&
182 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
183 {
184 goto fail;
185 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600186
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700187 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500188 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700189 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600190 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700191
192fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500193 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700194 server::Chassis::currentPowerState(PowerState::Off);
195 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600196
197 return;
198}
199
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600200void Chassis::determineStatusOfPower()
201{
Andrew Geissler77a91832022-05-11 12:11:03 -0400202 auto initialPowerStatus = server::Chassis::currentPowerStatus();
203
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400204 bool powerGood = determineStatusOfUPSPower();
205 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000206 {
207 return;
208 }
209
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400210 powerGood = determineStatusOfPSUPower();
211 if (powerGood)
212 {
213 // All checks passed, set power status to good
214 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400215
216 // If power status transitioned from bad to good and chassis power is
217 // off then call Auto Power Restart to see if the system should auto
218 // power on now that power status is good
219 if ((initialPowerStatus != PowerStatus::Good) &&
220 (server::Chassis::currentPowerState() == PowerState::Off))
221 {
222 info("power status transitioned from {START_PWR_STATE} to Good and "
223 "chassis power is off, calling APR",
224 "START_PWR_STATE", initialPowerStatus);
225 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
226 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400227 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000228}
229
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400230bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000231{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600232 // Find all implementations of the UPower interface
233 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
234 MAPPER_INTERFACE, "GetSubTree");
235
236 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
237
238 std::map<std::string, std::map<std::string, std::vector<std::string>>>
239 mapperResponse;
240
241 try
242 {
243 auto mapperResponseMsg = bus.call(mapper);
244 mapperResponseMsg.read(mapperResponse);
245 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500246 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600247 {
248 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
249 throw;
250 }
251
252 if (mapperResponse.empty())
253 {
254 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600255 }
256
257 // Iterate through all returned Upower interfaces and look for UPS's
258 for (const auto& [path, services] : mapperResponse)
259 {
260 for (const auto& serviceIter : services)
261 {
262 const std::string& service = serviceIter.first;
263
264 try
265 {
266 auto method = bus.new_method_call(service.c_str(), path.c_str(),
267 PROPERTY_INTERFACE, "GetAll");
268 method.append(UPOWER_INTERFACE);
269
270 auto response = bus.call(method);
271 using Property = std::string;
272 using Value = std::variant<bool, uint>;
273 using PropertyMap = std::map<Property, Value>;
274 PropertyMap properties;
275 response.read(properties);
276
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600277 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
278 {
279 info("UPower device {OBJ_PATH} is not a UPS device",
280 "OBJ_PATH", path);
281 continue;
282 }
283
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600284 if (std::get<bool>(properties["IsPresent"]) != true)
285 {
286 // There is a UPS detected but it is not officially
287 // "present" yet. Monitor it for state change.
288 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
289 path);
290 continue;
291 }
292
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600293 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
294 {
295 info("UPS is fully charged");
296 }
297 else
298 {
299 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
300 std::get<uint>(properties["State"]));
301 server::Chassis::currentPowerStatus(
302 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400303 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600304 }
305
306 if (std::get<uint>(properties["BatteryLevel"]) ==
307 BATTERY_LVL_FULL)
308 {
309 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600310 // Only one UPS per system, we've found it and it's all
311 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400312 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600313 }
314 else
315 {
316 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
317 "UPS_BAT_LEVEL",
318 std::get<uint>(properties["BatteryLevel"]));
319 server::Chassis::currentPowerStatus(
320 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400321 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600322 }
323 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500324 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600325 {
326 error("Error reading UPS property, error: {ERROR}, "
327 "service: {SERVICE} path: {PATH}",
328 "ERROR", e, "SERVICE", service, "PATH", path);
329 throw;
330 }
331 }
332 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400333 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600334}
335
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400336bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000337{
338 // Find all implementations of the PowerSystemInputs interface
339 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
340 MAPPER_INTERFACE, "GetSubTree");
341
342 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
343
344 std::map<std::string, std::map<std::string, std::vector<std::string>>>
345 mapperResponse;
346
347 try
348 {
349 auto mapperResponseMsg = bus.call(mapper);
350 mapperResponseMsg.read(mapperResponse);
351 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500352 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000353 {
354 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
355 "ERROR", e);
356 throw;
357 }
358
359 for (const auto& [path, services] : mapperResponse)
360 {
361 for (const auto& serviceIter : services)
362 {
363 const std::string& service = serviceIter.first;
364
365 try
366 {
367 auto method = bus.new_method_call(service.c_str(), path.c_str(),
368 PROPERTY_INTERFACE, "GetAll");
369 method.append(POWERSYSINPUTS_INTERFACE);
370
371 auto response = bus.call(method);
372 using Property = std::string;
373 using Value = std::variant<std::string>;
374 using PropertyMap = std::map<Property, Value>;
375 PropertyMap properties;
376 response.read(properties);
377
378 auto statusStr = std::get<std::string>(properties["Status"]);
379 auto status =
380 decoratorServer::PowerSystemInputs::convertStatusFromString(
381 statusStr);
382
383 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
384 {
385 info("Power System Inputs status is in Fault state");
386 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400387 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000388 }
389 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500390 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000391 {
392 error(
393 "Error reading Power System Inputs property, error: {ERROR}, "
394 "service: {SERVICE} path: {PATH}",
395 "ERROR", e, "SERVICE", service, "PATH", path);
396 throw;
397 }
398 }
399 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400400 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000401}
402
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500403void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600404{
405 debug("UPS Property Change Event Triggered");
406 std::string statusInterface;
407 std::map<std::string, std::variant<uint, bool>> msgData;
408 msg.read(statusInterface, msgData);
409
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000410 // If the change is to any of the properties we are interested in, then call
411 // determineStatusOfPower(), which looks at all the power-related
412 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600413 auto propertyMap = msgData.find("IsPresent");
414 if (propertyMap != msgData.end())
415 {
416 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
417 std::get<bool>(propertyMap->second));
418 determineStatusOfPower();
419 return;
420 }
421
422 propertyMap = msgData.find("State");
423 if (propertyMap != msgData.end())
424 {
425 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
426 std::get<uint>(propertyMap->second));
427 determineStatusOfPower();
428 return;
429 }
430
431 propertyMap = msgData.find("BatteryLevel");
432 if (propertyMap != msgData.end())
433 {
434 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
435 std::get<uint>(propertyMap->second));
436 determineStatusOfPower();
437 return;
438 }
439 return;
440}
441
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500442void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000443{
444 debug("Power System Inputs Property Change Event Triggered");
445 std::string statusInterface;
446 std::map<std::string, std::variant<std::string>> msgData;
447 msg.read(statusInterface, msgData);
448
449 // If the change is to any of the properties we are interested in, then call
450 // determineStatusOfPower(), which looks at all the power-related
451 // interfaces, to see if a power status change is needed
452 auto propertyMap = msgData.find("Status");
453 if (propertyMap != msgData.end())
454 {
455 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
456 "POWER_SYS_INPUT_STATUS",
457 std::get<std::string>(propertyMap->second));
458 determineStatusOfPower();
459 return;
460 }
461 return;
462}
463
Matthew Barthbe6efab2022-03-01 13:21:45 -0600464void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600465{
Andrew Geissler58a18012018-01-19 19:36:05 -0800466 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
467 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600468
Matthew Barthbe6efab2022-03-01 13:21:45 -0600469 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600470 method.append("replace");
471
472 this->bus.call_noreply(method);
473
474 return;
475}
476
Andrew Geissler77a91832022-05-11 12:11:03 -0400477void Chassis::restartUnit(const std::string& sysdUnit)
478{
479 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
480 SYSTEMD_INTERFACE, "RestartUnit");
481
482 method.append(sysdUnit);
483 method.append("replace");
484
485 this->bus.call_noreply(method);
486
487 return;
488}
489
Josh D. King697474c2017-03-02 11:15:55 -0600490bool Chassis::stateActive(const std::string& target)
491{
Patrick Williams2975e262020-05-13 18:01:09 -0500492 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600493 sdbusplus::message::object_path unitTargetPath;
494
Andrew Geissler58a18012018-01-19 19:36:05 -0800495 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
496 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600497
498 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600499
William A. Kennington III09568ff2018-05-11 00:03:12 -0700500 try
501 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500502 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700503 result.read(unitTargetPath);
504 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500505 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700506 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500507 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700508 return false;
509 }
Josh D. King697474c2017-03-02 11:15:55 -0600510
Andrew Geissler58a18012018-01-19 19:36:05 -0800511 method = this->bus.new_method_call(
512 SYSTEMD_SERVICE,
513 static_cast<const std::string&>(unitTargetPath).c_str(),
514 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600515
516 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600517
Anthony Wilson32c532e2018-10-25 21:56:07 -0500518 try
Josh D. King697474c2017-03-02 11:15:55 -0600519 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500520 auto result = this->bus.call(method);
521 result.read(currentState);
522 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500523 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500524 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500525 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600526 return false;
527 }
528
Patrick Williams37413dc2020-05-13 11:29:54 -0500529 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800530 return currentStateStr == ACTIVE_STATE ||
531 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600532}
533
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500534int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600535{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600536 sdbusplus::message::object_path newStateObjPath;
537 std::string newStateUnit{};
538 std::string newStateResult{};
539
Andrew Geissler58a18012018-01-19 19:36:05 -0800540 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700541 try
542 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500543 // newStateID is a throwaway that is needed in order to read the
544 // parameters that are useful out of the dbus message
545 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700546 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
547 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500548 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700549 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500550 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
551 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700552 return 0;
553 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600554
Allen.Wangc0895622022-03-23 15:46:47 +0800555 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800556 (newStateResult == "done") &&
557 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600558 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500559 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600560 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500561 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600562 }
Potin Lai70f36d82022-03-15 10:25:39 +0800563 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800564 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800565 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800566 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500567 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800568 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500569 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600570
571 // Remove temporary file which is utilized for scenarios where the
572 // BMC is rebooted while the chassis power is still on.
573 // This file is used to indicate to chassis related systemd services
574 // that the chassis is already on and they should skip running.
575 // Once the chassis state is back to on we can clear this file.
576 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
577 size++; // null
578 std::unique_ptr<char[]> chassisFile(new char[size]);
579 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
580 if (std::filesystem::exists(chassisFile.get()))
581 {
582 std::filesystem::remove(chassisFile.get());
583 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800584 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600585
586 return 0;
587}
588
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600589Chassis::Transition Chassis::requestedPowerTransition(Transition value)
590{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500591 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
592 "REQ_POWER_TRAN", value);
Potin Lai70f36d82022-03-15 10:25:39 +0800593 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600594 return server::Chassis::requestedPowerTransition(value);
595}
596
597Chassis::PowerState Chassis::currentPowerState(PowerState value)
598{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500599 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500600 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
601 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500602
603 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530604 if (chassisPowerState == PowerState::On)
605 {
606 pohTimer.resetRemaining();
607 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500608 return chassisPowerState;
609}
610
Patrick Williams45a1ed72021-04-30 21:02:43 -0500611uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500612{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500613 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500614 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500615 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500616 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500617 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500618 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500619}
620
Patrick Williams45a1ed72021-04-30 21:02:43 -0500621void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700622{
623 if (ChassisInherit::currentPowerState() == PowerState::On)
624 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500625 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700626 }
627}
628
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500629void Chassis::restorePOHCounter()
630{
631 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800632 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500633 {
634 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500635 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500636 }
637 else
638 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500639 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500640 }
641}
642
Allen.Wangba182f02022-03-23 19:01:53 +0800643fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500644{
Allen.Wangba182f02022-03-23 19:01:53 +0800645 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500646 std::ofstream os(path.c_str(), std::ios::binary);
647 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500648 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500649 return path;
650}
651
Allen.Wangba182f02022-03-23 19:01:53 +0800652bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500653{
Allen.Wangba182f02022-03-23 19:01:53 +0800654 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500655 try
656 {
657 if (fs::exists(path))
658 {
659 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
660 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500661 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500662 return true;
663 }
664 return false;
665 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500666 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500667 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500668 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500669 fs::remove(path);
670 return false;
671 }
672 catch (const fs::filesystem_error& e)
673 {
674 return false;
675 }
676
677 return false;
678}
679
680void Chassis::startPOHCounter()
681{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500682 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
683 fs::create_directories(dir);
684
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500685 try
686 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700687 auto event = sdeventplus::Event::get_default();
688 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
689 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500690 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700691 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500692 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500693 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
694 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500695 phosphor::logging::commit<InternalFailure>();
696 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600697}
698
Matt Spinler9eab9862018-07-11 14:13:52 -0500699void Chassis::serializeStateChangeTime()
700{
Allen.Wangba182f02022-03-23 19:01:53 +0800701 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500702 std::ofstream os(path.c_str(), std::ios::binary);
703 cereal::JSONOutputArchive oarchive(os);
704
705 oarchive(ChassisInherit::lastStateChangeTime(),
706 ChassisInherit::currentPowerState());
707}
708
709bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
710{
Allen.Wangba182f02022-03-23 19:01:53 +0800711 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500712
713 try
714 {
715 if (fs::exists(path))
716 {
717 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
718 cereal::JSONInputArchive iarchive(is);
719 iarchive(time, state);
720 return true;
721 }
722 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500723 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500724 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500725 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500726 fs::remove(path);
727 }
728
729 return false;
730}
731
732void Chassis::restoreChassisStateChangeTime()
733{
734 uint64_t time;
735 PowerState state;
736
737 if (!deserializeStateChangeTime(time, state))
738 {
739 ChassisInherit::lastStateChangeTime(0);
740 }
741 else
742 {
743 ChassisInherit::lastStateChangeTime(time);
744 }
745}
746
747void Chassis::setStateChangeTime()
748{
749 using namespace std::chrono;
750 uint64_t lastTime;
751 PowerState lastState;
752
753 auto now =
754 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
755 .count();
756
757 // If power is on when the BMC is rebooted, this function will get called
758 // because sysStateChange() runs. Since the power state didn't change
759 // in this case, neither should the state change time, so check that
760 // the power state actually did change here.
761 if (deserializeStateChangeTime(lastTime, lastState))
762 {
763 if (lastState == ChassisInherit::currentPowerState())
764 {
765 return;
766 }
767 }
768
769 ChassisInherit::lastStateChangeTime(now);
770 serializeStateChangeTime();
771}
772
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500773bool Chassis::standbyVoltageRegulatorFault()
774{
775 bool regulatorFault = false;
776
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600777 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500778
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600779 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
780
781 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500782 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600783 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500784 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600785
786 if (1 == gpioval)
787 {
788 info("Detected standby voltage regulator fault");
789 regulatorFault = true;
790 }
791
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500792 return regulatorFault;
793}
794
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600795} // namespace manager
796} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700797} // namespace phosphor