blob: a424ebff66bf6bbc52971a899ebe27d0ea9794a2 [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
3#include "chassis_state_manager.hpp"
4
5#include "xyz/openbmc_project/Common/error.hpp"
6#include "xyz/openbmc_project/State/Shutdown/Power/error.hpp"
7
Matt Spinlerbbbc0162020-11-11 14:43:50 -06008#include <fmt/format.h>
9
Andrew Geisslere426b582020-05-28 12:40:55 -050010#include <cereal/archives/json.hpp>
11#include <phosphor-logging/elog-errors.hpp>
12#include <phosphor-logging/log.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060013#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070014#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070015#include <sdeventplus/event.hpp>
16#include <sdeventplus/exception.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050017
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060018#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050019#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050020
Andrew Geisslera90a31a2016-12-13 16:16:28 -060021namespace phosphor
22{
23namespace state
24{
25namespace manager
26{
27
28// When you see server:: you know we're referencing our base class
29namespace server = sdbusplus::xyz::openbmc_project::State::server;
30
31using namespace phosphor::logging;
William A. Kennington III09568ff2018-05-11 00:03:12 -070032using sdbusplus::exception::SdBusError;
William A. Kennington IIId998f822018-10-17 23:17:57 -070033using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050034using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Josh D. King6838ea92017-04-11 13:39:18 -050035constexpr auto CHASSIS_STATE_POWEROFF_TGT = "obmc-chassis-poweroff@0.target";
Andrew Geissler58a18012018-01-19 19:36:05 -080036constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT =
37 "obmc-chassis-hard-poweroff@0.target";
Josh D. King6838ea92017-04-11 13:39:18 -050038constexpr auto CHASSIS_STATE_POWERON_TGT = "obmc-chassis-poweron@0.target";
Andrew Geissler0029a5d2017-01-24 14:48:35 -060039
Josh D. King697474c2017-03-02 11:15:55 -060040constexpr auto ACTIVE_STATE = "active";
41constexpr auto ACTIVATING_STATE = "activating";
42
Andrew Geisslerce80f242017-01-24 13:25:33 -060043/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080044const std::map<server::Chassis::Transition, std::string> SYSTEMD_TARGET_TABLE =
45 {
Andrew Geissler8cf2f9a2017-07-21 11:58:04 -050046 // Use the hard off target to ensure we shutdown immediately
47 {server::Chassis::Transition::Off, CHASSIS_STATE_HARD_POWEROFF_TGT},
Andrew Geissler58a18012018-01-19 19:36:05 -080048 {server::Chassis::Transition::On, CHASSIS_STATE_POWERON_TGT}};
Andrew Geisslerce80f242017-01-24 13:25:33 -060049
Andrew Geissler58a18012018-01-19 19:36:05 -080050constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
51constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060052constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
53
Josh D. King697474c2017-03-02 11:15:55 -060054constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
55constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
56
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060057constexpr auto CHASSIS_ON_FILE = "/run/openbmc/chassis@%d-on";
58
Andrew Geissler0029a5d2017-01-24 14:48:35 -060059void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060060{
Andrew Geissler3a30b052019-05-14 15:54:37 -050061 try
62 {
63 auto method = this->bus.new_method_call(
64 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050065 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050066 }
67 catch (const sdbusplus::exception::SdBusError& ex)
68 {
69 log<level::ERR>("Failed to subscribe to systemd signals",
70 entry("ERR=%s", ex.what()));
71 elog<InternalFailure>();
72 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060073
Andrew Geissler0029a5d2017-01-24 14:48:35 -060074 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060075}
76
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060077// TODO - Will be rewritten once sdbusplus client bindings are in place
78// and persistent storage design is in place and sdbusplus
79// has read property function
80void Chassis::determineInitialState()
81{
Patrick Williams2975e262020-05-13 18:01:09 -050082 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -080083 auto method = this->bus.new_method_call(
84 "org.openbmc.control.Power", "/org/openbmc/control/power0",
85 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060086
87 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -070088 try
89 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070090 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -050091 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070092
Patrick Williams37413dc2020-05-13 11:29:54 -050093 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -070094 {
95 log<level::INFO>("Initial Chassis State will be On",
96 entry("CHASSIS_CURRENT_POWER_STATE=%s",
97 convertForMessage(PowerState::On).c_str()));
98 server::Chassis::currentPowerState(PowerState::On);
99 server::Chassis::requestedPowerTransition(Transition::On);
100 return;
101 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500102 else
103 {
104 // The system is off. If we think it should be on then
105 // we probably lost AC while up, so set a new state
106 // change time.
107 uint64_t lastTime;
108 PowerState lastState;
109
110 if (deserializeStateChangeTime(lastTime, lastState))
111 {
112 if (lastState == PowerState::On)
113 {
Aatir Manzur27115ae2019-07-23 16:25:38 -0500114 report<Blackout>();
Matt Spinler9eab9862018-07-11 14:13:52 -0500115 setStateChangeTime();
116 }
117 }
118 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700119 }
120 catch (const SdBusError& e)
121 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700122 // It's acceptable for the pgood state service to not be available
123 // since it will notify us of the pgood state when it comes up.
124 if (e.name() != nullptr &&
125 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
126 {
127 goto fail;
128 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600129
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700130 // Only log for unexpected error types.
131 log<level::ERR>("Error performing call to get pgood",
132 entry("ERROR=%s", e.what()));
133 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600134 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700135
136fail:
137 log<level::INFO>("Initial Chassis State will be Off",
138 entry("CHASSIS_CURRENT_POWER_STATE=%s",
139 convertForMessage(PowerState::Off).c_str()));
140 server::Chassis::currentPowerState(PowerState::Off);
141 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600142
143 return;
144}
145
Andrew Geisslerce80f242017-01-24 13:25:33 -0600146void Chassis::executeTransition(Transition tranReq)
147{
148 auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;
149
Andrew Geissler58a18012018-01-19 19:36:05 -0800150 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
151 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600152
153 method.append(sysdTarget);
154 method.append("replace");
155
156 this->bus.call_noreply(method);
157
158 return;
159}
160
Josh D. King697474c2017-03-02 11:15:55 -0600161bool Chassis::stateActive(const std::string& target)
162{
Patrick Williams2975e262020-05-13 18:01:09 -0500163 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600164 sdbusplus::message::object_path unitTargetPath;
165
Andrew Geissler58a18012018-01-19 19:36:05 -0800166 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
167 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600168
169 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600170
William A. Kennington III09568ff2018-05-11 00:03:12 -0700171 try
172 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500173 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700174 result.read(unitTargetPath);
175 }
176 catch (const SdBusError& e)
177 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500178 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
William A. Kennington III09568ff2018-05-11 00:03:12 -0700179 return false;
180 }
Josh D. King697474c2017-03-02 11:15:55 -0600181
Andrew Geissler58a18012018-01-19 19:36:05 -0800182 method = this->bus.new_method_call(
183 SYSTEMD_SERVICE,
184 static_cast<const std::string&>(unitTargetPath).c_str(),
185 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600186
187 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600188
Anthony Wilson32c532e2018-10-25 21:56:07 -0500189 try
Josh D. King697474c2017-03-02 11:15:55 -0600190 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500191 auto result = this->bus.call(method);
192 result.read(currentState);
193 }
194 catch (const SdBusError& e)
195 {
196 log<level::ERR>("Error in ActiveState Get",
197 entry("ERROR=%s", e.what()));
Josh D. King697474c2017-03-02 11:15:55 -0600198 return false;
199 }
200
Patrick Williams37413dc2020-05-13 11:29:54 -0500201 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800202 return currentStateStr == ACTIVE_STATE ||
203 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600204}
205
Patrick Williams8f8ba392017-05-05 15:47:39 -0500206int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600207{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600208 sdbusplus::message::object_path newStateObjPath;
209 std::string newStateUnit{};
210 std::string newStateResult{};
211
Andrew Geissler58a18012018-01-19 19:36:05 -0800212 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700213 try
214 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500215 // newStateID is a throwaway that is needed in order to read the
216 // parameters that are useful out of the dbus message
217 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700218 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
219 }
220 catch (const SdBusError& e)
221 {
222 log<level::ERR>("Error in state change - bad encoding",
223 entry("ERROR=%s", e.what()),
224 entry("REPLY_SIG=%s", msg.get_signature()));
225 return 0;
226 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600227
Andrew Geissler58a18012018-01-19 19:36:05 -0800228 if ((newStateUnit == CHASSIS_STATE_POWEROFF_TGT) &&
229 (newStateResult == "done") && (!stateActive(CHASSIS_STATE_POWERON_TGT)))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600230 {
Andrew Jeffery55b983e2017-04-19 11:11:26 +0930231 log<level::INFO>("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600232 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500233 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600234 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800235 else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) &&
236 (newStateResult == "done") &&
237 (stateActive(CHASSIS_STATE_POWERON_TGT)))
238 {
239 log<level::INFO>("Received signal that power ON is complete");
240 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500241 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600242
243 // Remove temporary file which is utilized for scenarios where the
244 // BMC is rebooted while the chassis power is still on.
245 // This file is used to indicate to chassis related systemd services
246 // that the chassis is already on and they should skip running.
247 // Once the chassis state is back to on we can clear this file.
248 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
249 size++; // null
250 std::unique_ptr<char[]> chassisFile(new char[size]);
251 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
252 if (std::filesystem::exists(chassisFile.get()))
253 {
254 std::filesystem::remove(chassisFile.get());
255 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800256 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600257
258 return 0;
259}
260
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600261Chassis::Transition Chassis::requestedPowerTransition(Transition value)
262{
263
Matt Spinlerbbbc0162020-11-11 14:43:50 -0600264 log<level::INFO>(fmt::format("Change to Chassis Requested Power State: {}",
265 convertForMessage(value))
266 .c_str());
Andrew Geisslerce80f242017-01-24 13:25:33 -0600267 executeTransition(value);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600268 return server::Chassis::requestedPowerTransition(value);
269}
270
271Chassis::PowerState Chassis::currentPowerState(PowerState value)
272{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500273 PowerState chassisPowerState;
Matt Spinlerbbbc0162020-11-11 14:43:50 -0600274 log<level::INFO>(fmt::format("Change to Chassis Power State: {}",
275 convertForMessage(value))
276 .c_str());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500277
278 chassisPowerState = server::Chassis::currentPowerState(value);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500279 pohTimer.setEnabled(chassisPowerState == PowerState::On);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500280 return chassisPowerState;
281}
282
Patrick Williams45a1ed72021-04-30 21:02:43 -0500283uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500284{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500285 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500286 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500287 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500288 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500289 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500290 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500291}
292
Patrick Williams45a1ed72021-04-30 21:02:43 -0500293void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700294{
295 if (ChassisInherit::currentPowerState() == PowerState::On)
296 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500297 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700298 }
299}
300
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500301void Chassis::restorePOHCounter()
302{
303 uint32_t counter;
Matt Spinler81957842018-07-11 10:37:12 -0500304 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500305 {
306 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500307 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500308 }
309 else
310 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500311 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500312 }
313}
314
Matt Spinler81957842018-07-11 10:37:12 -0500315fs::path Chassis::serializePOH(const fs::path& path)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500316{
317 std::ofstream os(path.c_str(), std::ios::binary);
318 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500319 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500320 return path;
321}
322
Patrick Williams45a1ed72021-04-30 21:02:43 -0500323bool Chassis::deserializePOH(const fs::path& path, uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500324{
325 try
326 {
327 if (fs::exists(path))
328 {
329 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
330 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500331 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500332 return true;
333 }
334 return false;
335 }
336 catch (cereal::Exception& e)
337 {
338 log<level::ERR>(e.what());
339 fs::remove(path);
340 return false;
341 }
342 catch (const fs::filesystem_error& e)
343 {
344 return false;
345 }
346
347 return false;
348}
349
350void Chassis::startPOHCounter()
351{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500352 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
353 fs::create_directories(dir);
354
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500355 try
356 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700357 auto event = sdeventplus::Event::get_default();
358 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
359 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500360 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700361 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500362 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700363 log<level::ERR>("Error occurred during the sdeventplus loop",
364 entry("ERROR=%s", e.what()));
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500365 phosphor::logging::commit<InternalFailure>();
366 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600367}
368
Matt Spinler9eab9862018-07-11 14:13:52 -0500369void Chassis::serializeStateChangeTime()
370{
371 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
372 std::ofstream os(path.c_str(), std::ios::binary);
373 cereal::JSONOutputArchive oarchive(os);
374
375 oarchive(ChassisInherit::lastStateChangeTime(),
376 ChassisInherit::currentPowerState());
377}
378
379bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
380{
381 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH};
382
383 try
384 {
385 if (fs::exists(path))
386 {
387 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
388 cereal::JSONInputArchive iarchive(is);
389 iarchive(time, state);
390 return true;
391 }
392 }
393 catch (std::exception& e)
394 {
395 log<level::ERR>(e.what());
396 fs::remove(path);
397 }
398
399 return false;
400}
401
402void Chassis::restoreChassisStateChangeTime()
403{
404 uint64_t time;
405 PowerState state;
406
407 if (!deserializeStateChangeTime(time, state))
408 {
409 ChassisInherit::lastStateChangeTime(0);
410 }
411 else
412 {
413 ChassisInherit::lastStateChangeTime(time);
414 }
415}
416
417void Chassis::setStateChangeTime()
418{
419 using namespace std::chrono;
420 uint64_t lastTime;
421 PowerState lastState;
422
423 auto now =
424 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
425 .count();
426
427 // If power is on when the BMC is rebooted, this function will get called
428 // because sysStateChange() runs. Since the power state didn't change
429 // in this case, neither should the state change time, so check that
430 // the power state actually did change here.
431 if (deserializeStateChangeTime(lastTime, lastState))
432 {
433 if (lastState == ChassisInherit::currentPowerState())
434 {
435 return;
436 }
437 }
438
439 ChassisInherit::lastStateChangeTime(now);
440 serializeStateChangeTime();
441}
442
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600443} // namespace manager
444} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700445} // namespace phosphor