blob: 6fa10eef9ebdc1bd7ce14269bb9541c55b2b711b [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
8#include <cereal/archives/json.hpp>
9#include <phosphor-logging/elog-errors.hpp>
10#include <phosphor-logging/log.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060011#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070012#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070013#include <sdeventplus/event.hpp>
14#include <sdeventplus/exception.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050015
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050016#include <experimental/filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050017#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050018
Andrew Geisslera90a31a2016-12-13 16:16:28 -060019namespace phosphor
20{
21namespace state
22{
23namespace manager
24{
25
26// When you see server:: you know we're referencing our base class
27namespace server = sdbusplus::xyz::openbmc_project::State::server;
28
29using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070030using sdbusplus::exception::SdBusError;
William A. Kennington IIId998f822018-10-17 23:17:57 -070031using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050032using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Josh D. King6838ea92017-04-11 13:39:18 -050033constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080034constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
35 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050036constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060037
Josh D. King697474c2017-03-02 11:15:55 -060038constexpr auto ACTIVE_STATE = "active";
39constexpr auto ACTIVATING_STATE = "activating";
40
Andrew Geisslerce80f242017-01-24 13:25:33 -060041/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080042const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
43 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050044 // Use the hard off target to ensure we shutdown immediately
45 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080046 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060047
Andrew Geissler58a18012018-01-19 19:36:05 -080048constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
49constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060050constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
51
Josh D. King697474c2017-03-02 11:15:55 -060052constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
53constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
54
Andrew Geissler0029a5d2017-01-24 14:48:35 -060055void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060056{
Andrew Geissler3a30b052019-05-14 15:54:37 -050057 try
58 {
59 auto method = this->bus.new_method_call(
60 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050061 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050062 }
63 catch (const sdbusplus::exception::SdBusError& ex)
64 {
65 log<level::ERR>("Failed to subscribe to systemd signals",
66 entry("ERR=%s", ex.what()));
67 elog<InternalFailure>();
68 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060069
Andrew Geissler0029a5d2017-01-24 14:48:35 -060070 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060071}
72
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060073// TODO - Will be rewritten once sdbusplus client bindings are in place
74// and persistent storage design is in place and sdbusplus
75// has read property function
76void Chassis::determineInitialState()
77{
Patrick Williams2975e262020-05-13 18:01:09 -050078 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080079 auto method = this->bus.new_method_call(
80 "org.openbmc.control.Power", "/org/openbmc/control/power0",
81 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060082
83 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070084 try
85 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070086 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -050087 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070088
Patrick Williams37413dc2020-05-13 11:29:54 -050089 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070090 {
91 log<level::INFO>("Initial Chassis State will be On",
92 entry("CHASSIS_CURRENT_POWER_STATE=%s",
93 convertForMessage(PowerState::On).c_str()));
94 server::Chassis::currentPowerState(PowerState::On);
95 server::Chassis::requestedPowerTransition(Transition::On);
96 return;
97 }
Matt Spinler9eab9862018-07-11 14:13:52 -050098 else
99 {
100 // The system is off. If we think it should be on then
101 // we probably lost AC while up, so set a new state
102 // change time.
103 uint64_t lastTime;
104 PowerState lastState;
105
106 if (deserializeStateChangeTime(lastTime, lastState))
107 {
108 if (lastState == PowerState::On)
109 {
Aatir Manzur27115ae2019-07-23 16:25:38 -0500110 report<Blackout>();
Matt Spinler9eab9862018-07-11 14:13:52 -0500111 setStateChangeTime();
112 }
113 }
114 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700115 }
116 catch (const SdBusError& e)
117 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700118 // It's acceptable for the pgood state service to not be available
119 // since it will notify us of the pgood state when it comes up.
120 if (e.name() != nullptr &&
121 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
122 {
123 goto fail;
124 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600125
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700126 // Only log for unexpected error types.
127 log<level::ERR>("Error performing call to get pgood",
128 entry("ERROR=%s", e.what()));
129 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600130 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700131
132fail:
133 log<level::INFO>("Initial Chassis State will be Off",
134 entry("CHASSIS_CURRENT_POWER_STATE=%s",
135 convertForMessage(PowerState::Off).c_str()));
136 server::Chassis::currentPowerState(PowerState::Off);
137 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600138
139 return;
140}
141
Andrew Geisslerce80f242017-01-24 13:25:33 -0600142void Chassis::executeTransition(Transition tranReq)
143{
144 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
145
Andrew Geissler58a18012018-01-19 19:36:05 -0800146 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
147 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600148
149 method.append(sysdTarget);
150 method.append("replace");
151
152 this->bus.call_noreply(method);
153
154 return;
155}
156
Josh D. King697474c2017-03-02 11:15:55 -0600157bool Chassis::stateActive(const std::string& target)
158{
Patrick Williams2975e262020-05-13 18:01:09 -0500159 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600160 sdbusplus::message::object_path unitTargetPath;
161
Andrew Geissler58a18012018-01-19 19:36:05 -0800162 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
163 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600164
165 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600166
William A. Kennington III09568ff2018-05-11 00:03:12 -0700167 try
168 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500169 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700170 result.read(unitTargetPath);
171 }
172 catch (const SdBusError& e)
173 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500174 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
William A. Kennington III09568ff2018-05-11 00:03:12 -0700175 return false;
176 }
Josh D. King697474c2017-03-02 11:15:55 -0600177
Andrew Geissler58a18012018-01-19 19:36:05 -0800178 method = this->bus.new_method_call(
179 SYSTEMD_SERVICE,
180 static_cast<const std::string&>(unitTargetPath).c_str(),
181 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600182
183 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600184
Anthony Wilson32c532e2018-10-25 21:56:07 -0500185 try
Josh D. King697474c2017-03-02 11:15:55 -0600186 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500187 auto result = this->bus.call(method);
188 result.read(currentState);
189 }
190 catch (const SdBusError& e)
191 {
192 log<level::ERR>("Error in ActiveState Get",
193 entry("ERROR=%s", e.what()));
Josh D. King697474c2017-03-02 11:15:55 -0600194 return false;
195 }
196
Patrick Williams37413dc2020-05-13 11:29:54 -0500197 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800198 return currentStateStr == ACTIVE_STATE ||
199 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600200}
201
Patrick Williams8f8ba392017-05-05 15:47:39 -0500202int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600203{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600204 sdbusplus::message::object_path newStateObjPath;
205 std::string newStateUnit{};
206 std::string newStateResult{};
207
Andrew Geissler58a18012018-01-19 19:36:05 -0800208 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700209 try
210 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500211 // newStateID is a throwaway that is needed in order to read the
212 // parameters that are useful out of the dbus message
213 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700214 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
215 }
216 catch (const SdBusError& e)
217 {
218 log<level::ERR>("Error in state change - bad encoding",
219 entry("ERROR=%s", e.what()),
220 entry("REPLY_SIG=%s", msg.get_signature()));
221 return 0;
222 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600223
Andrew Geissler58a18012018-01-19 19:36:05 -0800224 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
225 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600226 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930227 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600228 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500229 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600230 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800231 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
232 (newStateResult == "done") &&
233 (stateActive(CHASSIS_STATE_POWERON_TGT)))
234 {
235 log<level::INFO>("Received signal that power ON is complete");
236 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500237 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800238 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600239
240 return 0;
241}
242
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600243Chassis::Transition Chassis::requestedPowerTransition(Transition value)
244{
245
246 log<level::INFO>("Change to Chassis Requested Power State",
247 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
248 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600249 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600250 return server::Chassis::requestedPowerTransition(value);
251}
252
253Chassis::PowerState Chassis::currentPowerState(PowerState value)
254{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500255 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600256 log<level::INFO>("Change to Chassis Power State",
257 entry("CHASSIS_CURRENT_POWER_STATE=%s",
258 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500259
260 chassisPowerState = server::Chassis::currentPowerState(value);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700261 pOHTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500262 return chassisPowerState;
263}
264
265uint32_t Chassis::pOHCounter(uint32_t value)
266{
267 if (value != pOHCounter())
268 {
269 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500270 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500271 }
272 return pOHCounter();
273}
274
William A. Kennington IIId998f822018-10-17 23:17:57 -0700275void Chassis::pOHCallback()
276{
277 if (ChassisInherit::currentPowerState() == PowerState::On)
278 {
279 pOHCounter(pOHCounter() + 1);
280 }
281}
282
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500283void Chassis::restorePOHCounter()
284{
285 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500286 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500287 {
288 // set to default value
289 pOHCounter(0);
290 }
291 else
292 {
293 pOHCounter(counter);
294 }
295}
296
Matt Spinler81957842018-07-11 10:37:12 -0500297fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500298{
299 std::ofstream os(path.c_str(), std::ios::binary);
300 cereal::JSONOutputArchive oarchive(os);
301 oarchive(pOHCounter());
302 return path;
303}
304
Matt Spinler81957842018-07-11 10:37:12 -0500305bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500306{
307 try
308 {
309 if (fs::exists(path))
310 {
311 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
312 cereal::JSONInputArchive iarchive(is);
313 iarchive(pOHCounter);
314 return true;
315 }
316 return false;
317 }
318 catch (cereal::Exception& e)
319 {
320 log<level::ERR>(e.what());
321 fs::remove(path);
322 return false;
323 }
324 catch (const fs::filesystem_error& e)
325 {
326 return false;
327 }
328
329 return false;
330}
331
332void Chassis::startPOHCounter()
333{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500334 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
335 fs::create_directories(dir);
336
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500337 try
338 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700339 auto event = sdeventplus::Event::get_default();
340 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
341 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500342 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700343 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500344 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700345 log<level::ERR>("Error occurred during the sdeventplus loop",
346 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500347 phosphor::logging::commit<InternalFailure>();
348 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600349}
350
Matt Spinler9eab9862018-07-11 14:13:52 -0500351void Chassis::serializeStateChangeTime()
352{
353 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
354 std::ofstream os(path.c_str(), std::ios::binary);
355 cereal::JSONOutputArchive oarchive(os);
356
357 oarchive(ChassisInherit::lastStateChangeTime(),
358 ChassisInherit::currentPowerState());
359}
360
361bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
362{
363 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
364
365 try
366 {
367 if (fs::exists(path))
368 {
369 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
370 cereal::JSONInputArchive iarchive(is);
371 iarchive(time, state);
372 return true;
373 }
374 }
375 catch (std::exception& e)
376 {
377 log<level::ERR>(e.what());
378 fs::remove(path);
379 }
380
381 return false;
382}
383
384void Chassis::restoreChassisStateChangeTime()
385{
386 uint64_t time;
387 PowerState state;
388
389 if (!deserializeStateChangeTime(time, state))
390 {
391 ChassisInherit::lastStateChangeTime(0);
392 }
393 else
394 {
395 ChassisInherit::lastStateChangeTime(time);
396 }
397}
398
399void Chassis::setStateChangeTime()
400{
401 using namespace std::chrono;
402 uint64_t lastTime;
403 PowerState lastState;
404
405 auto now =
406 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
407 .count();
408
409 // If power is on when the BMC is rebooted, this function will get called
410 // because sysStateChange() runs. Since the power state didn't change
411 // in this case, neither should the state change time, so check that
412 // the power state actually did change here.
413 if (deserializeStateChangeTime(lastTime, lastState))
414 {
415 if (lastState == ChassisInherit::currentPowerState())
416 {
417 return;
418 }
419 }
420
421 ChassisInherit::lastStateChangeTime(now);
422 serializeStateChangeTime();
423}
424
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600425} // namespace manager
426} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700427} // namespace phosphor