blob: 473329c665cda5efe040ff48ca08d10803b975ad [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 Williamsf053e6f2022-07-22 19:26:54 -050062 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050063 {
Andrew Geissler37d36312022-03-09 16:24:45 -060064 // Not all input units will have been loaded yet so just return an
65 // empty string if an exception is caught in this path
66 info("Unit {UNIT} not found: {ERROR}", "UNIT", unitToCheck, "ERROR", e);
Andrew Geissler2774c782022-02-17 16:57:14 -060067 return std::string{};
Josh D. King2b5d8872017-02-21 13:37:17 -060068 }
69
Andrew Geissler58a18012018-01-19 19:36:05 -080070 method = this->bus.new_method_call(
71 SYSTEMD_SERVICE,
72 static_cast<const std::string&>(unitTargetPath).c_str(),
73 SYSTEMD_PRP_INTERFACE, "Get");
Josh D. Kingd3e58472017-02-02 11:09:11 -060074
75 method.append("org.freedesktop.systemd1.Unit", "ActiveState");
76
Anthony Wilson32c532e2018-10-25 21:56:07 -050077 try
Josh D. King2b5d8872017-02-21 13:37:17 -060078 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050079 auto result = this->bus.call(method);
80
Andrew Geissler37d36312022-03-09 16:24:45 -060081 // Is input target active or inactive?
Anthony Wilson32c532e2018-10-25 21:56:07 -050082 result.read(currentState);
83 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050084 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050085 {
Andrew Geissler429100a2021-09-09 12:50:24 -050086 info("Error in ActiveState Get: {ERROR}", "ERROR", e);
Andrew Geissler2774c782022-02-17 16:57:14 -060087 return std::string{};
Josh D. King2b5d8872017-02-21 13:37:17 -060088 }
Andrew Geissler2774c782022-02-17 16:57:14 -060089 return (std::get<std::string>(currentState));
90}
Josh D. Kingd3e58472017-02-02 11:09:11 -060091
Andrew Geissler2774c782022-02-17 16:57:14 -060092void BMC::discoverInitialState()
93{
Andrew Geissler9eb0e442022-02-18 10:21:15 -060094 // First look to see if the BMC quiesce target is active
95 auto currentStateStr = getUnitState(obmcQuiesceTarget);
96 if (currentStateStr == activeState)
97 {
98 info("Setting the BMCState field to BMC_QUIESCED");
99 this->currentBMCState(BMCState::Quiesced);
100 return;
101 }
102
103 // If not quiesced, then check standby target
104 currentStateStr = getUnitState(obmcStandbyTarget);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500105 if (currentStateStr == activeState)
Josh D. Kingd3e58472017-02-02 11:09:11 -0600106 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500107 info("Setting the BMCState field to BMC_READY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600108 this->currentBMCState(BMCState::Ready);
Josh D. Kingd3e58472017-02-02 11:09:11 -0600109 }
110 else
111 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500112 info("Setting the BMCState field to BMC_NOTREADY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600113 this->currentBMCState(BMCState::NotReady);
114 }
115
116 return;
117}
118
Josh D. King6db38222016-12-19 14:52:40 -0600119void BMC::subscribeToSystemdSignals()
120{
Andrew Geissler58a18012018-01-19 19:36:05 -0800121 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
122 SYSTEMD_INTERFACE, "Subscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500123
124 try
125 {
126 this->bus.call(method);
127 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500128 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500129 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500130 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler2f60aae2019-09-12 13:25:21 -0500131 elog<InternalFailure>();
Anthony Wilson32c532e2018-10-25 21:56:07 -0500132 }
Josh D. King6db38222016-12-19 14:52:40 -0600133
134 return;
135}
136
Josh D. King5162a7b2016-12-19 16:15:00 -0600137void BMC::executeTransition(const Transition tranReq)
138{
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000139 // HardReboot does not shutdown any services and immediately transitions
140 // into the reboot process
141 if (server::BMC::Transition::HardReboot == tranReq)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500142 {
Andrew Geissler4ee59462022-04-28 16:58:26 -0400143 // Put BMC state not NotReady when issuing a BMC reboot
144 // and stop monitoring for state changes
145 this->currentBMCState(BMCState::NotReady);
146 this->stateSignal.reset();
147
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000148 auto method = this->bus.new_method_call(
149 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot");
150 try
151 {
152 this->bus.call(method);
153 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500154 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000155 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500156 info("Error in HardReboot: {ERROR}", "ERROR", e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000157 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500158 }
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000159 else
Anthony Wilson32c532e2018-10-25 21:56:07 -0500160 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000161 // Check to make sure it can be found
162 auto iter = SYSTEMD_TABLE.find(tranReq);
163 if (iter == SYSTEMD_TABLE.end())
164 return;
Anthony Wilson32c532e2018-10-25 21:56:07 -0500165
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000166 const auto& sysdUnit = iter->second;
167
168 auto method = this->bus.new_method_call(
169 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit");
170 // The only valid transition is reboot and that
171 // needs to be irreversible once started
172
173 method.append(sysdUnit, "replace-irreversibly");
174
Andrew Geissler4ee59462022-04-28 16:58:26 -0400175 // Put BMC state not NotReady when issuing a BMC reboot
176 // and stop monitoring for state changes
177 this->currentBMCState(BMCState::NotReady);
178 this->stateSignal.reset();
179
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000180 try
181 {
182 this->bus.call(method);
183 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500184 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000185 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500186 info("Error in StartUnit - replace-irreversibly: {ERROR}", "ERROR",
187 e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000188 }
189 }
Josh D. King5162a7b2016-12-19 16:15:00 -0600190 return;
191}
192
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500193int BMC::bmcStateChange(sdbusplus::message_t& msg)
Josh D. Kingd613b812016-12-19 16:47:45 -0600194{
Andrew Geissler58a18012018-01-19 19:36:05 -0800195 uint32_t newStateID{};
Josh D. Kingd613b812016-12-19 16:47:45 -0600196 sdbusplus::message::object_path newStateObjPath;
197 std::string newStateUnit{};
198 std::string newStateResult{};
199
Andrew Geissler58a18012018-01-19 19:36:05 -0800200 // Read the msg and populate each variable
Patrick Williamsd32f8182017-05-05 15:55:24 -0500201 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Josh D. Kingd613b812016-12-19 16:47:45 -0600202
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600203 if ((newStateUnit == obmcQuiesceTarget) && (newStateResult == signalDone))
Josh D. Kingd613b812016-12-19 16:47:45 -0600204 {
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600205 error("BMC has entered BMC_QUIESCED state");
206 this->currentBMCState(BMCState::Quiesced);
Josh D. Kingd613b812016-12-19 16:47:45 -0600207
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600208 // There is no getting out of Quiesced once entered (other then BMC
209 // reboot) so stop watching for signals
Andrew Geissler58a18012018-01-19 19:36:05 -0800210 auto method =
211 this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
212 SYSTEMD_INTERFACE, "Unsubscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500213
214 try
215 {
216 this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500217 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500218 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500219 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500220 info("Error in Unsubscribe: {ERROR}", "ERROR", e);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500221 }
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600222
Andrew Geissler71e538f2022-03-21 11:22:43 -0500223 // disable the system state change object as well
NodeMan978c26f0e2022-04-27 20:15:22 -0500224 this->stateSignal.reset();
Andrew Geissler71e538f2022-03-21 11:22:43 -0500225
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600226 return 0;
227 }
228
229 // Caught the signal that indicates the BMC is now BMC_READY
230 if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone))
231 {
232 info("BMC_READY");
233 this->currentBMCState(BMCState::Ready);
Josh D. Kingd613b812016-12-19 16:47:45 -0600234 }
235
236 return 0;
237}
238
Josh D. King6db38222016-12-19 14:52:40 -0600239BMC::Transition BMC::requestedBMCTransition(Transition value)
240{
Andrew Geissler429100a2021-09-09 12:50:24 -0500241 info("Setting the RequestedBMCTransition field to "
242 "{REQUESTED_BMC_TRANSITION}",
243 "REQUESTED_BMC_TRANSITION", value);
Josh D. King6db38222016-12-19 14:52:40 -0600244
Josh D. King5162a7b2016-12-19 16:15:00 -0600245 executeTransition(value);
246 return server::BMC::requestedBMCTransition(value);
Josh D. King6db38222016-12-19 14:52:40 -0600247}
248
Josh D. Kingd613b812016-12-19 16:47:45 -0600249BMC::BMCState BMC::currentBMCState(BMCState value)
250{
Andrew Geissler429100a2021-09-09 12:50:24 -0500251 info("Setting the BMCState field to {CURRENT_BMC_STATE}",
252 "CURRENT_BMC_STATE", value);
Josh D. Kingd613b812016-12-19 16:47:45 -0600253
254 return server::BMC::currentBMCState(value);
255}
256
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800257BMC::RebootCause BMC::lastRebootCause(RebootCause value)
258{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500259 info("Setting the RebootCause field to {LAST_REBOOT_CAUSE}",
260 "LAST_REBOOT_CAUSE", value);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800261
262 return server::BMC::lastRebootCause(value);
263}
264
Matt Spinlere6710b72018-07-12 16:05:55 -0500265uint64_t BMC::lastRebootTime() const
266{
267 using namespace std::chrono;
268 struct sysinfo info;
269
270 auto rc = sysinfo(&info);
271 assert(rc == 0);
272
273 // Since uptime is in seconds, also get the current time in seconds.
274 auto now = time_point_cast<seconds>(system_clock::now());
275 auto rebootTime = now - seconds(info.uptime);
276
277 return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count();
278}
279
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800280void BMC::discoverLastRebootCause()
281{
282 uint64_t bootReason = 0;
283 std::ifstream file;
284 auto bootstatusPath = "/sys/class/watchdog/watchdog0/bootstatus";
285
286 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
287 std::ifstream::eofbit);
288
289 try
290 {
291 file.open(bootstatusPath);
292 file >> bootReason;
293 }
294 catch (const std::exception& e)
295 {
296 auto rc = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500297 error("Failed to read sysfs file {FILE} with errno {ERRNO}", "FILE",
298 bootstatusPath, "ERRNO", rc);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800299 }
300
301 switch (bootReason)
302 {
303 case WDIOF_EXTERN1:
304 this->lastRebootCause(RebootCause::Watchdog);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600305 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800306 case WDIOF_CARDRESET:
307 this->lastRebootCause(RebootCause::POR);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600308 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800309 default:
310 this->lastRebootCause(RebootCause::Unknown);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600311 // Continue below to see if more details can be found
312 // on reason for reboot
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800313 break;
314 }
315
NodeMan9727d1e142022-07-27 15:10:07 -0500316 // If the above code could not detect a reason, check to see
317 // if an AC loss occured.
318 size_t chassisId = 0;
319 if (phosphor::state::manager::utils::checkACLoss(chassisId))
320 {
321 this->lastRebootCause(RebootCause::POR);
322 return;
323 }
324
Andrew Geissler98e64e62022-01-25 16:02:56 -0600325 // If the above code could not detect a reason, look for a the
326 // reset-cause-pinhole gpio to see if it is the reason for the reboot
327 auto gpioval =
328 phosphor::state::manager::utils::getGpioValue("reset-cause-pinhole");
329
Andrew Geissler2e352a22022-03-02 11:31:40 -0600330 // A 0 indicates a pinhole reset occurred
331 if (0 == gpioval)
Andrew Geissler98e64e62022-01-25 16:02:56 -0600332 {
333 info("The BMC reset was caused by a pinhole reset");
334 this->lastRebootCause(RebootCause::PinholeReset);
Andrew Geisslera2a7e122022-01-26 13:30:18 -0600335
336 // Generate log telling user a pinhole reset has occurred
337 const std::string errorMsg = "xyz.openbmc_project.State.PinholeReset";
338 phosphor::state::manager::utils::createError(
339 this->bus, errorMsg,
340 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
341 Notice);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600342 }
343
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800344 return;
345}
346
Josh D. Kingbdd9cb72016-12-19 11:13:43 -0600347} // namespace manager
348} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700349} // namespace phosphor