blob: 2b785204f4fd3ce2202473aa14fddd1836fb85f6 [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 Geissler98e64e62022-01-25 16:02:56 -06003#include "utils.hpp"
Andrew Geissler2f60aae2019-09-12 13:25:21 -05004#include "xyz/openbmc_project/Common/error.hpp"
Josh D. Kingbdd9cb72016-12-19 11:13:43 -06005
Andrew Geissler98e64e62022-01-25 16:02:56 -06006#include <gpiod.h>
Andrew Geisslere426b582020-05-28 12:40:55 -05007#include <sys/sysinfo.h>
8
9#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler429100a2021-09-09 12:50:24 -050010#include <phosphor-logging/lg2.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050011#include <sdbusplus/exception.hpp>
12
13#include <cassert>
Tim Lee2bfb1ef2021-03-17 09:50:35 +080014#include <filesystem>
15#include <fstream>
16#include <iostream>
Andrew Geisslere426b582020-05-28 12:40:55 -050017
Josh D. Kingbdd9cb72016-12-19 11:13:43 -060018namespace phosphor
19{
20namespace state
21{
22namespace manager
23{
24
Andrew Geissler429100a2021-09-09 12:50:24 -050025PHOSPHOR_LOG2_USING;
26
Josh D. King6db38222016-12-19 14:52:40 -060027// When you see server:: you know we're referencing our base class
28namespace server = sdbusplus::xyz::openbmc_project::State::server;
29
30using namespace phosphor::logging;
Andrew Geissler2f60aae2019-09-12 13:25:21 -050031using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Josh D. King6db38222016-12-19 14:52:40 -060032
Andrew Geissler9eb0e442022-02-18 10:21:15 -060033constexpr auto obmcQuiesceTarget = "obmc-bmc-service-quiesce@0.target";
Anthony Wilsoneef31f82019-04-23 17:04:09 -050034constexpr auto obmcStandbyTarget = "multi-user.target";
Josh D. Kingd613b812016-12-19 16:47:45 -060035constexpr auto signalDone = "done";
Josh D. Kingd3e58472017-02-02 11:09:11 -060036constexpr auto activeState = "active";
Josh D. Kingd613b812016-12-19 16:47:45 -060037
Josh D. King5162a7b2016-12-19 16:15:00 -060038/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080039const std::map<server::BMC::Transition, const char*> SYSTEMD_TABLE = {
40 {server::BMC::Transition::Reboot, "reboot.target"}};
Josh D. King5162a7b2016-12-19 16:15:00 -060041
Andrew Geissler58a18012018-01-19 19:36:05 -080042constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
43constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
44constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
Josh D. Kingd3e58472017-02-02 11:09:11 -060045constexpr auto SYSTEMD_PRP_INTERFACE = "org.freedesktop.DBus.Properties";
Josh D. Kingd3e58472017-02-02 11:09:11 -060046
Andrew Geissler2774c782022-02-17 16:57:14 -060047std::string BMC::getUnitState(const std::string& unitToCheck)
Josh D. Kingd3e58472017-02-02 11:09:11 -060048{
Patrick Williams2975e262020-05-13 18:01:09 -050049 std::variant<std::string> currentState;
Josh D. King2b5d8872017-02-21 13:37:17 -060050 sdbusplus::message::object_path unitTargetPath;
Josh D. Kingd3e58472017-02-02 11:09:11 -060051
Andrew Geissler58a18012018-01-19 19:36:05 -080052 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
53 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King2b5d8872017-02-21 13:37:17 -060054
Andrew Geissler2774c782022-02-17 16:57:14 -060055 method.append(unitToCheck);
Josh D. King2b5d8872017-02-21 13:37:17 -060056
Anthony Wilson32c532e2018-10-25 21:56:07 -050057 try
Josh D. King2b5d8872017-02-21 13:37:17 -060058 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050059 auto result = this->bus.call(method);
60 result.read(unitTargetPath);
61 }
Patrick Williams0a675212021-09-02 09:49:43 -050062 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050063 {
Andrew Geissler429100a2021-09-09 12:50:24 -050064 error("Error in GetUnit call: {ERROR}", "ERROR", e);
Andrew Geissler2774c782022-02-17 16:57:14 -060065 return std::string{};
Josh D. King2b5d8872017-02-21 13:37:17 -060066 }
67
Andrew Geissler58a18012018-01-19 19:36:05 -080068 method = this->bus.new_method_call(
69 SYSTEMD_SERVICE,
70 static_cast<const std::string&>(unitTargetPath).c_str(),
71 SYSTEMD_PRP_INTERFACE, "Get");
Josh D. Kingd3e58472017-02-02 11:09:11 -060072
73 method.append("org.freedesktop.systemd1.Unit", "ActiveState");
74
Anthony Wilson32c532e2018-10-25 21:56:07 -050075 try
Josh D. King2b5d8872017-02-21 13:37:17 -060076 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050077 auto result = this->bus.call(method);
78
79 // Is obmc-standby.target active or inactive?
80 result.read(currentState);
81 }
Patrick Williams0a675212021-09-02 09:49:43 -050082 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050083 {
Andrew Geissler429100a2021-09-09 12:50:24 -050084 info("Error in ActiveState Get: {ERROR}", "ERROR", e);
Andrew Geissler2774c782022-02-17 16:57:14 -060085 return std::string{};
Josh D. King2b5d8872017-02-21 13:37:17 -060086 }
Andrew Geissler2774c782022-02-17 16:57:14 -060087 return (std::get<std::string>(currentState));
88}
Josh D. Kingd3e58472017-02-02 11:09:11 -060089
Andrew Geissler2774c782022-02-17 16:57:14 -060090void BMC::discoverInitialState()
91{
Andrew Geissler9eb0e442022-02-18 10:21:15 -060092
93 // First look to see if the BMC quiesce target is active
94 auto currentStateStr = getUnitState(obmcQuiesceTarget);
95 if (currentStateStr == activeState)
96 {
97 info("Setting the BMCState field to BMC_QUIESCED");
98 this->currentBMCState(BMCState::Quiesced);
99 return;
100 }
101
102 // If not quiesced, then check standby target
103 currentStateStr = getUnitState(obmcStandbyTarget);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500104 if (currentStateStr == activeState)
Josh D. Kingd3e58472017-02-02 11:09:11 -0600105 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500106 info("Setting the BMCState field to BMC_READY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600107 this->currentBMCState(BMCState::Ready);
Josh D. Kingd3e58472017-02-02 11:09:11 -0600108 }
109 else
110 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500111 info("Setting the BMCState field to BMC_NOTREADY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600112 this->currentBMCState(BMCState::NotReady);
113 }
114
115 return;
116}
117
Josh D. King6db38222016-12-19 14:52:40 -0600118void BMC::subscribeToSystemdSignals()
119{
Andrew Geissler58a18012018-01-19 19:36:05 -0800120 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
121 SYSTEMD_INTERFACE, "Subscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500122
123 try
124 {
125 this->bus.call(method);
126 }
Patrick Williams0a675212021-09-02 09:49:43 -0500127 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500128 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500129 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler2f60aae2019-09-12 13:25:21 -0500130 elog<InternalFailure>();
Anthony Wilson32c532e2018-10-25 21:56:07 -0500131 }
Josh D. King6db38222016-12-19 14:52:40 -0600132
133 return;
134}
135
Josh D. King5162a7b2016-12-19 16:15:00 -0600136void BMC::executeTransition(const Transition tranReq)
137{
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000138 // HardReboot does not shutdown any services and immediately transitions
139 // into the reboot process
140 if (server::BMC::Transition::HardReboot == tranReq)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500141 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000142 auto method = this->bus.new_method_call(
143 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot");
144 try
145 {
146 this->bus.call(method);
147 }
Patrick Williams0a675212021-09-02 09:49:43 -0500148 catch (const sdbusplus::exception::exception& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000149 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500150 info("Error in HardReboot: {ERROR}", "ERROR", e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000151 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500152 }
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000153 else
Anthony Wilson32c532e2018-10-25 21:56:07 -0500154 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000155 // Check to make sure it can be found
156 auto iter = SYSTEMD_TABLE.find(tranReq);
157 if (iter == SYSTEMD_TABLE.end())
158 return;
Anthony Wilson32c532e2018-10-25 21:56:07 -0500159
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000160 const auto& sysdUnit = iter->second;
161
162 auto method = this->bus.new_method_call(
163 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit");
164 // The only valid transition is reboot and that
165 // needs to be irreversible once started
166
167 method.append(sysdUnit, "replace-irreversibly");
168
169 try
170 {
171 this->bus.call(method);
172 }
Patrick Williams0a675212021-09-02 09:49:43 -0500173 catch (const sdbusplus::exception::exception& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000174 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500175 info("Error in StartUnit - replace-irreversibly: {ERROR}", "ERROR",
176 e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000177 }
178 }
Josh D. King5162a7b2016-12-19 16:15:00 -0600179 return;
180}
181
Patrick Williamsd32f8182017-05-05 15:55:24 -0500182int BMC::bmcStateChange(sdbusplus::message::message& msg)
Josh D. Kingd613b812016-12-19 16:47:45 -0600183{
Andrew Geissler58a18012018-01-19 19:36:05 -0800184 uint32_t newStateID{};
Josh D. Kingd613b812016-12-19 16:47:45 -0600185 sdbusplus::message::object_path newStateObjPath;
186 std::string newStateUnit{};
187 std::string newStateResult{};
188
Andrew Geissler58a18012018-01-19 19:36:05 -0800189 // Read the msg and populate each variable
Patrick Williamsd32f8182017-05-05 15:55:24 -0500190 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Josh D. Kingd613b812016-12-19 16:47:45 -0600191
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600192 if ((newStateUnit == obmcQuiesceTarget) && (newStateResult == signalDone))
Josh D. Kingd613b812016-12-19 16:47:45 -0600193 {
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600194 error("BMC has entered BMC_QUIESCED state");
195 this->currentBMCState(BMCState::Quiesced);
Josh D. Kingd613b812016-12-19 16:47:45 -0600196
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600197 // There is no getting out of Quiesced once entered (other then BMC
198 // reboot) so stop watching for signals
Andrew Geissler58a18012018-01-19 19:36:05 -0800199 auto method =
200 this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
201 SYSTEMD_INTERFACE, "Unsubscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500202
203 try
204 {
205 this->bus.call(method);
206 this->stateSignal.release();
207 }
Patrick Williams0a675212021-09-02 09:49:43 -0500208 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500209 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500210 info("Error in Unsubscribe: {ERROR}", "ERROR", e);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500211 }
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600212
213 return 0;
214 }
215
216 // Caught the signal that indicates the BMC is now BMC_READY
217 if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone))
218 {
219 info("BMC_READY");
220 this->currentBMCState(BMCState::Ready);
Josh D. Kingd613b812016-12-19 16:47:45 -0600221 }
222
223 return 0;
224}
225
Josh D. King6db38222016-12-19 14:52:40 -0600226BMC::Transition BMC::requestedBMCTransition(Transition value)
227{
Andrew Geissler429100a2021-09-09 12:50:24 -0500228 info("Setting the RequestedBMCTransition field to "
229 "{REQUESTED_BMC_TRANSITION}",
230 "REQUESTED_BMC_TRANSITION", value);
Josh D. King6db38222016-12-19 14:52:40 -0600231
Josh D. King5162a7b2016-12-19 16:15:00 -0600232 executeTransition(value);
233 return server::BMC::requestedBMCTransition(value);
Josh D. King6db38222016-12-19 14:52:40 -0600234}
235
Josh D. Kingd613b812016-12-19 16:47:45 -0600236BMC::BMCState BMC::currentBMCState(BMCState value)
237{
Andrew Geissler429100a2021-09-09 12:50:24 -0500238
239 info("Setting the BMCState field to {CURRENT_BMC_STATE}",
240 "CURRENT_BMC_STATE", value);
Josh D. Kingd613b812016-12-19 16:47:45 -0600241
242 return server::BMC::currentBMCState(value);
243}
244
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800245BMC::RebootCause BMC::lastRebootCause(RebootCause value)
246{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500247 info("Setting the RebootCause field to {LAST_REBOOT_CAUSE}",
248 "LAST_REBOOT_CAUSE", value);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800249
250 return server::BMC::lastRebootCause(value);
251}
252
Matt Spinlere6710b72018-07-12 16:05:55 -0500253uint64_t BMC::lastRebootTime() const
254{
255 using namespace std::chrono;
256 struct sysinfo info;
257
258 auto rc = sysinfo(&info);
259 assert(rc == 0);
260
261 // Since uptime is in seconds, also get the current time in seconds.
262 auto now = time_point_cast<seconds>(system_clock::now());
263 auto rebootTime = now - seconds(info.uptime);
264
265 return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count();
266}
267
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800268void BMC::discoverLastRebootCause()
269{
270 uint64_t bootReason = 0;
271 std::ifstream file;
272 auto bootstatusPath = "/sys/class/watchdog/watchdog0/bootstatus";
273
274 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
275 std::ifstream::eofbit);
276
277 try
278 {
279 file.open(bootstatusPath);
280 file >> bootReason;
281 }
282 catch (const std::exception& e)
283 {
284 auto rc = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500285 error("Failed to read sysfs file {FILE} with errno {ERRNO}", "FILE",
286 bootstatusPath, "ERRNO", rc);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800287 }
288
289 switch (bootReason)
290 {
291 case WDIOF_EXTERN1:
292 this->lastRebootCause(RebootCause::Watchdog);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600293 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800294 case WDIOF_CARDRESET:
295 this->lastRebootCause(RebootCause::POR);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600296 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800297 default:
298 this->lastRebootCause(RebootCause::Unknown);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600299 // Continue below to see if more details can be found
300 // on reason for reboot
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800301 break;
302 }
303
Andrew Geissler98e64e62022-01-25 16:02:56 -0600304 // If the above code could not detect a reason, look for a the
305 // reset-cause-pinhole gpio to see if it is the reason for the reboot
306 auto gpioval =
307 phosphor::state::manager::utils::getGpioValue("reset-cause-pinhole");
308
Andrew Geissler2e352a22022-03-02 11:31:40 -0600309 // A 0 indicates a pinhole reset occurred
310 if (0 == gpioval)
Andrew Geissler98e64e62022-01-25 16:02:56 -0600311 {
312 info("The BMC reset was caused by a pinhole reset");
313 this->lastRebootCause(RebootCause::PinholeReset);
Andrew Geisslera2a7e122022-01-26 13:30:18 -0600314
315 // Generate log telling user a pinhole reset has occurred
316 const std::string errorMsg = "xyz.openbmc_project.State.PinholeReset";
317 phosphor::state::manager::utils::createError(
318 this->bus, errorMsg,
319 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
320 Notice);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600321 }
322
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800323 return;
324}
325
Josh D. Kingbdd9cb72016-12-19 11:13:43 -0600326} // namespace manager
327} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700328} // namespace phosphor