blob: 4e8e1e7adb4314fd85ebf7bfd20b56df6649bb41 [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>
Saqib Khana8006a22017-02-14 11:37:08 -06003#include <phosphor-logging/log.hpp>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05004#include <phosphor-logging/elog-errors.hpp>
5#include "xyz/openbmc_project/Common/error.hpp"
Andrew Geisslera90a31a2016-12-13 16:16:28 -06006#include "chassis_state_manager.hpp"
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05007#include <cereal/archives/json.hpp>
8#include <fstream>
9#include "config.h"
10#include <experimental/filesystem>
11
Andrew Geisslera90a31a2016-12-13 16:16:28 -060012namespace phosphor
13{
14namespace state
15{
16namespace manager
17{
18
19// When you see server:: you know we're referencing our base class
20namespace server = sdbusplus::xyz::openbmc_project::State::server;
21
22using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070023using sdbusplus::exception::SdBusError;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060024
Josh D. King6838ea92017-04-11 13:39:18 -050025constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080026constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
27 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050028constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060029
Josh D. King697474c2017-03-02 11:15:55 -060030constexpr auto ACTIVE_STATE = "active";
31constexpr auto ACTIVATING_STATE = "activating";
32
Andrew Geisslerce80f242017-01-24 13:25:33 -060033/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080034const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
35 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050036 // Use the hard off target to ensure we shutdown immediately
37 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080038 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060039
Andrew Geissler58a18012018-01-19 19:36:05 -080040constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
41constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060042constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
43
Josh D. King697474c2017-03-02 11:15:55 -060044constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
45constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
46
Andrew Geissler0029a5d2017-01-24 14:48:35 -060047void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060048{
Andrew Geissler58a18012018-01-19 19:36:05 -080049 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
50 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler0029a5d2017-01-24 14:48:35 -060051 this->bus.call_noreply(method);
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060052
Andrew Geissler0029a5d2017-01-24 14:48:35 -060053 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060054}
55
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060056// TODO - Will be rewritten once sdbusplus client bindings are in place
57// and persistent storage design is in place and sdbusplus
58// has read property function
59void Chassis::determineInitialState()
60{
61 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080062 auto method = this->bus.new_method_call(
63 "org.openbmc.control.Power", "/org/openbmc/control/power0",
64 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060065
66 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070067 try
68 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070069 auto reply = this->bus.call(method);
70 if (reply.is_method_error())
71 {
72 log<level::ERR>(
73 "Error in response message - could not get initial pgood");
74 goto fail;
75 }
76
77 try
78 {
79 reply.read(pgood);
80 }
81 catch (const SdBusError& e)
82 {
83 log<level::ERR>("Error in bus response - bad encoding of pgood",
84 entry("ERROR=%s", e.what()),
85 entry("REPLY_SIG=%s", reply.get_signature()));
86 goto fail;
87 }
88
89 if (pgood == 1)
90 {
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 {
110 setStateChangeTime();
111 }
112 }
113 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700114 }
115 catch (const SdBusError& e)
116 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700117 // It's acceptable for the pgood state service to not be available
118 // since it will notify us of the pgood state when it comes up.
119 if (e.name() != nullptr &&
120 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
121 {
122 goto fail;
123 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600124
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700125 // Only log for unexpected error types.
126 log<level::ERR>("Error performing call to get pgood",
127 entry("ERROR=%s", e.what()));
128 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600129 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700130
131fail:
132 log<level::INFO>("Initial Chassis State will be Off",
133 entry("CHASSIS_CURRENT_POWER_STATE=%s",
134 convertForMessage(PowerState::Off).c_str()));
135 server::Chassis::currentPowerState(PowerState::Off);
136 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600137
138 return;
139}
140
Andrew Geisslerce80f242017-01-24 13:25:33 -0600141void Chassis::executeTransition(Transition tranReq)
142{
143 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
144
Andrew Geissler58a18012018-01-19 19:36:05 -0800145 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
146 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600147
148 method.append(sysdTarget);
149 method.append("replace");
150
151 this->bus.call_noreply(method);
152
153 return;
154}
155
Josh D. King697474c2017-03-02 11:15:55 -0600156bool Chassis::stateActive(const std::string& target)
157{
158 sdbusplus::message::variant<std::string> currentState;
159 sdbusplus::message::object_path unitTargetPath;
160
Andrew Geissler58a18012018-01-19 19:36:05 -0800161 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
162 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600163
164 method.append(target);
165 auto result = this->bus.call(method);
166
Andrew Geissler58a18012018-01-19 19:36:05 -0800167 // Check that the bus call didn't result in an error
168 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600169 {
170 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
171 entry(" %s", SYSTEMD_INTERFACE));
172 return false;
173 }
174
William A. Kennington III09568ff2018-05-11 00:03:12 -0700175 try
176 {
177 result.read(unitTargetPath);
178 }
179 catch (const SdBusError& e)
180 {
181 log<level::ERR>("Error in bus response - bad encoding for GetUnit",
182 entry("ERROR=%s", e.what()),
183 entry("REPLY_SIG=%s", result.get_signature()));
184 return false;
185 }
Josh D. King697474c2017-03-02 11:15:55 -0600186
Andrew Geissler58a18012018-01-19 19:36:05 -0800187 method = this->bus.new_method_call(
188 SYSTEMD_SERVICE,
189 static_cast<const std::string&>(unitTargetPath).c_str(),
190 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600191
192 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
193 result = this->bus.call(method);
194
Andrew Geissler58a18012018-01-19 19:36:05 -0800195 // Check that the bus call didn't result in an error
196 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600197 {
198 log<level::ERR>("Error in bus call - could not resolve Get for:",
199 entry(" %s", SYSTEMD_PROPERTY_IFACE));
200 return false;
201 }
202
203 result.read(currentState);
204
Andrew Geissler58a18012018-01-19 19:36:05 -0800205 if (currentState != ACTIVE_STATE && currentState != ACTIVATING_STATE)
Josh D. King697474c2017-03-02 11:15:55 -0600206 {
Andrew Geissler58a18012018-01-19 19:36:05 -0800207 // False - not active
Josh D. King697474c2017-03-02 11:15:55 -0600208 return false;
209 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800210 // True - active
Josh D. King697474c2017-03-02 11:15:55 -0600211 return true;
Josh D. King697474c2017-03-02 11:15:55 -0600212}
213
Patrick Williams8f8ba392017-05-05 15:47:39 -0500214int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600215{
Andrew Geissler58a18012018-01-19 19:36:05 -0800216 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600217 sdbusplus::message::object_path newStateObjPath;
218 std::string newStateUnit{};
219 std::string newStateResult{};
220
Andrew Geissler58a18012018-01-19 19:36:05 -0800221 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700222 try
223 {
224 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
225 }
226 catch (const SdBusError& e)
227 {
228 log<level::ERR>("Error in state change - bad encoding",
229 entry("ERROR=%s", e.what()),
230 entry("REPLY_SIG=%s", msg.get_signature()));
231 return 0;
232 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600233
Andrew Geissler58a18012018-01-19 19:36:05 -0800234 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
235 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600236 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930237 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600238 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500239 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600240 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800241 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
242 (newStateResult == "done") &&
243 (stateActive(CHASSIS_STATE_POWERON_TGT)))
244 {
245 log<level::INFO>("Received signal that power ON is complete");
246 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500247 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800248 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600249
250 return 0;
251}
252
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600253Chassis::Transition Chassis::requestedPowerTransition(Transition value)
254{
255
256 log<level::INFO>("Change to Chassis Requested Power State",
257 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
258 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600259 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600260 return server::Chassis::requestedPowerTransition(value);
261}
262
263Chassis::PowerState Chassis::currentPowerState(PowerState value)
264{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500265 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600266 log<level::INFO>("Change to Chassis Power State",
267 entry("CHASSIS_CURRENT_POWER_STATE=%s",
268 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500269
270 chassisPowerState = server::Chassis::currentPowerState(value);
271 if (chassisPowerState == PowerState::On)
272 {
273 timer->state(timer::ON);
274 }
275 else
276 {
277 timer->state(timer::OFF);
278 }
279 return chassisPowerState;
280}
281
282uint32_t Chassis::pOHCounter(uint32_t value)
283{
284 if (value != pOHCounter())
285 {
286 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500287 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500288 }
289 return pOHCounter();
290}
291
292void Chassis::restorePOHCounter()
293{
294 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500295 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500296 {
297 // set to default value
298 pOHCounter(0);
299 }
300 else
301 {
302 pOHCounter(counter);
303 }
304}
305
Matt Spinler81957842018-07-11 10:37:12 -0500306fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500307{
308 std::ofstream os(path.c_str(), std::ios::binary);
309 cereal::JSONOutputArchive oarchive(os);
310 oarchive(pOHCounter());
311 return path;
312}
313
Matt Spinler81957842018-07-11 10:37:12 -0500314bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500315{
316 try
317 {
318 if (fs::exists(path))
319 {
320 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
321 cereal::JSONInputArchive iarchive(is);
322 iarchive(pOHCounter);
323 return true;
324 }
325 return false;
326 }
327 catch (cereal::Exception& e)
328 {
329 log<level::ERR>(e.what());
330 fs::remove(path);
331 return false;
332 }
333 catch (const fs::filesystem_error& e)
334 {
335 return false;
336 }
337
338 return false;
339}
340
341void Chassis::startPOHCounter()
342{
343 using namespace std::chrono_literals;
344 using namespace phosphor::logging;
345 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
346
347 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
348 fs::create_directories(dir);
349
350 sd_event* event = nullptr;
351 auto r = sd_event_default(&event);
352 if (r < 0)
353 {
354 log<level::ERR>("Error creating a default sd_event handler");
355 throw;
356 }
357
358 phosphor::state::manager::EventPtr eventP{event};
359 event = nullptr;
360
361 auto callback = [&]() {
362 if (ChassisInherit::currentPowerState() == PowerState::On)
363 {
364 pOHCounter(pOHCounter() + 1);
365 }
366 };
367
368 try
369 {
370 timer = std::make_unique<phosphor::state::manager::Timer>(
371 eventP, callback, std::chrono::seconds(POH::hour),
372 phosphor::state::manager::timer::ON);
373 bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
374 r = sd_event_loop(eventP.get());
375 if (r < 0)
376 {
377 log<level::ERR>("Error occurred during the sd_event_loop",
378 entry("RC=%d", r));
379 elog<InternalFailure>();
380 }
381 }
382 catch (InternalFailure& e)
383 {
384 phosphor::logging::commit<InternalFailure>();
385 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600386}
387
Matt Spinler9eab9862018-07-11 14:13:52 -0500388void Chassis::serializeStateChangeTime()
389{
390 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
391 std::ofstream os(path.c_str(), std::ios::binary);
392 cereal::JSONOutputArchive oarchive(os);
393
394 oarchive(ChassisInherit::lastStateChangeTime(),
395 ChassisInherit::currentPowerState());
396}
397
398bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
399{
400 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
401
402 try
403 {
404 if (fs::exists(path))
405 {
406 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
407 cereal::JSONInputArchive iarchive(is);
408 iarchive(time, state);
409 return true;
410 }
411 }
412 catch (std::exception& e)
413 {
414 log<level::ERR>(e.what());
415 fs::remove(path);
416 }
417
418 return false;
419}
420
421void Chassis::restoreChassisStateChangeTime()
422{
423 uint64_t time;
424 PowerState state;
425
426 if (!deserializeStateChangeTime(time, state))
427 {
428 ChassisInherit::lastStateChangeTime(0);
429 }
430 else
431 {
432 ChassisInherit::lastStateChangeTime(time);
433 }
434}
435
436void Chassis::setStateChangeTime()
437{
438 using namespace std::chrono;
439 uint64_t lastTime;
440 PowerState lastState;
441
442 auto now =
443 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
444 .count();
445
446 // If power is on when the BMC is rebooted, this function will get called
447 // because sysStateChange() runs. Since the power state didn't change
448 // in this case, neither should the state change time, so check that
449 // the power state actually did change here.
450 if (deserializeStateChangeTime(lastTime, lastState))
451 {
452 if (lastState == ChassisInherit::currentPowerState())
453 {
454 return;
455 }
456 }
457
458 ChassisInherit::lastStateChangeTime(now);
459 serializeStateChangeTime();
460}
461
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600462} // namespace manager
463} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700464} // namespace phosphor