blob: efaa0a2e11ee2a1576d9088494494b00b55e24af [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
8#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler429100a2021-09-09 12:50:24 -05009#include <phosphor-logging/lg2.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050010#include <sdbusplus/exception.hpp>
11
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
Andrew Geissler429100a2021-09-09 12:50:24 -050023PHOSPHOR_LOG2_USING;
24
Josh D. King6db38222016-12-19 14:52:40 -060025// When you see server:: you know we're referencing our base class
Patrick Williams7e969cb2023-08-23 16:24:23 -050026namespace server = sdbusplus::server::xyz::openbmc_project::state;
Josh D. King6db38222016-12-19 14:52:40 -060027
28using namespace phosphor::logging;
Andrew Geissler2f60aae2019-09-12 13:25:21 -050029using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Josh D. King6db38222016-12-19 14:52:40 -060030
Andrew Geissler9eb0e442022-02-18 10:21:15 -060031constexpr auto obmcQuiesceTarget = "obmc-bmc-service-quiesce@0.target";
Anthony Wilsoneef31f82019-04-23 17:04:09 -050032constexpr auto obmcStandbyTarget = "multi-user.target";
Josh D. Kingd613b812016-12-19 16:47:45 -060033constexpr auto signalDone = "done";
Josh D. Kingd3e58472017-02-02 11:09:11 -060034constexpr auto activeState = "active";
Josh D. Kingd613b812016-12-19 16:47:45 -060035
Josh D. King5162a7b2016-12-19 16:15:00 -060036/* Map a transition to it's systemd target */
Andrew Geissler58a18012018-01-19 19:36:05 -080037const std::map<server::BMC::Transition, const char*> SYSTEMD_TABLE = {
38 {server::BMC::Transition::Reboot, "reboot.target"}};
Josh D. King5162a7b2016-12-19 16:15:00 -060039
Andrew Geissler58a18012018-01-19 19:36:05 -080040constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
41constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
42constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
Josh D. Kingd3e58472017-02-02 11:09:11 -060043constexpr auto SYSTEMD_PRP_INTERFACE = "org.freedesktop.DBus.Properties";
Josh D. Kingd3e58472017-02-02 11:09:11 -060044
Andrew Geissler2774c782022-02-17 16:57:14 -060045std::string BMC::getUnitState(const std::string& unitToCheck)
Josh D. Kingd3e58472017-02-02 11:09:11 -060046{
Patrick Williams2975e262020-05-13 18:01:09 -050047 std::variant<std::string> currentState;
Josh D. King2b5d8872017-02-21 13:37:17 -060048 sdbusplus::message::object_path unitTargetPath;
Josh D. Kingd3e58472017-02-02 11:09:11 -060049
Andrew Geissler58a18012018-01-19 19:36:05 -080050 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
51 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King2b5d8872017-02-21 13:37:17 -060052
Andrew Geissler2774c782022-02-17 16:57:14 -060053 method.append(unitToCheck);
Josh D. King2b5d8872017-02-21 13:37:17 -060054
Anthony Wilson32c532e2018-10-25 21:56:07 -050055 try
Josh D. King2b5d8872017-02-21 13:37:17 -060056 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050057 auto result = this->bus.call(method);
58 result.read(unitTargetPath);
59 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050060 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050061 {
Andrew Geissler37d36312022-03-09 16:24:45 -060062 // Not all input units will have been loaded yet so just return an
63 // empty string if an exception is caught in this path
64 info("Unit {UNIT} not found: {ERROR}", "UNIT", unitToCheck, "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
Andrew Geissler37d36312022-03-09 16:24:45 -060079 // Is input target active or inactive?
Anthony Wilson32c532e2018-10-25 21:56:07 -050080 result.read(currentState);
81 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050082 catch (const sdbusplus::exception_t& 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 // First look to see if the BMC quiesce target is active
93 auto currentStateStr = getUnitState(obmcQuiesceTarget);
94 if (currentStateStr == activeState)
95 {
96 info("Setting the BMCState field to BMC_QUIESCED");
97 this->currentBMCState(BMCState::Quiesced);
98 return;
99 }
100
101 // If not quiesced, then check standby target
102 currentStateStr = getUnitState(obmcStandbyTarget);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500103 if (currentStateStr == activeState)
Josh D. Kingd3e58472017-02-02 11:09:11 -0600104 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500105 info("Setting the BMCState field to BMC_READY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600106 this->currentBMCState(BMCState::Ready);
Josh D. Kingd3e58472017-02-02 11:09:11 -0600107 }
108 else
109 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500110 info("Setting the BMCState field to BMC_NOTREADY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600111 this->currentBMCState(BMCState::NotReady);
112 }
113
114 return;
115}
116
Josh D. King5162a7b2016-12-19 16:15:00 -0600117void BMC::executeTransition(const Transition tranReq)
118{
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000119 // HardReboot does not shutdown any services and immediately transitions
120 // into the reboot process
121 if (server::BMC::Transition::HardReboot == tranReq)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500122 {
Andrew Geissler4ee59462022-04-28 16:58:26 -0400123 // Put BMC state not NotReady when issuing a BMC reboot
124 // and stop monitoring for state changes
125 this->currentBMCState(BMCState::NotReady);
126 this->stateSignal.reset();
127
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000128 auto method = this->bus.new_method_call(
129 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot");
130 try
131 {
132 this->bus.call(method);
133 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500134 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000135 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500136 info("Error in HardReboot: {ERROR}", "ERROR", e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000137 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500138 }
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000139 else
Anthony Wilson32c532e2018-10-25 21:56:07 -0500140 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000141 // Check to make sure it can be found
142 auto iter = SYSTEMD_TABLE.find(tranReq);
143 if (iter == SYSTEMD_TABLE.end())
144 return;
Anthony Wilson32c532e2018-10-25 21:56:07 -0500145
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000146 const auto& sysdUnit = iter->second;
147
148 auto method = this->bus.new_method_call(
149 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit");
150 // The only valid transition is reboot and that
151 // needs to be irreversible once started
152
153 method.append(sysdUnit, "replace-irreversibly");
154
Andrew Geissler4ee59462022-04-28 16:58:26 -0400155 // Put BMC state not NotReady when issuing a BMC reboot
156 // and stop monitoring for state changes
157 this->currentBMCState(BMCState::NotReady);
158 this->stateSignal.reset();
159
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000160 try
161 {
162 this->bus.call(method);
163 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500164 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000165 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500166 info("Error in StartUnit - replace-irreversibly: {ERROR}", "ERROR",
167 e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000168 }
169 }
Josh D. King5162a7b2016-12-19 16:15:00 -0600170 return;
171}
172
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500173int BMC::bmcStateChange(sdbusplus::message_t& msg)
Josh D. Kingd613b812016-12-19 16:47:45 -0600174{
Andrew Geissler58a18012018-01-19 19:36:05 -0800175 uint32_t newStateID{};
Josh D. Kingd613b812016-12-19 16:47:45 -0600176 sdbusplus::message::object_path newStateObjPath;
177 std::string newStateUnit{};
178 std::string newStateResult{};
179
Andrew Geissler58a18012018-01-19 19:36:05 -0800180 // Read the msg and populate each variable
Patrick Williamsd32f8182017-05-05 15:55:24 -0500181 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Josh D. Kingd613b812016-12-19 16:47:45 -0600182
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600183 if ((newStateUnit == obmcQuiesceTarget) && (newStateResult == signalDone))
Josh D. Kingd613b812016-12-19 16:47:45 -0600184 {
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600185 error("BMC has entered BMC_QUIESCED state");
186 this->currentBMCState(BMCState::Quiesced);
Josh D. Kingd613b812016-12-19 16:47:45 -0600187
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600188 // There is no getting out of Quiesced once entered (other then BMC
189 // reboot) so stop watching for signals
Andrew Geissler58a18012018-01-19 19:36:05 -0800190 auto method =
191 this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
192 SYSTEMD_INTERFACE, "Unsubscribe");
Anthony Wilson32c532e2018-10-25 21:56:07 -0500193
194 try
195 {
196 this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500197 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500198 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500199 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500200 info("Error in Unsubscribe: {ERROR}", "ERROR", e);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500201 }
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600202
Andrew Geissler71e538f2022-03-21 11:22:43 -0500203 // disable the system state change object as well
NodeMan978c26f0e2022-04-27 20:15:22 -0500204 this->stateSignal.reset();
Andrew Geissler71e538f2022-03-21 11:22:43 -0500205
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600206 return 0;
207 }
208
209 // Caught the signal that indicates the BMC is now BMC_READY
210 if ((newStateUnit == obmcStandbyTarget) && (newStateResult == signalDone))
211 {
212 info("BMC_READY");
213 this->currentBMCState(BMCState::Ready);
Josh D. Kingd613b812016-12-19 16:47:45 -0600214 }
215
216 return 0;
217}
218
Josh D. King6db38222016-12-19 14:52:40 -0600219BMC::Transition BMC::requestedBMCTransition(Transition value)
220{
Andrew Geissler429100a2021-09-09 12:50:24 -0500221 info("Setting the RequestedBMCTransition field to "
222 "{REQUESTED_BMC_TRANSITION}",
223 "REQUESTED_BMC_TRANSITION", value);
Josh D. King6db38222016-12-19 14:52:40 -0600224
Josh D. King5162a7b2016-12-19 16:15:00 -0600225 executeTransition(value);
226 return server::BMC::requestedBMCTransition(value);
Josh D. King6db38222016-12-19 14:52:40 -0600227}
228
Josh D. Kingd613b812016-12-19 16:47:45 -0600229BMC::BMCState BMC::currentBMCState(BMCState value)
230{
Andrew Geissler429100a2021-09-09 12:50:24 -0500231 info("Setting the BMCState field to {CURRENT_BMC_STATE}",
232 "CURRENT_BMC_STATE", value);
Josh D. Kingd613b812016-12-19 16:47:45 -0600233
234 return server::BMC::currentBMCState(value);
235}
236
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800237BMC::RebootCause BMC::lastRebootCause(RebootCause value)
238{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500239 info("Setting the RebootCause field to {LAST_REBOOT_CAUSE}",
240 "LAST_REBOOT_CAUSE", value);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800241
242 return server::BMC::lastRebootCause(value);
243}
244
Willy Tubd1eebd2023-10-05 12:14:20 -0700245void BMC::updateLastRebootTime()
246{
247 using namespace std::chrono;
248 struct sysinfo info;
249
250 auto rc = sysinfo(&info);
251 assert(rc == 0);
252 // Since uptime is in seconds, also get the current time in seconds.
253 auto now = time_point_cast<seconds>(system_clock::now());
254 auto rebootTimeTs = now - seconds(info.uptime);
255 rebootTime =
256 duration_cast<milliseconds>(rebootTimeTs.time_since_epoch()).count();
257}
258
Matt Spinlere6710b72018-07-12 16:05:55 -0500259uint64_t BMC::lastRebootTime() const
260{
Willy Tu564eb4f2023-09-07 16:02:46 -0700261 return rebootTime;
Matt Spinlere6710b72018-07-12 16:05:55 -0500262}
263
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800264void BMC::discoverLastRebootCause()
265{
266 uint64_t bootReason = 0;
267 std::ifstream file;
268 auto bootstatusPath = "/sys/class/watchdog/watchdog0/bootstatus";
269
270 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
271 std::ifstream::eofbit);
272
273 try
274 {
275 file.open(bootstatusPath);
276 file >> bootReason;
277 }
278 catch (const std::exception& e)
279 {
280 auto rc = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500281 error("Failed to read sysfs file {FILE} with errno {ERRNO}", "FILE",
282 bootstatusPath, "ERRNO", rc);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800283 }
284
285 switch (bootReason)
286 {
287 case WDIOF_EXTERN1:
288 this->lastRebootCause(RebootCause::Watchdog);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600289 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800290 case WDIOF_CARDRESET:
291 this->lastRebootCause(RebootCause::POR);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600292 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800293 default:
294 this->lastRebootCause(RebootCause::Unknown);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600295 // Continue below to see if more details can be found
296 // on reason for reboot
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800297 break;
298 }
299
Andrew Geissler98e64e62022-01-25 16:02:56 -0600300 // If the above code could not detect a reason, look for a the
301 // reset-cause-pinhole gpio to see if it is the reason for the reboot
302 auto gpioval =
303 phosphor::state::manager::utils::getGpioValue("reset-cause-pinhole");
304
Andrew Geissler2e352a22022-03-02 11:31:40 -0600305 // A 0 indicates a pinhole reset occurred
306 if (0 == gpioval)
Andrew Geissler98e64e62022-01-25 16:02:56 -0600307 {
308 info("The BMC reset was caused by a pinhole reset");
309 this->lastRebootCause(RebootCause::PinholeReset);
Andrew Geisslera2a7e122022-01-26 13:30:18 -0600310
311 // Generate log telling user a pinhole reset has occurred
312 const std::string errorMsg = "xyz.openbmc_project.State.PinholeReset";
313 phosphor::state::manager::utils::createError(
314 this->bus, errorMsg,
Patrick Williams7e969cb2023-08-23 16:24:23 -0500315 sdbusplus::server::xyz::openbmc_project::logging::Entry::Level::
Andrew Geisslera2a7e122022-01-26 13:30:18 -0600316 Notice);
Andrew Geissler2038e492023-06-16 15:32:58 -0400317 return;
318 }
319
320 // If we still haven't found a reason, see if we lost AC power
321 // Note that a pinhole reset will remove AC power to the chassis
322 // on some systems so we always want to look for the pinhole reset
323 // first as that would be the main reason AC power was lost.
324 size_t chassisId = 0;
325 if (phosphor::state::manager::utils::checkACLoss(chassisId))
326 {
327 this->lastRebootCause(RebootCause::POR);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600328 }
329
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800330 return;
331}
332
Josh D. Kingbdd9cb72016-12-19 11:13:43 -0600333} // namespace manager
334} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700335} // namespace phosphor