blob: 16cecec0f00e96a72863d99a0d444a3d56d8d0e3 [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"
Andrew Geisslera90a31a2016-12-13 16:16:28 -06008#include "chassis_state_manager.hpp"
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -05009#include <cereal/archives/json.hpp>
10#include <fstream>
11#include "config.h"
12#include <experimental/filesystem>
13
Andrew Geisslera90a31a2016-12-13 16:16:28 -060014namespace phosphor
15{
16namespace state
17{
18namespace manager
19{
20
21// When you see server:: you know we're referencing our base class
22namespace server = sdbusplus::xyz::openbmc_project::State::server;
23
24using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070025using sdbusplus::exception::SdBusError;
William A. Kennington IIId998f822018-10-17 23:17:57 -070026using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060027
Josh D. King6838ea92017-04-11 13:39:18 -050028constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080029constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
30 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050031constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060032
Josh D. King697474c2017-03-02 11:15:55 -060033constexpr auto ACTIVE_STATE = "active";
34constexpr auto ACTIVATING_STATE = "activating";
35
Andrew Geisslerce80f242017-01-24 13:25:33 -060036/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080037const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
38 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050039 // Use the hard off target to ensure we shutdown immediately
40 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080041 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060042
Andrew Geissler58a18012018-01-19 19:36:05 -080043constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
44constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060045constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
46
Josh D. King697474c2017-03-02 11:15:55 -060047constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
48constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
49
Andrew Geissler0029a5d2017-01-24 14:48:35 -060050void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060051{
Andrew Geissler58a18012018-01-19 19:36:05 -080052 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
53 SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler0029a5d2017-01-24 14:48:35 -060054 this->bus.call_noreply(method);
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060055
Andrew Geissler0029a5d2017-01-24 14:48:35 -060056 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060057}
58
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060059// TODO - Will be rewritten once sdbusplus client bindings are in place
60// and persistent storage design is in place and sdbusplus
61// has read property function
62void Chassis::determineInitialState()
63{
64 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080065 auto method = this->bus.new_method_call(
66 "org.openbmc.control.Power", "/org/openbmc/control/power0",
67 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060068
69 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070070 try
71 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070072 auto reply = this->bus.call(method);
73 if (reply.is_method_error())
74 {
75 log<level::ERR>(
76 "Error in response message - could not get initial pgood");
77 goto fail;
78 }
79
80 try
81 {
82 reply.read(pgood);
83 }
84 catch (const SdBusError& e)
85 {
86 log<level::ERR>("Error in bus response - bad encoding of pgood",
87 entry("ERROR=%s", e.what()),
88 entry("REPLY_SIG=%s", reply.get_signature()));
89 goto fail;
90 }
91
William A. Kennington III7a0689a2018-11-12 17:19:33 -080092 if (sdbusplus::message::variant_ns::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070093 {
94 log<level::INFO>("Initial Chassis State will be On",
95 entry("CHASSIS_CURRENT_POWER_STATE=%s",
96 convertForMessage(PowerState::On).c_str()));
97 server::Chassis::currentPowerState(PowerState::On);
98 server::Chassis::requestedPowerTransition(Transition::On);
99 return;
100 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500101 else
102 {
103 // The system is off. If we think it should be on then
104 // we probably lost AC while up, so set a new state
105 // change time.
106 uint64_t lastTime;
107 PowerState lastState;
108
109 if (deserializeStateChangeTime(lastTime, lastState))
110 {
111 if (lastState == PowerState::On)
112 {
113 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{
161 sdbusplus::message::variant<std::string> currentState;
162 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);
168 auto result = this->bus.call(method);
169
Andrew Geissler58a18012018-01-19 19:36:05 -0800170 // Check that the bus call didn't result in an error
171 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600172 {
173 log<level::ERR>("Error in bus call - could not resolve GetUnit for:",
174 entry(" %s", SYSTEMD_INTERFACE));
175 return false;
176 }
177
William A. Kennington III09568ff2018-05-11 00:03:12 -0700178 try
179 {
180 result.read(unitTargetPath);
181 }
182 catch (const SdBusError& e)
183 {
184 log<level::ERR>("Error in bus response - bad encoding for GetUnit",
185 entry("ERROR=%s", e.what()),
186 entry("REPLY_SIG=%s", result.get_signature()));
187 return false;
188 }
Josh D. King697474c2017-03-02 11:15:55 -0600189
Andrew Geissler58a18012018-01-19 19:36:05 -0800190 method = this->bus.new_method_call(
191 SYSTEMD_SERVICE,
192 static_cast<const std::string&>(unitTargetPath).c_str(),
193 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600194
195 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
196 result = this->bus.call(method);
197
Andrew Geissler58a18012018-01-19 19:36:05 -0800198 // Check that the bus call didn't result in an error
199 if (result.is_method_error())
Josh D. King697474c2017-03-02 11:15:55 -0600200 {
201 log<level::ERR>("Error in bus call - could not resolve Get for:",
202 entry(" %s", SYSTEMD_PROPERTY_IFACE));
203 return false;
204 }
205
206 result.read(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800207 const auto& currentStateStr =
208 sdbusplus::message::variant_ns::get<std::string>(currentState);
209 return currentStateStr == ACTIVE_STATE ||
210 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600211}
212
Patrick Williams8f8ba392017-05-05 15:47:39 -0500213int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600214{
Andrew Geissler58a18012018-01-19 19:36:05 -0800215 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600216 sdbusplus::message::object_path newStateObjPath;
217 std::string newStateUnit{};
218 std::string newStateResult{};
219
Andrew Geissler58a18012018-01-19 19:36:05 -0800220 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700221 try
222 {
223 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
224 }
225 catch (const SdBusError& e)
226 {
227 log<level::ERR>("Error in state change - bad encoding",
228 entry("ERROR=%s", e.what()),
229 entry("REPLY_SIG=%s", msg.get_signature()));
230 return 0;
231 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600232
Andrew Geissler58a18012018-01-19 19:36:05 -0800233 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
234 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600235 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930236 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600237 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500238 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600239 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800240 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
241 (newStateResult == "done") &&
242 (stateActive(CHASSIS_STATE_POWERON_TGT)))
243 {
244 log<level::INFO>("Received signal that power ON is complete");
245 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500246 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800247 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600248
249 return 0;
250}
251
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600252Chassis::Transition Chassis::requestedPowerTransition(Transition value)
253{
254
255 log<level::INFO>("Change to Chassis Requested Power State",
256 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
257 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600258 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600259 return server::Chassis::requestedPowerTransition(value);
260}
261
262Chassis::PowerState Chassis::currentPowerState(PowerState value)
263{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500264 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600265 log<level::INFO>("Change to Chassis Power State",
266 entry("CHASSIS_CURRENT_POWER_STATE=%s",
267 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500268
269 chassisPowerState = server::Chassis::currentPowerState(value);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700270 pOHTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500271 return chassisPowerState;
272}
273
274uint32_t Chassis::pOHCounter(uint32_t value)
275{
276 if (value != pOHCounter())
277 {
278 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500279 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500280 }
281 return pOHCounter();
282}
283
William A. Kennington IIId998f822018-10-17 23:17:57 -0700284void Chassis::pOHCallback()
285{
286 if (ChassisInherit::currentPowerState() == PowerState::On)
287 {
288 pOHCounter(pOHCounter() + 1);
289 }
290}
291
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500292void 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{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500343 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
344 fs::create_directories(dir);
345
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500346 try
347 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700348 auto event = sdeventplus::Event::get_default();
349 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
350 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500351 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700352 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500353 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700354 log<level::ERR>("Error occurred during the sdeventplus loop",
355 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500356 phosphor::logging::commit<InternalFailure>();
357 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600358}
359
Matt Spinler9eab9862018-07-11 14:13:52 -0500360void Chassis::serializeStateChangeTime()
361{
362 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
363 std::ofstream os(path.c_str(), std::ios::binary);
364 cereal::JSONOutputArchive oarchive(os);
365
366 oarchive(ChassisInherit::lastStateChangeTime(),
367 ChassisInherit::currentPowerState());
368}
369
370bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
371{
372 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
373
374 try
375 {
376 if (fs::exists(path))
377 {
378 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
379 cereal::JSONInputArchive iarchive(is);
380 iarchive(time, state);
381 return true;
382 }
383 }
384 catch (std::exception& e)
385 {
386 log<level::ERR>(e.what());
387 fs::remove(path);
388 }
389
390 return false;
391}
392
393void Chassis::restoreChassisStateChangeTime()
394{
395 uint64_t time;
396 PowerState state;
397
398 if (!deserializeStateChangeTime(time, state))
399 {
400 ChassisInherit::lastStateChangeTime(0);
401 }
402 else
403 {
404 ChassisInherit::lastStateChangeTime(time);
405 }
406}
407
408void Chassis::setStateChangeTime()
409{
410 using namespace std::chrono;
411 uint64_t lastTime;
412 PowerState lastState;
413
414 auto now =
415 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
416 .count();
417
418 // If power is on when the BMC is rebooted, this function will get called
419 // because sysStateChange() runs. Since the power state didn't change
420 // in this case, neither should the state change time, so check that
421 // the power state actually did change here.
422 if (deserializeStateChangeTime(lastTime, lastState))
423 {
424 if (lastState == ChassisInherit::currentPowerState())
425 {
426 return;
427 }
428 }
429
430 ChassisInherit::lastStateChangeTime(now);
431 serializeStateChangeTime();
432}
433
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600434} // namespace manager
435} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700436} // namespace phosphor