blob: e71e4b57c3462fa671adbf0d1955d51cd13c7722 [file] [log] [blame]
Andrew Geissler0029a5d2017-01-24 14:48:35 -06001#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -07002#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -07003#include <sdeventplus/event.hpp>
4#include <sdeventplus/exception.hpp>
Saqib Khana8006a22017-02-14 11:37:08 -06005#include <phosphor-logging/log.hpp>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05006#include <phosphor-logging/elog-errors.hpp>
7#include "xyz/openbmc_project/Common/error.hpp"
Aatir Manzur27115ae2019-07-23 16:25:38 -05008#include "xyz/openbmc_project/State/Shutdown/Power/error.hpp"
Andrew Geisslera90a31a2016-12-13 16:16:28 -06009#include "chassis_state_manager.hpp"
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050010#include <cereal/archives/json.hpp>
11#include <fstream>
12#include "config.h"
13#include <experimental/filesystem>
14
Andrew Geisslera90a31a2016-12-13 16:16:28 -060015namespace phosphor
16{
17namespace state
18{
19namespace manager
20{
21
22// When you see server:: you know we're referencing our base class
23namespace server = sdbusplus::xyz::openbmc_project::State::server;
24
25using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070026using sdbusplus::exception::SdBusError;
William A. Kennington IIId998f822018-10-17 23:17:57 -070027using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050028using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Josh D. King6838ea92017-04-11 13:39:18 -050029constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080030constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
31 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050032constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060033
Josh D. King697474c2017-03-02 11:15:55 -060034constexpr auto ACTIVE_STATE = "active";
35constexpr auto ACTIVATING_STATE = "activating";
36
Andrew Geisslerce80f242017-01-24 13:25:33 -060037/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080038const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
39 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050040 // Use the hard off target to ensure we shutdown immediately
41 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080042 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060043
Andrew Geissler58a18012018-01-19 19:36:05 -080044constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
45constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060046constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
47
Josh D. King697474c2017-03-02 11:15:55 -060048constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
49constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
50
Andrew Geissler0029a5d2017-01-24 14:48:35 -060051void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060052{
Andrew Geissler3a30b052019-05-14 15:54:37 -050053 try
54 {
55 auto method = this->bus.new_method_call(
56 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050057 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050058 }
59 catch (const sdbusplus::exception::SdBusError& ex)
60 {
61 log<level::ERR>("Failed to subscribe to systemd signals",
62 entry("ERR=%s", ex.what()));
63 elog<InternalFailure>();
64 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060065
Andrew Geissler0029a5d2017-01-24 14:48:35 -060066 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060067}
68
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060069// TODO - Will be rewritten once sdbusplus client bindings are in place
70// and persistent storage design is in place and sdbusplus
71// has read property function
72void Chassis::determineInitialState()
73{
74 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080075 auto method = this->bus.new_method_call(
76 "org.openbmc.control.Power", "/org/openbmc/control/power0",
77 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060078
79 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070080 try
81 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070082 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -050083 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070084
William A. Kennington III7a0689a2018-11-12 17:19:33 -080085 if (sdbusplus::message::variant_ns::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070086 {
87 log<level::INFO>("Initial Chassis State will be On",
88 entry("CHASSIS_CURRENT_POWER_STATE=%s",
89 convertForMessage(PowerState::On).c_str()));
90 server::Chassis::currentPowerState(PowerState::On);
91 server::Chassis::requestedPowerTransition(Transition::On);
92 return;
93 }
Matt Spinler9eab9862018-07-11 14:13:52 -050094 else
95 {
96 // The system is off. If we think it should be on then
97 // we probably lost AC while up, so set a new state
98 // change time.
99 uint64_t lastTime;
100 PowerState lastState;
101
102 if (deserializeStateChangeTime(lastTime, lastState))
103 {
104 if (lastState == PowerState::On)
105 {
Aatir Manzur27115ae2019-07-23 16:25:38 -0500106 report<Blackout>();
Matt Spinler9eab9862018-07-11 14:13:52 -0500107 setStateChangeTime();
108 }
109 }
110 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700111 }
112 catch (const SdBusError& e)
113 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700114 // It's acceptable for the pgood state service to not be available
115 // since it will notify us of the pgood state when it comes up.
116 if (e.name() != nullptr &&
117 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
118 {
119 goto fail;
120 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600121
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700122 // Only log for unexpected error types.
123 log<level::ERR>("Error performing call to get pgood",
124 entry("ERROR=%s", e.what()));
125 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600126 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700127
128fail:
129 log<level::INFO>("Initial Chassis State will be Off",
130 entry("CHASSIS_CURRENT_POWER_STATE=%s",
131 convertForMessage(PowerState::Off).c_str()));
132 server::Chassis::currentPowerState(PowerState::Off);
133 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600134
135 return;
136}
137
Andrew Geisslerce80f242017-01-24 13:25:33 -0600138void Chassis::executeTransition(Transition tranReq)
139{
140 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
141
Andrew Geissler58a18012018-01-19 19:36:05 -0800142 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
143 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600144
145 method.append(sysdTarget);
146 method.append("replace");
147
148 this->bus.call_noreply(method);
149
150 return;
151}
152
Josh D. King697474c2017-03-02 11:15:55 -0600153bool Chassis::stateActive(const std::string& target)
154{
155 sdbusplus::message::variant<std::string> currentState;
156 sdbusplus::message::object_path unitTargetPath;
157
Andrew Geissler58a18012018-01-19 19:36:05 -0800158 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
159 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600160
161 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600162
William A. Kennington III09568ff2018-05-11 00:03:12 -0700163 try
164 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500165 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700166 result.read(unitTargetPath);
167 }
168 catch (const SdBusError& e)
169 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500170 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
William A. Kennington III09568ff2018-05-11 00:03:12 -0700171 return false;
172 }
Josh D. King697474c2017-03-02 11:15:55 -0600173
Andrew Geissler58a18012018-01-19 19:36:05 -0800174 method = this->bus.new_method_call(
175 SYSTEMD_SERVICE,
176 static_cast<const std::string&>(unitTargetPath).c_str(),
177 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600178
179 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600180
Anthony Wilson32c532e2018-10-25 21:56:07 -0500181 try
Josh D. King697474c2017-03-02 11:15:55 -0600182 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500183 auto result = this->bus.call(method);
184 result.read(currentState);
185 }
186 catch (const SdBusError& e)
187 {
188 log<level::ERR>("Error in ActiveState Get",
189 entry("ERROR=%s", e.what()));
Josh D. King697474c2017-03-02 11:15:55 -0600190 return false;
191 }
192
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800193 const auto& currentStateStr =
194 sdbusplus::message::variant_ns::get<std::string>(currentState);
195 return currentStateStr == ACTIVE_STATE ||
196 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600197}
198
Patrick Williams8f8ba392017-05-05 15:47:39 -0500199int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600200{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600201 sdbusplus::message::object_path newStateObjPath;
202 std::string newStateUnit{};
203 std::string newStateResult{};
204
Andrew Geissler58a18012018-01-19 19:36:05 -0800205 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700206 try
207 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500208 // newStateID is a throwaway that is needed in order to read the
209 // parameters that are useful out of the dbus message
210 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700211 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
212 }
213 catch (const SdBusError& e)
214 {
215 log<level::ERR>("Error in state change - bad encoding",
216 entry("ERROR=%s", e.what()),
217 entry("REPLY_SIG=%s", msg.get_signature()));
218 return 0;
219 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600220
Andrew Geissler58a18012018-01-19 19:36:05 -0800221 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
222 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600223 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930224 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600225 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500226 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600227 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800228 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
229 (newStateResult == "done") &&
230 (stateActive(CHASSIS_STATE_POWERON_TGT)))
231 {
232 log<level::INFO>("Received signal that power ON is complete");
233 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500234 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800235 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600236
237 return 0;
238}
239
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600240Chassis::Transition Chassis::requestedPowerTransition(Transition value)
241{
242
243 log<level::INFO>("Change to Chassis Requested Power State",
244 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
245 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600246 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600247 return server::Chassis::requestedPowerTransition(value);
248}
249
250Chassis::PowerState Chassis::currentPowerState(PowerState value)
251{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500252 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600253 log<level::INFO>("Change to Chassis Power State",
254 entry("CHASSIS_CURRENT_POWER_STATE=%s",
255 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500256
257 chassisPowerState = server::Chassis::currentPowerState(value);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700258 pOHTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500259 return chassisPowerState;
260}
261
262uint32_t Chassis::pOHCounter(uint32_t value)
263{
264 if (value != pOHCounter())
265 {
266 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500267 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500268 }
269 return pOHCounter();
270}
271
William A. Kennington IIId998f822018-10-17 23:17:57 -0700272void Chassis::pOHCallback()
273{
274 if (ChassisInherit::currentPowerState() == PowerState::On)
275 {
276 pOHCounter(pOHCounter() + 1);
277 }
278}
279
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500280void Chassis::restorePOHCounter()
281{
282 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500283 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500284 {
285 // set to default value
286 pOHCounter(0);
287 }
288 else
289 {
290 pOHCounter(counter);
291 }
292}
293
Matt Spinler81957842018-07-11 10:37:12 -0500294fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500295{
296 std::ofstream os(path.c_str(), std::ios::binary);
297 cereal::JSONOutputArchive oarchive(os);
298 oarchive(pOHCounter());
299 return path;
300}
301
Matt Spinler81957842018-07-11 10:37:12 -0500302bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500303{
304 try
305 {
306 if (fs::exists(path))
307 {
308 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
309 cereal::JSONInputArchive iarchive(is);
310 iarchive(pOHCounter);
311 return true;
312 }
313 return false;
314 }
315 catch (cereal::Exception& e)
316 {
317 log<level::ERR>(e.what());
318 fs::remove(path);
319 return false;
320 }
321 catch (const fs::filesystem_error& e)
322 {
323 return false;
324 }
325
326 return false;
327}
328
329void Chassis::startPOHCounter()
330{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500331 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
332 fs::create_directories(dir);
333
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500334 try
335 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700336 auto event = sdeventplus::Event::get_default();
337 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
338 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500339 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700340 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500341 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700342 log<level::ERR>("Error occurred during the sdeventplus loop",
343 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500344 phosphor::logging::commit<InternalFailure>();
345 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600346}
347
Matt Spinler9eab9862018-07-11 14:13:52 -0500348void Chassis::serializeStateChangeTime()
349{
350 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
351 std::ofstream os(path.c_str(), std::ios::binary);
352 cereal::JSONOutputArchive oarchive(os);
353
354 oarchive(ChassisInherit::lastStateChangeTime(),
355 ChassisInherit::currentPowerState());
356}
357
358bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
359{
360 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
361
362 try
363 {
364 if (fs::exists(path))
365 {
366 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
367 cereal::JSONInputArchive iarchive(is);
368 iarchive(time, state);
369 return true;
370 }
371 }
372 catch (std::exception& e)
373 {
374 log<level::ERR>(e.what());
375 fs::remove(path);
376 }
377
378 return false;
379}
380
381void Chassis::restoreChassisStateChangeTime()
382{
383 uint64_t time;
384 PowerState state;
385
386 if (!deserializeStateChangeTime(time, state))
387 {
388 ChassisInherit::lastStateChangeTime(0);
389 }
390 else
391 {
392 ChassisInherit::lastStateChangeTime(time);
393 }
394}
395
396void Chassis::setStateChangeTime()
397{
398 using namespace std::chrono;
399 uint64_t lastTime;
400 PowerState lastState;
401
402 auto now =
403 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
404 .count();
405
406 // If power is on when the BMC is rebooted, this function will get called
407 // because sysStateChange() runs. Since the power state didn't change
408 // in this case, neither should the state change time, so check that
409 // the power state actually did change here.
410 if (deserializeStateChangeTime(lastTime, lastState))
411 {
412 if (lastState == ChassisInherit::currentPowerState())
413 {
414 return;
415 }
416 }
417
418 ChassisInherit::lastStateChangeTime(now);
419 serializeStateChangeTime();
420}
421
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600422} // namespace manager
423} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700424} // namespace phosphor