blob: 8fd56b8f5bc5270a40b340d847ce505f6f23a849 [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);
Anthony Wilson32c532e2018-10-25 21:56:07 -050073 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070074
William A. Kennington III7a0689a2018-11-12 17:19:33 -080075 if (sdbusplus::message::variant_ns::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070076 {
77 log<level::INFO>("Initial Chassis State will be On",
78 entry("CHASSIS_CURRENT_POWER_STATE=%s",
79 convertForMessage(PowerState::On).c_str()));
80 server::Chassis::currentPowerState(PowerState::On);
81 server::Chassis::requestedPowerTransition(Transition::On);
82 return;
83 }
Matt Spinler9eab9862018-07-11 14:13:52 -050084 else
85 {
86 // The system is off. If we think it should be on then
87 // we probably lost AC while up, so set a new state
88 // change time.
89 uint64_t lastTime;
90 PowerState lastState;
91
92 if (deserializeStateChangeTime(lastTime, lastState))
93 {
94 if (lastState == PowerState::On)
95 {
96 setStateChangeTime();
97 }
98 }
99 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700100 }
101 catch (const SdBusError& e)
102 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700103 // It's acceptable for the pgood state service to not be available
104 // since it will notify us of the pgood state when it comes up.
105 if (e.name() != nullptr &&
106 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
107 {
108 goto fail;
109 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600110
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700111 // Only log for unexpected error types.
112 log<level::ERR>("Error performing call to get pgood",
113 entry("ERROR=%s", e.what()));
114 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600115 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700116
117fail:
118 log<level::INFO>("Initial Chassis State will be Off",
119 entry("CHASSIS_CURRENT_POWER_STATE=%s",
120 convertForMessage(PowerState::Off).c_str()));
121 server::Chassis::currentPowerState(PowerState::Off);
122 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600123
124 return;
125}
126
Andrew Geisslerce80f242017-01-24 13:25:33 -0600127void Chassis::executeTransition(Transition tranReq)
128{
129 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
130
Andrew Geissler58a18012018-01-19 19:36:05 -0800131 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
132 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600133
134 method.append(sysdTarget);
135 method.append("replace");
136
137 this->bus.call_noreply(method);
138
139 return;
140}
141
Josh D. King697474c2017-03-02 11:15:55 -0600142bool Chassis::stateActive(const std::string& target)
143{
144 sdbusplus::message::variant<std::string> currentState;
145 sdbusplus::message::object_path unitTargetPath;
146
Andrew Geissler58a18012018-01-19 19:36:05 -0800147 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
148 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600149
150 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600151
William A. Kennington III09568ff2018-05-11 00:03:12 -0700152 try
153 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500154 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700155 result.read(unitTargetPath);
156 }
157 catch (const SdBusError& e)
158 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500159 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
William A. Kennington III09568ff2018-05-11 00:03:12 -0700160 return false;
161 }
Josh D. King697474c2017-03-02 11:15:55 -0600162
Andrew Geissler58a18012018-01-19 19:36:05 -0800163 method = this->bus.new_method_call(
164 SYSTEMD_SERVICE,
165 static_cast<const std::string&>(unitTargetPath).c_str(),
166 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600167
168 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600169
Anthony Wilson32c532e2018-10-25 21:56:07 -0500170 try
Josh D. King697474c2017-03-02 11:15:55 -0600171 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500172 auto result = this->bus.call(method);
173 result.read(currentState);
174 }
175 catch (const SdBusError& e)
176 {
177 log<level::ERR>("Error in ActiveState Get",
178 entry("ERROR=%s", e.what()));
Josh D. King697474c2017-03-02 11:15:55 -0600179 return false;
180 }
181
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800182 const auto& currentStateStr =
183 sdbusplus::message::variant_ns::get<std::string>(currentState);
184 return currentStateStr == ACTIVE_STATE ||
185 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600186}
187
Patrick Williams8f8ba392017-05-05 15:47:39 -0500188int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600189{
Andrew Geissler58a18012018-01-19 19:36:05 -0800190 uint32_t newStateID{};
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600191 sdbusplus::message::object_path newStateObjPath;
192 std::string newStateUnit{};
193 std::string newStateResult{};
194
Andrew Geissler58a18012018-01-19 19:36:05 -0800195 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700196 try
197 {
198 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
199 }
200 catch (const SdBusError& e)
201 {
202 log<level::ERR>("Error in state change - bad encoding",
203 entry("ERROR=%s", e.what()),
204 entry("REPLY_SIG=%s", msg.get_signature()));
205 return 0;
206 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600207
Andrew Geissler58a18012018-01-19 19:36:05 -0800208 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
209 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600210 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930211 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600212 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500213 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600214 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800215 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
216 (newStateResult == "done") &&
217 (stateActive(CHASSIS_STATE_POWERON_TGT)))
218 {
219 log<level::INFO>("Received signal that power ON is complete");
220 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500221 this->setStateChangeTime();
Andrew Geissler58a18012018-01-19 19:36:05 -0800222 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600223
224 return 0;
225}
226
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600227Chassis::Transition Chassis::requestedPowerTransition(Transition value)
228{
229
230 log<level::INFO>("Change to Chassis Requested Power State",
231 entry("CHASSIS_REQUESTED_POWER_STATE=%s",
232 convertForMessage(value).c_str()));
Andrew Geisslerce80f242017-01-24 13:25:33 -0600233 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600234 return server::Chassis::requestedPowerTransition(value);
235}
236
237Chassis::PowerState Chassis::currentPowerState(PowerState value)
238{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500239 PowerState chassisPowerState;
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600240 log<level::INFO>("Change to Chassis Power State",
241 entry("CHASSIS_CURRENT_POWER_STATE=%s",
242 convertForMessage(value).c_str()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500243
244 chassisPowerState = server::Chassis::currentPowerState(value);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700245 pOHTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500246 return chassisPowerState;
247}
248
249uint32_t Chassis::pOHCounter(uint32_t value)
250{
251 if (value != pOHCounter())
252 {
253 ChassisInherit::pOHCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500254 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500255 }
256 return pOHCounter();
257}
258
William A. Kennington IIId998f822018-10-17 23:17:57 -0700259void Chassis::pOHCallback()
260{
261 if (ChassisInherit::currentPowerState() == PowerState::On)
262 {
263 pOHCounter(pOHCounter() + 1);
264 }
265}
266
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500267void Chassis::restorePOHCounter()
268{
269 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500270 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500271 {
272 // set to default value
273 pOHCounter(0);
274 }
275 else
276 {
277 pOHCounter(counter);
278 }
279}
280
Matt Spinler81957842018-07-11 10:37:12 -0500281fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500282{
283 std::ofstream os(path.c_str(), std::ios::binary);
284 cereal::JSONOutputArchive oarchive(os);
285 oarchive(pOHCounter());
286 return path;
287}
288
Matt Spinler81957842018-07-11 10:37:12 -0500289bool Chassis::deserializePOH(const fs::path& path, uint32_t& pOHCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500290{
291 try
292 {
293 if (fs::exists(path))
294 {
295 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
296 cereal::JSONInputArchive iarchive(is);
297 iarchive(pOHCounter);
298 return true;
299 }
300 return false;
301 }
302 catch (cereal::Exception& e)
303 {
304 log<level::ERR>(e.what());
305 fs::remove(path);
306 return false;
307 }
308 catch (const fs::filesystem_error& e)
309 {
310 return false;
311 }
312
313 return false;
314}
315
316void Chassis::startPOHCounter()
317{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500318 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
319 fs::create_directories(dir);
320
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500321 try
322 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700323 auto event = sdeventplus::Event::get_default();
324 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
325 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500326 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700327 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500328 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700329 log<level::ERR>("Error occurred during the sdeventplus loop",
330 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500331 phosphor::logging::commit<InternalFailure>();
332 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600333}
334
Matt Spinler9eab9862018-07-11 14:13:52 -0500335void Chassis::serializeStateChangeTime()
336{
337 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
338 std::ofstream os(path.c_str(), std::ios::binary);
339 cereal::JSONOutputArchive oarchive(os);
340
341 oarchive(ChassisInherit::lastStateChangeTime(),
342 ChassisInherit::currentPowerState());
343}
344
345bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
346{
347 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
348
349 try
350 {
351 if (fs::exists(path))
352 {
353 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
354 cereal::JSONInputArchive iarchive(is);
355 iarchive(time, state);
356 return true;
357 }
358 }
359 catch (std::exception& e)
360 {
361 log<level::ERR>(e.what());
362 fs::remove(path);
363 }
364
365 return false;
366}
367
368void Chassis::restoreChassisStateChangeTime()
369{
370 uint64_t time;
371 PowerState state;
372
373 if (!deserializeStateChangeTime(time, state))
374 {
375 ChassisInherit::lastStateChangeTime(0);
376 }
377 else
378 {
379 ChassisInherit::lastStateChangeTime(time);
380 }
381}
382
383void Chassis::setStateChangeTime()
384{
385 using namespace std::chrono;
386 uint64_t lastTime;
387 PowerState lastState;
388
389 auto now =
390 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
391 .count();
392
393 // If power is on when the BMC is rebooted, this function will get called
394 // because sysStateChange() runs. Since the power state didn't change
395 // in this case, neither should the state change time, so check that
396 // the power state actually did change here.
397 if (deserializeStateChangeTime(lastTime, lastState))
398 {
399 if (lastState == ChassisInherit::currentPowerState())
400 {
401 return;
402 }
403 }
404
405 ChassisInherit::lastStateChangeTime(now);
406 serializeStateChangeTime();
407}
408
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600409} // namespace manager
410} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700411} // namespace phosphor