blob: 38b7c943a8b3f4c736f53efd63fdf025d3500a9d [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
9#include <cereal/archives/json.hpp>
10#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050011#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060012#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070013#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070014#include <sdeventplus/event.hpp>
15#include <sdeventplus/exception.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050016
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060017#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050018#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050019
Andrew Geisslera90a31a2016-12-13 16:16:28 -060020namespace phosphor
21{
22namespace state
23{
24namespace manager
25{
26
Andrew Geissler8ffdb262021-09-20 15:25:19 -050027PHOSPHOR_LOG2_USING;
28
Andrew Geisslera90a31a2016-12-13 16:16:28 -060029// When you see server:: you know we're referencing our base class
30namespace server = sdbusplus::xyz::openbmc_project::State::server;
31
32using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070033using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050034using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050035using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Josh D. King6838ea92017-04-11 13:39:18 -050036constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080037constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
38 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050039constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060040
Josh D. King697474c2017-03-02 11:15:55 -060041constexpr auto ACTIVE_STATE = "active";
42constexpr auto ACTIVATING_STATE = "activating";
43
Andrew Geissler2cf2a262022-02-02 14:38:41 -060044// Details at https://upower.freedesktop.org/docs/Device.html
45constexpr uint TYPE_UPS = 3;
46constexpr uint STATE_FULLY_CHARGED = 4;
47constexpr uint BATTERY_LVL_FULL = 8;
48
Andrew Geisslerce80f242017-01-24 13:25:33 -060049/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080050const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
51 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050052 // Use the hard off target to ensure we shutdown immediately
53 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080054 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060055
Andrew Geissler58a18012018-01-19 19:36:05 -080056constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
57constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060058constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
59
Josh D. King697474c2017-03-02 11:15:55 -060060constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
61constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
62
Andrew Geissler8b1f8622022-01-28 16:37:07 -060063constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
64constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
65constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
66constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
67constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
68
Andrew Geissler0029a5d2017-01-24 14:48:35 -060069void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060070{
Andrew Geissler3a30b052019-05-14 15:54:37 -050071 try
72 {
73 auto method = this->bus.new_method_call(
74 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050075 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050076 }
Andrew Geissler8ffdb262021-09-20 15:25:19 -050077 catch (const sdbusplus::exception::exception& e)
Andrew Geissler3a30b052019-05-14 15:54:37 -050078 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050079 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler3a30b052019-05-14 15:54:37 -050080 elog<InternalFailure>();
81 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060082
Andrew Geissler0029a5d2017-01-24 14:48:35 -060083 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060084}
85
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060086// TODO - Will be rewritten once sdbusplus client bindings are in place
87// and persistent storage design is in place and sdbusplus
88// has read property function
89void Chassis::determineInitialState()
90{
Andrew Geissler2cf2a262022-02-02 14:38:41 -060091
92 // Monitor for any properties changed signals on UPower device path
93 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
94 bus,
95 sdbusplus::bus::match::rules::propertiesChangedNamespace(
96 "/org/freedesktop/UPower", UPOWER_INTERFACE),
97 [this](auto& msg) { this->uPowerChangeEvent(msg); });
98
Andrew Geissler8b1f8622022-01-28 16:37:07 -060099 determineStatusOfPower();
100
Patrick Williams2975e262020-05-13 18:01:09 -0500101 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800102 auto method = this->bus.new_method_call(
103 "org.openbmc.control.Power", "/org/openbmc/control/power0",
104 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600105
106 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700107 try
108 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700109 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500110 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700111
Patrick Williams37413dc2020-05-13 11:29:54 -0500112 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700113 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500114 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700115 server::Chassis::currentPowerState(PowerState::On);
116 server::Chassis::requestedPowerTransition(Transition::On);
117 return;
118 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500119 else
120 {
121 // The system is off. If we think it should be on then
122 // we probably lost AC while up, so set a new state
123 // change time.
124 uint64_t lastTime;
125 PowerState lastState;
126
127 if (deserializeStateChangeTime(lastTime, lastState))
128 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600129 // If power was on before the BMC reboot and the reboot reason
130 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500131 if (lastState == PowerState::On)
132 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600133 info(
134 "Chassis power was on before the BMC reboot and it is off now");
Matt Spinler9eab9862018-07-11 14:13:52 -0500135 setStateChangeTime();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600136
137 if (phosphor::state::manager::utils::getGpioValue(
138 "reset-cause-pinhole") != 1)
139 {
140 if (standbyVoltageRegulatorFault())
141 {
142 report<Regulator>();
143 }
144 else
145 {
146 report<Blackout>();
147 }
148 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500149 }
150 }
151 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700152 }
Patrick Williams0a675212021-09-02 09:49:43 -0500153 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700154 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700155 // It's acceptable for the pgood state service to not be available
156 // since it will notify us of the pgood state when it comes up.
157 if (e.name() != nullptr &&
158 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
159 {
160 goto fail;
161 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600162
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700163 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500164 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700165 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600166 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700167
168fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500169 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700170 server::Chassis::currentPowerState(PowerState::Off);
171 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600172
173 return;
174}
175
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600176void Chassis::determineStatusOfPower()
177{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600178 // Default PowerStatus to good
179 server::Chassis::currentPowerStatus(PowerStatus::Good);
180
181 // Find all implementations of the UPower interface
182 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
183 MAPPER_INTERFACE, "GetSubTree");
184
185 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
186
187 std::map<std::string, std::map<std::string, std::vector<std::string>>>
188 mapperResponse;
189
190 try
191 {
192 auto mapperResponseMsg = bus.call(mapper);
193 mapperResponseMsg.read(mapperResponse);
194 }
195 catch (const sdbusplus::exception::exception& e)
196 {
197 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
198 throw;
199 }
200
201 if (mapperResponse.empty())
202 {
203 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600204 }
205
206 // Iterate through all returned Upower interfaces and look for UPS's
207 for (const auto& [path, services] : mapperResponse)
208 {
209 for (const auto& serviceIter : services)
210 {
211 const std::string& service = serviceIter.first;
212
213 try
214 {
215 auto method = bus.new_method_call(service.c_str(), path.c_str(),
216 PROPERTY_INTERFACE, "GetAll");
217 method.append(UPOWER_INTERFACE);
218
219 auto response = bus.call(method);
220 using Property = std::string;
221 using Value = std::variant<bool, uint>;
222 using PropertyMap = std::map<Property, Value>;
223 PropertyMap properties;
224 response.read(properties);
225
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600226 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
227 {
228 info("UPower device {OBJ_PATH} is not a UPS device",
229 "OBJ_PATH", path);
230 continue;
231 }
232
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600233 if (std::get<bool>(properties["IsPresent"]) != true)
234 {
235 // There is a UPS detected but it is not officially
236 // "present" yet. Monitor it for state change.
237 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
238 path);
239 continue;
240 }
241
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600242 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
243 {
244 info("UPS is fully charged");
245 }
246 else
247 {
248 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
249 std::get<uint>(properties["State"]));
250 server::Chassis::currentPowerStatus(
251 PowerStatus::UninterruptiblePowerSupply);
252 return;
253 }
254
255 if (std::get<uint>(properties["BatteryLevel"]) ==
256 BATTERY_LVL_FULL)
257 {
258 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600259 // Only one UPS per system, we've found it and it's all
260 // good so exit function
261 return;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600262 }
263 else
264 {
265 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
266 "UPS_BAT_LEVEL",
267 std::get<uint>(properties["BatteryLevel"]));
268 server::Chassis::currentPowerStatus(
269 PowerStatus::UninterruptiblePowerSupply);
270 return;
271 }
272 }
273 catch (const sdbusplus::exception::exception& e)
274 {
275 error("Error reading UPS property, error: {ERROR}, "
276 "service: {SERVICE} path: {PATH}",
277 "ERROR", e, "SERVICE", service, "PATH", path);
278 throw;
279 }
280 }
281 }
282 return;
283}
284
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600285void Chassis::uPowerChangeEvent(sdbusplus::message::message& msg)
286{
287 debug("UPS Property Change Event Triggered");
288 std::string statusInterface;
289 std::map<std::string, std::variant<uint, bool>> msgData;
290 msg.read(statusInterface, msgData);
291
292 // If the change is to any of the three properties we are interested in
293 // then call determineStatusOfPower() to see if a power status change
294 // is needed
295 auto propertyMap = msgData.find("IsPresent");
296 if (propertyMap != msgData.end())
297 {
298 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
299 std::get<bool>(propertyMap->second));
300 determineStatusOfPower();
301 return;
302 }
303
304 propertyMap = msgData.find("State");
305 if (propertyMap != msgData.end())
306 {
307 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
308 std::get<uint>(propertyMap->second));
309 determineStatusOfPower();
310 return;
311 }
312
313 propertyMap = msgData.find("BatteryLevel");
314 if (propertyMap != msgData.end())
315 {
316 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
317 std::get<uint>(propertyMap->second));
318 determineStatusOfPower();
319 return;
320 }
321 return;
322}
323
Andrew Geisslerce80f242017-01-24 13:25:33 -0600324void Chassis::executeTransition(Transition tranReq)
325{
326 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
327
Andrew Geissler58a18012018-01-19 19:36:05 -0800328 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
329 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600330
331 method.append(sysdTarget);
332 method.append("replace");
333
334 this->bus.call_noreply(method);
335
336 return;
337}
338
Josh D. King697474c2017-03-02 11:15:55 -0600339bool Chassis::stateActive(const std::string& target)
340{
Patrick Williams2975e262020-05-13 18:01:09 -0500341 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600342 sdbusplus::message::object_path unitTargetPath;
343
Andrew Geissler58a18012018-01-19 19:36:05 -0800344 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
345 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600346
347 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600348
William A. Kennington III09568ff2018-05-11 00:03:12 -0700349 try
350 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500351 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700352 result.read(unitTargetPath);
353 }
Patrick Williams0a675212021-09-02 09:49:43 -0500354 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700355 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500356 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700357 return false;
358 }
Josh D. King697474c2017-03-02 11:15:55 -0600359
Andrew Geissler58a18012018-01-19 19:36:05 -0800360 method = this->bus.new_method_call(
361 SYSTEMD_SERVICE,
362 static_cast<const std::string&>(unitTargetPath).c_str(),
363 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600364
365 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600366
Anthony Wilson32c532e2018-10-25 21:56:07 -0500367 try
Josh D. King697474c2017-03-02 11:15:55 -0600368 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500369 auto result = this->bus.call(method);
370 result.read(currentState);
371 }
Patrick Williams0a675212021-09-02 09:49:43 -0500372 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500373 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500374 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600375 return false;
376 }
377
Patrick Williams37413dc2020-05-13 11:29:54 -0500378 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800379 return currentStateStr == ACTIVE_STATE ||
380 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600381}
382
Patrick Williams8f8ba392017-05-05 15:47:39 -0500383int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600384{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600385 sdbusplus::message::object_path newStateObjPath;
386 std::string newStateUnit{};
387 std::string newStateResult{};
388
Andrew Geissler58a18012018-01-19 19:36:05 -0800389 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700390 try
391 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500392 // newStateID is a throwaway that is needed in order to read the
393 // parameters that are useful out of the dbus message
394 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700395 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
396 }
Patrick Williams0a675212021-09-02 09:49:43 -0500397 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700398 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500399 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
400 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700401 return 0;
402 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600403
Andrew Geissler58a18012018-01-19 19:36:05 -0800404 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
405 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600406 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500407 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600408 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500409 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600410 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800411 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
412 (newStateResult == "done") &&
413 (stateActive(CHASSIS_STATE_POWERON_TGT)))
414 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500415 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800416 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500417 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600418
419 // Remove temporary file which is utilized for scenarios where the
420 // BMC is rebooted while the chassis power is still on.
421 // This file is used to indicate to chassis related systemd services
422 // that the chassis is already on and they should skip running.
423 // Once the chassis state is back to on we can clear this file.
424 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
425 size++; // null
426 std::unique_ptr<char[]> chassisFile(new char[size]);
427 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
428 if (std::filesystem::exists(chassisFile.get()))
429 {
430 std::filesystem::remove(chassisFile.get());
431 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800432 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600433
434 return 0;
435}
436
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600437Chassis::Transition Chassis::requestedPowerTransition(Transition value)
438{
439
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500440 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
441 "REQ_POWER_TRAN", value);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600442 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600443 return server::Chassis::requestedPowerTransition(value);
444}
445
446Chassis::PowerState Chassis::currentPowerState(PowerState value)
447{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500448 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500449 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
450 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500451
452 chassisPowerState = server::Chassis::currentPowerState(value);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500453 pohTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500454 return chassisPowerState;
455}
456
Patrick Williams45a1ed72021-04-30 21:02:43 -0500457uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500458{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500459 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500460 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500461 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500462 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500463 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500464 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500465}
466
Patrick Williams45a1ed72021-04-30 21:02:43 -0500467void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700468{
469 if (ChassisInherit::currentPowerState() == PowerState::On)
470 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500471 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700472 }
473}
474
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500475void Chassis::restorePOHCounter()
476{
477 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500478 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500479 {
480 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500481 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500482 }
483 else
484 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500485 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500486 }
487}
488
Matt Spinler81957842018-07-11 10:37:12 -0500489fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500490{
491 std::ofstream os(path.c_str(), std::ios::binary);
492 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500493 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500494 return path;
495}
496
Patrick Williams45a1ed72021-04-30 21:02:43 -0500497bool Chassis::deserializePOH(const fs::path& path, uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500498{
499 try
500 {
501 if (fs::exists(path))
502 {
503 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
504 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500505 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500506 return true;
507 }
508 return false;
509 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500510 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500511 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500512 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500513 fs::remove(path);
514 return false;
515 }
516 catch (const fs::filesystem_error& e)
517 {
518 return false;
519 }
520
521 return false;
522}
523
524void Chassis::startPOHCounter()
525{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500526 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
527 fs::create_directories(dir);
528
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500529 try
530 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700531 auto event = sdeventplus::Event::get_default();
532 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
533 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500534 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700535 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500536 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500537 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
538 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500539 phosphor::logging::commit<InternalFailure>();
540 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600541}
542
Matt Spinler9eab9862018-07-11 14:13:52 -0500543void Chassis::serializeStateChangeTime()
544{
545 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
546 std::ofstream os(path.c_str(), std::ios::binary);
547 cereal::JSONOutputArchive oarchive(os);
548
549 oarchive(ChassisInherit::lastStateChangeTime(),
550 ChassisInherit::currentPowerState());
551}
552
553bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
554{
555 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
556
557 try
558 {
559 if (fs::exists(path))
560 {
561 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
562 cereal::JSONInputArchive iarchive(is);
563 iarchive(time, state);
564 return true;
565 }
566 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500567 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500568 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500569 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500570 fs::remove(path);
571 }
572
573 return false;
574}
575
576void Chassis::restoreChassisStateChangeTime()
577{
578 uint64_t time;
579 PowerState state;
580
581 if (!deserializeStateChangeTime(time, state))
582 {
583 ChassisInherit::lastStateChangeTime(0);
584 }
585 else
586 {
587 ChassisInherit::lastStateChangeTime(time);
588 }
589}
590
591void Chassis::setStateChangeTime()
592{
593 using namespace std::chrono;
594 uint64_t lastTime;
595 PowerState lastState;
596
597 auto now =
598 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
599 .count();
600
601 // If power is on when the BMC is rebooted, this function will get called
602 // because sysStateChange() runs. Since the power state didn't change
603 // in this case, neither should the state change time, so check that
604 // the power state actually did change here.
605 if (deserializeStateChangeTime(lastTime, lastState))
606 {
607 if (lastState == ChassisInherit::currentPowerState())
608 {
609 return;
610 }
611 }
612
613 ChassisInherit::lastStateChangeTime(now);
614 serializeStateChangeTime();
615}
616
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500617bool Chassis::standbyVoltageRegulatorFault()
618{
619 bool regulatorFault = false;
620
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600621 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500622
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600623 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
624
625 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500626 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600627 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500628 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600629
630 if (1 == gpioval)
631 {
632 info("Detected standby voltage regulator fault");
633 regulatorFault = true;
634 }
635
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500636 return regulatorFault;
637}
638
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600639} // namespace manager
640} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700641} // namespace phosphor