blob: 8f1ce3bd1b3786b5bd968f1b8dbbb5836352439a [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 Geissler3a30b052019-05-14 15:54:37 -050052 try
53 {
54 auto method = this->bus.new_method_call(
55 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
56 this->bus.call_noreply(method);
57 }
58 catch (const sdbusplus::exception::SdBusError& ex)
59 {
60 log<level::ERR>("Failed to subscribe to systemd signals",
61 entry("ERR=%s", ex.what()));
62 elog<InternalFailure>();
63 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060064
Andrew Geissler0029a5d2017-01-24 14:48:35 -060065 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060066}
67
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060068// TODO - Will be rewritten once sdbusplus client bindings are in place
69// and persistent storage design is in place and sdbusplus
70// has read property function
71void Chassis::determineInitialState()
72{
73 sdbusplus::message::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080074 auto method = this->bus.new_method_call(
75 "org.openbmc.control.Power", "/org/openbmc/control/power0",
76 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060077
78 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070079 try
80 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070081 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -050082 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070083
William A. Kennington III7a0689a2018-11-12 17:19:33 -080084 if (sdbusplus::message::variant_ns::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070085 {
86 log<level::INFO>("Initial Chassis State will be On",
87 entry("CHASSIS_CURRENT_POWER_STATE=%s",
88 convertForMessage(PowerState::On).c_str()));
89 server::Chassis::currentPowerState(PowerState::On);
90 server::Chassis::requestedPowerTransition(Transition::On);
91 return;
92 }
Matt Spinler9eab9862018-07-11 14:13:52 -050093 else
94 {
95 // The system is off. If we think it should be on then
96 // we probably lost AC while up, so set a new state
97 // change time.
98 uint64_t lastTime;
99 PowerState lastState;
100
101 if (deserializeStateChangeTime(lastTime, lastState))
102 {
103 if (lastState == PowerState::On)
104 {
105 setStateChangeTime();
106 }
107 }
108 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700109 }
110 catch (const SdBusError& e)
111 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700112 // It's acceptable for the pgood state service to not be available
113 // since it will notify us of the pgood state when it comes up.
114 if (e.name() != nullptr &&
115 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
116 {
117 goto fail;
118 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600119
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700120 // Only log for unexpected error types.
121 log<level::ERR>("Error performing call to get pgood",
122 entry("ERROR=%s", e.what()));
123 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600124 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700125
126fail:
127 log<level::INFO>("Initial Chassis State will be Off",
128 entry("CHASSIS_CURRENT_POWER_STATE=%s",
129 convertForMessage(PowerState::Off).c_str()));
130 server::Chassis::currentPowerState(PowerState::Off);
131 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600132
133 return;
134}
135
Andrew Geisslerce80f242017-01-24 13:25:33 -0600136void Chassis::executeTransition(Transition tranReq)
137{
138 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
139
Andrew Geissler58a18012018-01-19 19:36:05 -0800140 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
141 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600142
143 method.append(sysdTarget);
144 method.append("replace");
145
146 this->bus.call_noreply(method);
147
148 return;
149}
150
Josh D. King697474c2017-03-02 11:15:55 -0600151bool Chassis::stateActive(const std::string& target)
152{
153 sdbusplus::message::variant<std::string> currentState;
154 sdbusplus::message::object_path unitTargetPath;
155
Andrew Geissler58a18012018-01-19 19:36:05 -0800156 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
157 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600158
159 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600160
William A. Kennington III09568ff2018-05-11 00:03:12 -0700161 try
162 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500163 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700164 result.read(unitTargetPath);
165 }
166 catch (const SdBusError& e)
167 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500168 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
William A. Kennington III09568ff2018-05-11 00:03:12 -0700169 return false;
170 }
Josh D. King697474c2017-03-02 11:15:55 -0600171
Andrew Geissler58a18012018-01-19 19:36:05 -0800172 method = this->bus.new_method_call(
173 SYSTEMD_SERVICE,
174 static_cast<const std::string&>(unitTargetPath).c_str(),
175 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600176
177 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600178
Anthony Wilson32c532e2018-10-25 21:56:07 -0500179 try
Josh D. King697474c2017-03-02 11:15:55 -0600180 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500181 auto result = this->bus.call(method);
182 result.read(currentState);
183 }
184 catch (const SdBusError& e)
185 {
186 log<level::ERR>("Error in ActiveState Get",
187 entry("ERROR=%s", e.what()));
Josh D. King697474c2017-03-02 11:15:55 -0600188 return false;
189 }
190
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800191 const auto& currentStateStr =
192 sdbusplus::message::variant_ns::get<std::string>(currentState);
193 return currentStateStr == ACTIVE_STATE ||
194 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600195}
196
Patrick Williams8f8ba392017-05-05 15:47:39 -0500197int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600198{
Andrew Geissler58a18012018-01-19 19:36:05 -0800199 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600200 sdbusplus::message::object_path newStateObjPath;
201 std::string newStateUnit{};
202 std::string newStateResult{};
203
Andrew Geissler58a18012018-01-19 19:36:05 -0800204 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700205 try
206 {
207 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
208 }
209 catch (const SdBusError& e)
210 {
211 log<level::ERR>("Error in state change - bad encoding",
212 entry("ERROR=%s", e.what()),
213 entry("REPLY_SIG=%s", msg.get_signature()));
214 return 0;
215 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600216
Andrew Geissler58a18012018-01-19 19:36:05 -0800217 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
218 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600219 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930220 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600221 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500222 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600223 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800224 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
225 (newStateResult == "done") &&
226 (stateActive(CHASSIS_STATE_POWERON_TGT)))
227 {
228 log<level::INFO>("Received signal that power ON is complete");
229 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500230 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800231 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600232
233 return 0;
234}
235
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600236Chassis::Transition Chassis::requestedPowerTransition(Transition value)
237{
238
239 log<level::INFO>("Change to Chassis Requested Power State",
240 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
241 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600242 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600243 return server::Chassis::requestedPowerTransition(value);
244}
245
246Chassis::PowerState Chassis::currentPowerState(PowerState value)
247{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500248 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600249 log<level::INFO>("Change to Chassis Power State",
250 entry("CHASSIS_CURRENT_POWER_STATE=%s",
251 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500252
253 chassisPowerState = server::Chassis::currentPowerState(value);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700254 pOHTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500255 return chassisPowerState;
256}
257
258uint32_t Chassis::pOHCounter(uint32_t value)
259{
260 if (value != pOHCounter())
261 {
262 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500263 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500264 }
265 return pOHCounter();
266}
267
William A. Kennington IIId998f822018-10-17 23:17:57 -0700268void Chassis::pOHCallback()
269{
270 if (ChassisInherit::currentPowerState() == PowerState::On)
271 {
272 pOHCounter(pOHCounter() + 1);
273 }
274}
275
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500276void Chassis::restorePOHCounter()
277{
278 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500279 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500280 {
281 // set to default value
282 pOHCounter(0);
283 }
284 else
285 {
286 pOHCounter(counter);
287 }
288}
289
Matt Spinler81957842018-07-11 10:37:12 -0500290fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500291{
292 std::ofstream os(path.c_str(), std::ios::binary);
293 cereal::JSONOutputArchive oarchive(os);
294 oarchive(pOHCounter());
295 return path;
296}
297
Matt Spinler81957842018-07-11 10:37:12 -0500298bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500299{
300 try
301 {
302 if (fs::exists(path))
303 {
304 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
305 cereal::JSONInputArchive iarchive(is);
306 iarchive(pOHCounter);
307 return true;
308 }
309 return false;
310 }
311 catch (cereal::Exception& e)
312 {
313 log<level::ERR>(e.what());
314 fs::remove(path);
315 return false;
316 }
317 catch (const fs::filesystem_error& e)
318 {
319 return false;
320 }
321
322 return false;
323}
324
325void Chassis::startPOHCounter()
326{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500327 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
328 fs::create_directories(dir);
329
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500330 try
331 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700332 auto event = sdeventplus::Event::get_default();
333 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
334 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500335 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700336 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500337 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700338 log<level::ERR>("Error occurred during the sdeventplus loop",
339 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500340 phosphor::logging::commit<InternalFailure>();
341 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600342}
343
Matt Spinler9eab9862018-07-11 14:13:52 -0500344void Chassis::serializeStateChangeTime()
345{
346 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
347 std::ofstream os(path.c_str(), std::ios::binary);
348 cereal::JSONOutputArchive oarchive(os);
349
350 oarchive(ChassisInherit::lastStateChangeTime(),
351 ChassisInherit::currentPowerState());
352}
353
354bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
355{
356 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
357
358 try
359 {
360 if (fs::exists(path))
361 {
362 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
363 cereal::JSONInputArchive iarchive(is);
364 iarchive(time, state);
365 return true;
366 }
367 }
368 catch (std::exception& e)
369 {
370 log<level::ERR>(e.what());
371 fs::remove(path);
372 }
373
374 return false;
375}
376
377void Chassis::restoreChassisStateChangeTime()
378{
379 uint64_t time;
380 PowerState state;
381
382 if (!deserializeStateChangeTime(time, state))
383 {
384 ChassisInherit::lastStateChangeTime(0);
385 }
386 else
387 {
388 ChassisInherit::lastStateChangeTime(time);
389 }
390}
391
392void Chassis::setStateChangeTime()
393{
394 using namespace std::chrono;
395 uint64_t lastTime;
396 PowerState lastState;
397
398 auto now =
399 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
400 .count();
401
402 // If power is on when the BMC is rebooted, this function will get called
403 // because sysStateChange() runs. Since the power state didn't change
404 // in this case, neither should the state change time, so check that
405 // the power state actually did change here.
406 if (deserializeStateChangeTime(lastTime, lastState))
407 {
408 if (lastState == ChassisInherit::currentPowerState())
409 {
410 return;
411 }
412 }
413
414 ChassisInherit::lastStateChangeTime(now);
415 serializeStateChangeTime();
416}
417
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600418} // namespace manager
419} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700420} // namespace phosphor