blob: 2ee58bca54151e9653d7c00ff2ea01359847468d [file] [log] [blame]
Josh D. Kingbdd9cb72016-12-19 11:13:43 -06001#include "bmc_state_manager.hpp"
Andrew Geisslere426b582020-05-28 12:40:55 -05002
Andrew Geissler2f60aae2019-09-12 13:25:21 -05003#include "xyz/openbmc_project/Common/error.hpp"
Josh D. Kingbdd9cb72016-12-19 11:13:43 -06004
Andrew Geisslere426b582020-05-28 12:40:55 -05005#include <sys/sysinfo.h>
6
7#include <phosphor-logging/elog-errors.hpp>
8#include <phosphor-logging/log.hpp>
9#include <sdbusplus/exception.hpp>
10
11#include <cassert>
Tim Lee2bfb1ef2021-03-17 09:50:35 +080012#include <filesystem>
13#include <fstream>
14#include <iostream>
Andrew Geisslere426b582020-05-28 12:40:55 -050015
Josh D. Kingbdd9cb72016-12-19 11:13:43 -060016namespace phosphor
17{
18namespace state
19{
20namespace manager
21{
22
Josh D. King6db38222016-12-19 14:52:40 -060023// When you see server:: you know we're referencing our base class
24namespace server = sdbusplus::xyz::openbmc_project::State::server;
25
26using namespace phosphor::logging;
Andrew Geissler2f60aae2019-09-12 13:25:21 -050027using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Josh D. King6db38222016-12-19 14:52:40 -060028
Anthony Wilsoneef31f82019-04-23 17:04:09 -050029constexpr auto obmcStandbyTarget = "multi-user.target";
Josh D. Kingd613b812016-12-19 16:47:45 -060030constexpr auto signalDone = "done";
Josh D. Kingd3e58472017-02-02 11:09:11 -060031constexpr auto activeState = "active";
Josh D. Kingd613b812016-12-19 16:47:45 -060032
Josh D. King5162a7b2016-12-19 16:15:00 -060033/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080034const std::map<server::BMC::Transition, const char*> SYSTEMD_TABLE = {
35 {server::BMC::Transition::Reboot, "reboot.target"}};
Josh D. King5162a7b2016-12-19 16:15:00 -060036
Andrew Geissler58a18012018-01-19 19:36:05 -080037constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
38constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
39constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
Josh D. Kingd3e58472017-02-02 11:09:11 -060040constexpr auto SYSTEMD_PRP_INTERFACE = "org.freedesktop.DBus.Properties";
Josh D. Kingd3e58472017-02-02 11:09:11 -060041
42void BMC::discoverInitialState()
43{
Patrick Williams2975e262020-05-13 18:01:09 -050044 std::variant<std::string> currentState;
Josh D. King2b5d8872017-02-21 13:37:17 -060045 sdbusplus::message::object_path unitTargetPath;
Josh D. Kingd3e58472017-02-02 11:09:11 -060046
Andrew Geissler58a18012018-01-19 19:36:05 -080047 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
48 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King2b5d8872017-02-21 13:37:17 -060049
50 method.append(obmcStandbyTarget);
51
Anthony Wilson32c532e2018-10-25 21:56:07 -050052 try
Josh D. King2b5d8872017-02-21 13:37:17 -060053 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050054 auto result = this->bus.call(method);
55 result.read(unitTargetPath);
56 }
Patrick Williams0a675212021-09-02 09:49:43 -050057 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050058 {
59 log<level::ERR>("Error in GetUnit call", entry("ERROR=%s", e.what()));
Josh D. King2b5d8872017-02-21 13:37:17 -060060 return;
61 }
62
Andrew Geissler58a18012018-01-19 19:36:05 -080063 method = this->bus.new_method_call(
64 SYSTEMD_SERVICE,
65 static_cast<const std::string&>(unitTargetPath).c_str(),
66 SYSTEMD_PRP_INTERFACE, "Get");
Josh D. Kingd3e58472017-02-02 11:09:11 -060067
68 method.append("org.freedesktop.systemd1.Unit", "ActiveState");
69
Anthony Wilson32c532e2018-10-25 21:56:07 -050070 try
Josh D. King2b5d8872017-02-21 13:37:17 -060071 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050072 auto result = this->bus.call(method);
73
74 // Is obmc-standby.target active or inactive?
75 result.read(currentState);
76 }
Patrick Williams0a675212021-09-02 09:49:43 -050077 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050078 {
79 log<level::INFO>("Error in ActiveState Get",
80 entry("ERROR=%s", e.what()));
Josh D. King2b5d8872017-02-21 13:37:17 -060081 return;
82 }
Josh D. Kingd3e58472017-02-02 11:09:11 -060083
Patrick Williams37413dc2020-05-13 11:29:54 -050084 auto currentStateStr = std::get<std::string>(currentState);
Anthony Wilson32c532e2018-10-25 21:56:07 -050085 if (currentStateStr == activeState)
Josh D. Kingd3e58472017-02-02 11:09:11 -060086 {
87 log<level::INFO>("Setting the BMCState field",
Andrew Geissler58a18012018-01-19 19:36:05 -080088 entry("CURRENT_BMC_STATE=%s", "BMC_READY"));
Josh D. Kingd3e58472017-02-02 11:09:11 -060089 this->currentBMCState(BMCState::Ready);
90
Andrew Geissler58a18012018-01-19 19:36:05 -080091 // Unsubscribe so we stop processing all other signals
92 method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
93 SYSTEMD_INTERFACE, "Unsubscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -050094 try
95 {
96 this->bus.call(method);
97 this->stateSignal.release();
98 }
Patrick Williams0a675212021-09-02 09:49:43 -050099 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500100 {
101 log<level::INFO>("Error in Unsubscribe",
102 entry("ERROR=%s", e.what()));
103 }
Josh D. Kingd3e58472017-02-02 11:09:11 -0600104 }
105 else
106 {
107 log<level::INFO>("Setting the BMCState field",
Andrew Geissler58a18012018-01-19 19:36:05 -0800108 entry("CURRENT_BMC_STATE=%s", "BMC_NOTREADY"));
Josh D. Kingd3e58472017-02-02 11:09:11 -0600109 this->currentBMCState(BMCState::NotReady);
110 }
111
112 return;
113}
114
Josh D. King6db38222016-12-19 14:52:40 -0600115void BMC::subscribeToSystemdSignals()
116{
Andrew Geissler58a18012018-01-19 19:36:05 -0800117 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
118 SYSTEMD_INTERFACE, "Subscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500119
120 try
121 {
122 this->bus.call(method);
123 }
Patrick Williams0a675212021-09-02 09:49:43 -0500124 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500125 {
Andrew Geissler2f60aae2019-09-12 13:25:21 -0500126 log<level::ERR>("Failed to subscribe to systemd signals",
127 entry("ERR=%s", e.what()));
128 elog<InternalFailure>();
Anthony Wilson32c532e2018-10-25 21:56:07 -0500129 }
Josh D. King6db38222016-12-19 14:52:40 -0600130
131 return;
132}
133
Josh D. King5162a7b2016-12-19 16:15:00 -0600134void BMC::executeTransition(const Transition tranReq)
135{
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000136 // HardReboot does not shutdown any services and immediately transitions
137 // into the reboot process
138 if (server::BMC::Transition::HardReboot == tranReq)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500139 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000140 auto method = this->bus.new_method_call(
141 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot");
142 try
143 {
144 this->bus.call(method);
145 }
Patrick Williams0a675212021-09-02 09:49:43 -0500146 catch (const sdbusplus::exception::exception& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000147 {
148 log<level::INFO>("Error in HardReboot",
149 entry("ERROR=%s", e.what()));
150 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500151 }
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000152 else
Anthony Wilson32c532e2018-10-25 21:56:07 -0500153 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000154 // Check to make sure it can be found
155 auto iter = SYSTEMD_TABLE.find(tranReq);
156 if (iter == SYSTEMD_TABLE.end())
157 return;
Anthony Wilson32c532e2018-10-25 21:56:07 -0500158
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000159 const auto& sysdUnit = iter->second;
160
161 auto method = this->bus.new_method_call(
162 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit");
163 // The only valid transition is reboot and that
164 // needs to be irreversible once started
165
166 method.append(sysdUnit, "replace-irreversibly");
167
168 try
169 {
170 this->bus.call(method);
171 }
Patrick Williams0a675212021-09-02 09:49:43 -0500172 catch (const sdbusplus::exception::exception& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000173 {
174 log<level::INFO>("Error in StartUnit - replace-irreversibly",
175 entry("ERROR=%s", e.what()));
176 }
177 }
Josh D. King5162a7b2016-12-19 16:15:00 -0600178 return;
179}
180
Patrick Williamsd32f8182017-05-05 15:55:24 -0500181int BMC::bmcStateChange(sdbusplus::message::message& msg)
Josh D. Kingd613b812016-12-19 16:47:45 -0600182{
Andrew Geissler58a18012018-01-19 19:36:05 -0800183 uint32_t newStateID{};
Josh D. Kingd613b812016-12-19 16:47:45 -0600184 sdbusplus::message::object_path newStateObjPath;
185 std::string newStateUnit{};
186 std::string newStateResult{};
187
Andrew Geissler58a18012018-01-19 19:36:05 -0800188 // Read the msg and populate each variable
Patrick Williamsd32f8182017-05-05 15:55:24 -0500189 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Josh D. Kingd613b812016-12-19 16:47:45 -0600190
Andrew Geissler58a18012018-01-19 19:36:05 -0800191 // Caught the signal that indicates the BMC is now BMC_READY
192 if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone))
Josh D. Kingd613b812016-12-19 16:47:45 -0600193 {
194 log<level::INFO>("BMC_READY");
195 this->currentBMCState(BMCState::Ready);
196
Andrew Geissler58a18012018-01-19 19:36:05 -0800197 // Unsubscribe so we stop processing all other signals
198 auto method =
199 this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
200 SYSTEMD_INTERFACE, "Unsubscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500201
202 try
203 {
204 this->bus.call(method);
205 this->stateSignal.release();
206 }
Patrick Williams0a675212021-09-02 09:49:43 -0500207 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500208 {
209 log<level::INFO>("Error in Unsubscribe",
210 entry("ERROR=%s", e.what()));
211 }
Josh D. Kingd613b812016-12-19 16:47:45 -0600212 }
213
214 return 0;
215}
216
Josh D. King6db38222016-12-19 14:52:40 -0600217BMC::Transition BMC::requestedBMCTransition(Transition value)
218{
Andrew Geissler58a18012018-01-19 19:36:05 -0800219 log<level::INFO>("Setting the RequestedBMCTransition field",
220 entry("REQUESTED_BMC_TRANSITION=0x%s",
221 convertForMessage(value).c_str()));
Josh D. King6db38222016-12-19 14:52:40 -0600222
Josh D. King5162a7b2016-12-19 16:15:00 -0600223 executeTransition(value);
224 return server::BMC::requestedBMCTransition(value);
Josh D. King6db38222016-12-19 14:52:40 -0600225}
226
Josh D. Kingd613b812016-12-19 16:47:45 -0600227BMC::BMCState BMC::currentBMCState(BMCState value)
228{
229 log<level::INFO>(
Andrew Geissler58a18012018-01-19 19:36:05 -0800230 "Setting the BMCState field",
231 entry("CURRENT_BMC_STATE=0x%s", convertForMessage(value).c_str()));
Josh D. Kingd613b812016-12-19 16:47:45 -0600232
233 return server::BMC::currentBMCState(value);
234}
235
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800236BMC::RebootCause BMC::lastRebootCause(RebootCause value)
237{
238 log<level::INFO>(
239 "Setting the RebootCause field",
240 entry("LAST_REBOOT_CAUSE=0x%s", convertForMessage(value).c_str()));
241
242 return server::BMC::lastRebootCause(value);
243}
244
Matt Spinlere6710b72018-07-12 16:05:55 -0500245uint64_t BMC::lastRebootTime() const
246{
247 using namespace std::chrono;
248 struct sysinfo info;
249
250 auto rc = sysinfo(&info);
251 assert(rc == 0);
252
253 // Since uptime is in seconds, also get the current time in seconds.
254 auto now = time_point_cast<seconds>(system_clock::now());
255 auto rebootTime = now - seconds(info.uptime);
256
257 return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count();
258}
259
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800260void BMC::discoverLastRebootCause()
261{
262 uint64_t bootReason = 0;
263 std::ifstream file;
264 auto bootstatusPath = "/sys/class/watchdog/watchdog0/bootstatus";
265
266 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
267 std::ifstream::eofbit);
268
269 try
270 {
271 file.open(bootstatusPath);
272 file >> bootReason;
273 }
274 catch (const std::exception& e)
275 {
276 auto rc = errno;
277 log<level::ERR>((std::string("Failed to read sysfs file "
278 "errno=") +
279 std::to_string(rc) + " FILENAME=" + bootstatusPath)
280 .c_str());
281 }
282
283 switch (bootReason)
284 {
285 case WDIOF_EXTERN1:
286 this->lastRebootCause(RebootCause::Watchdog);
287 break;
288 case WDIOF_CARDRESET:
289 this->lastRebootCause(RebootCause::POR);
290 break;
291 default:
292 this->lastRebootCause(RebootCause::Unknown);
293 break;
294 }
295
296 return;
297}
298
Josh D. Kingbdd9cb72016-12-19 11:13:43 -0600299} // namespace manager
300} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700301} // namespace phosphor