blob: d970e58386d232cc32a3f364dda0428a7216197d [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
3#include "chassis_state_manager.hpp"
4
5#include "xyz/openbmc_project/Common/error.hpp"
6#include "xyz/openbmc_project/State/Shutdown/Power/error.hpp"
7
Matt Spinlerbbbc0162020-11-11 14:43:50 -06008#include <fmt/format.h>
9
Andrew Geisslere426b582020-05-28 12:40:55 -050010#include <cereal/archives/json.hpp>
11#include <phosphor-logging/elog-errors.hpp>
12#include <phosphor-logging/log.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060013#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070014#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070015#include <sdeventplus/event.hpp>
16#include <sdeventplus/exception.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050017
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050018#include <experimental/filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050019#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050020
Andrew Geisslera90a31a2016-12-13 16:16:28 -060021namespace phosphor
22{
23namespace state
24{
25namespace manager
26{
27
28// When you see server:: you know we're referencing our base class
29namespace server = sdbusplus::xyz::openbmc_project::State::server;
30
31using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070032using sdbusplus::exception::SdBusError;
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;
Josh D. King6838ea92017-04-11 13:39:18 -050035constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080036constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
37 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050038constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060039
Josh D. King697474c2017-03-02 11:15:55 -060040constexpr auto ACTIVE_STATE = "active";
41constexpr auto ACTIVATING_STATE = "activating";
42
Andrew Geisslerce80f242017-01-24 13:25:33 -060043/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080044const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
45 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050046 // Use the hard off target to ensure we shutdown immediately
47 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080048 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060049
Andrew Geissler58a18012018-01-19 19:36:05 -080050constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
51constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060052constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
53
Josh D. King697474c2017-03-02 11:15:55 -060054constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
55constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
56
Andrew Geissler0029a5d2017-01-24 14:48:35 -060057void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060058{
Andrew Geissler3a30b052019-05-14 15:54:37 -050059 try
60 {
61 auto method = this->bus.new_method_call(
62 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050063 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050064 }
65 catch (const sdbusplus::exception::SdBusError& ex)
66 {
67 log<level::ERR>("Failed to subscribe to systemd signals",
68 entry("ERR=%s", ex.what()));
69 elog<InternalFailure>();
70 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060071
Andrew Geissler0029a5d2017-01-24 14:48:35 -060072 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060073}
74
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060075// TODO - Will be rewritten once sdbusplus client bindings are in place
76// and persistent storage design is in place and sdbusplus
77// has read property function
78void Chassis::determineInitialState()
79{
Patrick Williams2975e262020-05-13 18:01:09 -050080 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080081 auto method = this->bus.new_method_call(
82 "org.openbmc.control.Power", "/org/openbmc/control/power0",
83 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060084
85 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070086 try
87 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070088 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -050089 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070090
Patrick Williams37413dc2020-05-13 11:29:54 -050091 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070092 {
93 log<level::INFO>("Initial Chassis State will be On",
94 entry("CHASSIS_CURRENT_POWER_STATE=%s",
95 convertForMessage(PowerState::On).c_str()));
96 server::Chassis::currentPowerState(PowerState::On);
97 server::Chassis::requestedPowerTransition(Transition::On);
98 return;
99 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500100 else
101 {
102 // The system is off. If we think it should be on then
103 // we probably lost AC while up, so set a new state
104 // change time.
105 uint64_t lastTime;
106 PowerState lastState;
107
108 if (deserializeStateChangeTime(lastTime, lastState))
109 {
110 if (lastState == PowerState::On)
111 {
Aatir Manzur27115ae2019-07-23 16:25:38 -0500112 report<Blackout>();
Matt Spinler9eab9862018-07-11 14:13:52 -0500113 setStateChangeTime();
114 }
115 }
116 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700117 }
118 catch (const SdBusError& e)
119 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700120 // It's acceptable for the pgood state service to not be available
121 // since it will notify us of the pgood state when it comes up.
122 if (e.name() != nullptr &&
123 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
124 {
125 goto fail;
126 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600127
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700128 // Only log for unexpected error types.
129 log<level::ERR>("Error performing call to get pgood",
130 entry("ERROR=%s", e.what()));
131 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600132 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700133
134fail:
135 log<level::INFO>("Initial Chassis State will be Off",
136 entry("CHASSIS_CURRENT_POWER_STATE=%s",
137 convertForMessage(PowerState::Off).c_str()));
138 server::Chassis::currentPowerState(PowerState::Off);
139 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600140
141 return;
142}
143
Andrew Geisslerce80f242017-01-24 13:25:33 -0600144void Chassis::executeTransition(Transition tranReq)
145{
146 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
147
Andrew Geissler58a18012018-01-19 19:36:05 -0800148 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
149 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600150
151 method.append(sysdTarget);
152 method.append("replace");
153
154 this->bus.call_noreply(method);
155
156 return;
157}
158
Josh D. King697474c2017-03-02 11:15:55 -0600159bool Chassis::stateActive(const std::string& target)
160{
Patrick Williams2975e262020-05-13 18:01:09 -0500161 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600162 sdbusplus::message::object_path unitTargetPath;
163
Andrew Geissler58a18012018-01-19 19:36:05 -0800164 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
165 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600166
167 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600168
William A. Kennington III09568ff2018-05-11 00:03:12 -0700169 try
170 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500171 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700172 result.read(unitTargetPath);
173 }
174 catch (const SdBusError& e)
175 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500176 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
William A. Kennington III09568ff2018-05-11 00:03:12 -0700177 return false;
178 }
Josh D. King697474c2017-03-02 11:15:55 -0600179
Andrew Geissler58a18012018-01-19 19:36:05 -0800180 method = this->bus.new_method_call(
181 SYSTEMD_SERVICE,
182 static_cast<const std::string&>(unitTargetPath).c_str(),
183 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600184
185 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600186
Anthony Wilson32c532e2018-10-25 21:56:07 -0500187 try
Josh D. King697474c2017-03-02 11:15:55 -0600188 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500189 auto result = this->bus.call(method);
190 result.read(currentState);
191 }
192 catch (const SdBusError& e)
193 {
194 log<level::ERR>("Error in ActiveState Get",
195 entry("ERROR=%s", e.what()));
Josh D. King697474c2017-03-02 11:15:55 -0600196 return false;
197 }
198
Patrick Williams37413dc2020-05-13 11:29:54 -0500199 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800200 return currentStateStr == ACTIVE_STATE ||
201 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600202}
203
Patrick Williams8f8ba392017-05-05 15:47:39 -0500204int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600205{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600206 sdbusplus::message::object_path newStateObjPath;
207 std::string newStateUnit{};
208 std::string newStateResult{};
209
Andrew Geissler58a18012018-01-19 19:36:05 -0800210 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700211 try
212 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500213 // newStateID is a throwaway that is needed in order to read the
214 // parameters that are useful out of the dbus message
215 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700216 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
217 }
218 catch (const SdBusError& e)
219 {
220 log<level::ERR>("Error in state change - bad encoding",
221 entry("ERROR=%s", e.what()),
222 entry("REPLY_SIG=%s", msg.get_signature()));
223 return 0;
224 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600225
Andrew Geissler58a18012018-01-19 19:36:05 -0800226 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
227 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600228 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930229 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600230 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500231 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600232 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800233 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
234 (newStateResult == "done") &&
235 (stateActive(CHASSIS_STATE_POWERON_TGT)))
236 {
237 log<level::INFO>("Received signal that power ON is complete");
238 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500239 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800240 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600241
242 return 0;
243}
244
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600245Chassis::Transition Chassis::requestedPowerTransition(Transition value)
246{
247
Matt Spinlerbbbc0162020-11-11 14:43:50 -0600248 log<level::INFO>(fmt::format("Change to Chassis Requested Power State: {}",
249 convertForMessage(value))
250 .c_str());
Andrew Geisslerce80f242017-01-24 13:25:33 -0600251 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600252 return server::Chassis::requestedPowerTransition(value);
253}
254
255Chassis::PowerState Chassis::currentPowerState(PowerState value)
256{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500257 PowerState chassisPowerState;
Matt Spinlerbbbc0162020-11-11 14:43:50 -0600258 log<level::INFO>(fmt::format("Change to Chassis Power State: {}",
259 convertForMessage(value))
260 .c_str());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500261
262 chassisPowerState = server::Chassis::currentPowerState(value);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700263 pOHTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500264 return chassisPowerState;
265}
266
267uint32_t Chassis::pOHCounter(uint32_t value)
268{
269 if (value != pOHCounter())
270 {
271 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500272 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500273 }
274 return pOHCounter();
275}
276
William A. Kennington IIId998f822018-10-17 23:17:57 -0700277void Chassis::pOHCallback()
278{
279 if (ChassisInherit::currentPowerState() == PowerState::On)
280 {
281 pOHCounter(pOHCounter() + 1);
282 }
283}
284
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500285void Chassis::restorePOHCounter()
286{
287 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500288 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500289 {
290 // set to default value
291 pOHCounter(0);
292 }
293 else
294 {
295 pOHCounter(counter);
296 }
297}
298
Matt Spinler81957842018-07-11 10:37:12 -0500299fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500300{
301 std::ofstream os(path.c_str(), std::ios::binary);
302 cereal::JSONOutputArchive oarchive(os);
303 oarchive(pOHCounter());
304 return path;
305}
306
Matt Spinler81957842018-07-11 10:37:12 -0500307bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500308{
309 try
310 {
311 if (fs::exists(path))
312 {
313 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
314 cereal::JSONInputArchive iarchive(is);
315 iarchive(pOHCounter);
316 return true;
317 }
318 return false;
319 }
320 catch (cereal::Exception& e)
321 {
322 log<level::ERR>(e.what());
323 fs::remove(path);
324 return false;
325 }
326 catch (const fs::filesystem_error& e)
327 {
328 return false;
329 }
330
331 return false;
332}
333
334void Chassis::startPOHCounter()
335{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500336 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
337 fs::create_directories(dir);
338
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500339 try
340 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700341 auto event = sdeventplus::Event::get_default();
342 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
343 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500344 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700345 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500346 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700347 log<level::ERR>("Error occurred during the sdeventplus loop",
348 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500349 phosphor::logging::commit<InternalFailure>();
350 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600351}
352
Matt Spinler9eab9862018-07-11 14:13:52 -0500353void Chassis::serializeStateChangeTime()
354{
355 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
356 std::ofstream os(path.c_str(), std::ios::binary);
357 cereal::JSONOutputArchive oarchive(os);
358
359 oarchive(ChassisInherit::lastStateChangeTime(),
360 ChassisInherit::currentPowerState());
361}
362
363bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
364{
365 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
366
367 try
368 {
369 if (fs::exists(path))
370 {
371 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
372 cereal::JSONInputArchive iarchive(is);
373 iarchive(time, state);
374 return true;
375 }
376 }
377 catch (std::exception& e)
378 {
379 log<level::ERR>(e.what());
380 fs::remove(path);
381 }
382
383 return false;
384}
385
386void Chassis::restoreChassisStateChangeTime()
387{
388 uint64_t time;
389 PowerState state;
390
391 if (!deserializeStateChangeTime(time, state))
392 {
393 ChassisInherit::lastStateChangeTime(0);
394 }
395 else
396 {
397 ChassisInherit::lastStateChangeTime(time);
398 }
399}
400
401void Chassis::setStateChangeTime()
402{
403 using namespace std::chrono;
404 uint64_t lastTime;
405 PowerState lastState;
406
407 auto now =
408 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
409 .count();
410
411 // If power is on when the BMC is rebooted, this function will get called
412 // because sysStateChange() runs. Since the power state didn't change
413 // in this case, neither should the state change time, so check that
414 // the power state actually did change here.
415 if (deserializeStateChangeTime(lastTime, lastState))
416 {
417 if (lastState == ChassisInherit::currentPowerState())
418 {
419 return;
420 }
421 }
422
423 ChassisInherit::lastStateChangeTime(now);
424 serializeStateChangeTime();
425}
426
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600427} // namespace manager
428} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700429} // namespace phosphor