blob: 849b6a870a004a9fb0da0903e3dc2fdadd6da466 [file] [log] [blame]
Thang Tran9f381522024-10-18 10:11:01 +07001#include "config.h"
2
Josh D. Kingbdd9cb72016-12-19 11:13:43 -06003#include "bmc_state_manager.hpp"
Andrew Geisslere426b582020-05-28 12:40:55 -05004
Andrew Geissler98e64e62022-01-25 16:02:56 -06005#include "utils.hpp"
Andrew Geissler2f60aae2019-09-12 13:25:21 -05006#include "xyz/openbmc_project/Common/error.hpp"
Josh D. Kingbdd9cb72016-12-19 11:13:43 -06007
Andrew Geissler98e64e62022-01-25 16:02:56 -06008#include <gpiod.h>
Andrew Geisslere426b582020-05-28 12:40:55 -05009
10#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler429100a2021-09-09 12:50:24 -050011#include <phosphor-logging/lg2.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050012#include <sdbusplus/exception.hpp>
13
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
Patrick Williams7e969cb2023-08-23 16:24:23 -050028namespace server = sdbusplus::server::xyz::openbmc_project::state;
Josh D. King6db38222016-12-19 14:52:40 -060029
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 Geissler4b6dc112025-06-02 13:31:57 -050047void BMC::bmcIsQuiesced()
48{
49 this->currentBMCState(BMCState::Quiesced);
50
51 // There is no getting out of Quiesced once entered (other then BMC
52 // reboot) so stop watching for signals
53 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
54 SYSTEMD_INTERFACE, "Unsubscribe");
55
56 try
57 {
58 this->bus.call(method);
59 }
60 catch (const sdbusplus::exception_t& e)
61 {
62 info("Error in Unsubscribe: {ERROR}", "ERROR", e);
63 }
64
65 // disable the system state change object as well
66 this->stateSignal.reset();
67}
68
Andrew Geissler2774c782022-02-17 16:57:14 -060069std::string BMC::getUnitState(const std::string& unitToCheck)
Josh D. Kingd3e58472017-02-02 11:09:11 -060070{
Patrick Williams2975e262020-05-13 18:01:09 -050071 std::variant<std::string> currentState;
Josh D. King2b5d8872017-02-21 13:37:17 -060072 sdbusplus::message::object_path unitTargetPath;
Josh D. Kingd3e58472017-02-02 11:09:11 -060073
Andrew Geissler58a18012018-01-19 19:36:05 -080074 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
75 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King2b5d8872017-02-21 13:37:17 -060076
Andrew Geissler2774c782022-02-17 16:57:14 -060077 method.append(unitToCheck);
Josh D. King2b5d8872017-02-21 13:37:17 -060078
Anthony Wilson32c532e2018-10-25 21:56:07 -050079 try
Josh D. King2b5d8872017-02-21 13:37:17 -060080 {
Anthony Wilson32c532e2018-10-25 21:56:07 -050081 auto result = this->bus.call(method);
82 result.read(unitTargetPath);
83 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050084 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050085 {
Andrew Geissler37d36312022-03-09 16:24:45 -060086 // Not all input units will have been loaded yet so just return an
87 // empty string if an exception is caught in this path
88 info("Unit {UNIT} not found: {ERROR}", "UNIT", unitToCheck, "ERROR", e);
Andrew Geissler2774c782022-02-17 16:57:14 -060089 return std::string{};
Josh D. King2b5d8872017-02-21 13:37:17 -060090 }
91
Andrew Geissler58a18012018-01-19 19:36:05 -080092 method = this->bus.new_method_call(
93 SYSTEMD_SERVICE,
94 static_cast<const std::string&>(unitTargetPath).c_str(),
95 SYSTEMD_PRP_INTERFACE, "Get");
Josh D. Kingd3e58472017-02-02 11:09:11 -060096
97 method.append("org.freedesktop.systemd1.Unit", "ActiveState");
98
Anthony Wilson32c532e2018-10-25 21:56:07 -050099 try
Josh D. King2b5d8872017-02-21 13:37:17 -0600100 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500101 auto result = this->bus.call(method);
102
Andrew Geissler37d36312022-03-09 16:24:45 -0600103 // Is input target active or inactive?
Anthony Wilson32c532e2018-10-25 21:56:07 -0500104 result.read(currentState);
105 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500106 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500107 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500108 info("Error in ActiveState Get: {ERROR}", "ERROR", e);
Andrew Geissler2774c782022-02-17 16:57:14 -0600109 return std::string{};
Josh D. King2b5d8872017-02-21 13:37:17 -0600110 }
Andrew Geissler2774c782022-02-17 16:57:14 -0600111 return (std::get<std::string>(currentState));
112}
Josh D. Kingd3e58472017-02-02 11:09:11 -0600113
Andrew Geissler2774c782022-02-17 16:57:14 -0600114void BMC::discoverInitialState()
115{
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600116 // First look to see if the BMC quiesce target is active
117 auto currentStateStr = getUnitState(obmcQuiesceTarget);
118 if (currentStateStr == activeState)
119 {
120 info("Setting the BMCState field to BMC_QUIESCED");
Andrew Geissler4b6dc112025-06-02 13:31:57 -0500121 bmcIsQuiesced();
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600122 return;
123 }
124
125 // If not quiesced, then check standby target
126 currentStateStr = getUnitState(obmcStandbyTarget);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500127 if (currentStateStr == activeState)
Josh D. Kingd3e58472017-02-02 11:09:11 -0600128 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500129 info("Setting the BMCState field to BMC_READY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600130 this->currentBMCState(BMCState::Ready);
Josh D. Kingd3e58472017-02-02 11:09:11 -0600131 }
132 else
133 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500134 info("Setting the BMCState field to BMC_NOTREADY");
Josh D. Kingd3e58472017-02-02 11:09:11 -0600135 this->currentBMCState(BMCState::NotReady);
136 }
137
138 return;
139}
140
Josh D. King5162a7b2016-12-19 16:15:00 -0600141void BMC::executeTransition(const Transition tranReq)
142{
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000143 // HardReboot does not shutdown any services and immediately transitions
144 // into the reboot process
145 if (server::BMC::Transition::HardReboot == tranReq)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500146 {
Andrew Geissler4ee59462022-04-28 16:58:26 -0400147 // Put BMC state not NotReady when issuing a BMC reboot
148 // and stop monitoring for state changes
149 this->currentBMCState(BMCState::NotReady);
150 this->stateSignal.reset();
151
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000152 auto method = this->bus.new_method_call(
153 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Reboot");
154 try
155 {
156 this->bus.call(method);
157 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500158 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000159 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500160 info("Error in HardReboot: {ERROR}", "ERROR", e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000161 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500162 }
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000163 else
Anthony Wilson32c532e2018-10-25 21:56:07 -0500164 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000165 // Check to make sure it can be found
166 auto iter = SYSTEMD_TABLE.find(tranReq);
167 if (iter == SYSTEMD_TABLE.end())
Pavithra Barithayab594ac12024-06-21 12:09:04 -0500168 {
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000169 return;
Pavithra Barithayab594ac12024-06-21 12:09:04 -0500170 }
Anthony Wilson32c532e2018-10-25 21:56:07 -0500171
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000172 const auto& sysdUnit = iter->second;
173
174 auto method = this->bus.new_method_call(
175 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "StartUnit");
176 // The only valid transition is reboot and that
177 // needs to be irreversible once started
178
179 method.append(sysdUnit, "replace-irreversibly");
180
Andrew Geissler4ee59462022-04-28 16:58:26 -0400181 // Put BMC state not NotReady when issuing a BMC reboot
182 // and stop monitoring for state changes
183 this->currentBMCState(BMCState::NotReady);
184 this->stateSignal.reset();
185
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000186 try
187 {
188 this->bus.call(method);
189 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500190 catch (const sdbusplus::exception_t& e)
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000191 {
Andrew Geissler429100a2021-09-09 12:50:24 -0500192 info("Error in StartUnit - replace-irreversibly: {ERROR}", "ERROR",
193 e);
Jayaprakash Mutyala44c223c2020-08-14 00:08:03 +0000194 }
195 }
Josh D. King5162a7b2016-12-19 16:15:00 -0600196 return;
197}
198
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500199int BMC::bmcStateChange(sdbusplus::message_t& msg)
Josh D. Kingd613b812016-12-19 16:47:45 -0600200{
Andrew Geissler58a18012018-01-19 19:36:05 -0800201 uint32_t newStateID{};
Josh D. Kingd613b812016-12-19 16:47:45 -0600202 sdbusplus::message::object_path newStateObjPath;
203 std::string newStateUnit{};
204 std::string newStateResult{};
205
Andrew Geissler58a18012018-01-19 19:36:05 -0800206 // Read the msg and populate each variable
Patrick Williamsd32f8182017-05-05 15:55:24 -0500207 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
Josh D. Kingd613b812016-12-19 16:47:45 -0600208
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600209 if ((newStateUnit == obmcQuiesceTarget) && (newStateResult == signalDone))
Josh D. Kingd613b812016-12-19 16:47:45 -0600210 {
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600211 error("BMC has entered BMC_QUIESCED state");
Andrew Geissler4b6dc112025-06-02 13:31:57 -0500212 bmcIsQuiesced();
Andrew Geissler9eb0e442022-02-18 10:21:15 -0600213 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
Thang Tran9f381522024-10-18 10:11:01 +0700232#ifdef CHECK_FWUPDATE_BEFORE_DO_TRANSITION
233 /*
234 * Do not do transition when the any firmware being updated
235 */
236 if ((server::BMC::Transition::Reboot == value) &&
237 (phosphor::state::manager::utils::isFirmwareUpdating(this->bus)))
238 {
239 info("Firmware being updated, reject the transition request");
240 throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
241 }
242#endif // CHECK_FWUPDATE_BEFORE_DO_TRANSITION
243
Josh D. King5162a7b2016-12-19 16:15:00 -0600244 executeTransition(value);
245 return server::BMC::requestedBMCTransition(value);
Josh D. King6db38222016-12-19 14:52:40 -0600246}
247
Josh D. Kingd613b812016-12-19 16:47:45 -0600248BMC::BMCState BMC::currentBMCState(BMCState value)
249{
Andrew Geissler429100a2021-09-09 12:50:24 -0500250 info("Setting the BMCState field to {CURRENT_BMC_STATE}",
251 "CURRENT_BMC_STATE", value);
Josh D. Kingd613b812016-12-19 16:47:45 -0600252
253 return server::BMC::currentBMCState(value);
254}
255
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800256BMC::RebootCause BMC::lastRebootCause(RebootCause value)
257{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500258 info("Setting the RebootCause field to {LAST_REBOOT_CAUSE}",
259 "LAST_REBOOT_CAUSE", value);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800260
261 return server::BMC::lastRebootCause(value);
262}
263
Willy Tubd1eebd2023-10-05 12:14:20 -0700264void BMC::updateLastRebootTime()
265{
266 using namespace std::chrono;
267 struct sysinfo info;
268
269 auto rc = sysinfo(&info);
270 assert(rc == 0);
271 // Since uptime is in seconds, also get the current time in seconds.
272 auto now = time_point_cast<seconds>(system_clock::now());
273 auto rebootTimeTs = now - seconds(info.uptime);
274 rebootTime =
275 duration_cast<milliseconds>(rebootTimeTs.time_since_epoch()).count();
Willy Tu47120842024-01-09 14:04:03 -0800276 server::BMC::lastRebootTime(rebootTime);
Willy Tubd1eebd2023-10-05 12:14:20 -0700277}
278
Matt Spinlere6710b72018-07-12 16:05:55 -0500279uint64_t BMC::lastRebootTime() const
280{
Willy Tu564eb4f2023-09-07 16:02:46 -0700281 return rebootTime;
Matt Spinlere6710b72018-07-12 16:05:55 -0500282}
283
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800284void BMC::discoverLastRebootCause()
285{
286 uint64_t bootReason = 0;
287 std::ifstream file;
Pavithra Barithaya319eda42024-06-21 11:54:43 -0500288 const auto* bootstatusPath = "/sys/class/watchdog/watchdog0/bootstatus";
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800289
290 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
291 std::ifstream::eofbit);
292
293 try
294 {
295 file.open(bootstatusPath);
296 file >> bootReason;
297 }
298 catch (const std::exception& e)
299 {
300 auto rc = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500301 error("Failed to read sysfs file {FILE} with errno {ERRNO}", "FILE",
302 bootstatusPath, "ERRNO", rc);
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800303 }
304
305 switch (bootReason)
306 {
307 case WDIOF_EXTERN1:
Tim Lee4ab59212024-09-05 09:51:02 +0800308 this->lastRebootCause(RebootCause::Software);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600309 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800310 case WDIOF_CARDRESET:
Tim Lee4ab59212024-09-05 09:51:02 +0800311 this->lastRebootCause(RebootCause::Watchdog);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600312 return;
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800313 default:
Tim Lee4ab59212024-09-05 09:51:02 +0800314 this->lastRebootCause(RebootCause::POR);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600315 // Continue below to see if more details can be found
316 // on reason for reboot
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800317 break;
318 }
319
Andrew Geissler98e64e62022-01-25 16:02:56 -0600320 // If the above code could not detect a reason, look for a the
321 // reset-cause-pinhole gpio to see if it is the reason for the reboot
322 auto gpioval =
323 phosphor::state::manager::utils::getGpioValue("reset-cause-pinhole");
324
Andrew Geissler2e352a22022-03-02 11:31:40 -0600325 // A 0 indicates a pinhole reset occurred
326 if (0 == gpioval)
Andrew Geissler98e64e62022-01-25 16:02:56 -0600327 {
328 info("The BMC reset was caused by a pinhole reset");
329 this->lastRebootCause(RebootCause::PinholeReset);
Andrew Geisslera2a7e122022-01-26 13:30:18 -0600330
331 // Generate log telling user a pinhole reset has occurred
332 const std::string errorMsg = "xyz.openbmc_project.State.PinholeReset";
333 phosphor::state::manager::utils::createError(
334 this->bus, errorMsg,
Patrick Williams7e969cb2023-08-23 16:24:23 -0500335 sdbusplus::server::xyz::openbmc_project::logging::Entry::Level::
Andrew Geisslera2a7e122022-01-26 13:30:18 -0600336 Notice);
Andrew Geissler2038e492023-06-16 15:32:58 -0400337 return;
338 }
339
340 // If we still haven't found a reason, see if we lost AC power
341 // Note that a pinhole reset will remove AC power to the chassis
342 // on some systems so we always want to look for the pinhole reset
343 // first as that would be the main reason AC power was lost.
344 size_t chassisId = 0;
345 if (phosphor::state::manager::utils::checkACLoss(chassisId))
346 {
347 this->lastRebootCause(RebootCause::POR);
Andrew Geissler98e64e62022-01-25 16:02:56 -0600348 }
349
Tim Lee2bfb1ef2021-03-17 09:50:35 +0800350 return;
351}
352
Josh D. Kingbdd9cb72016-12-19 11:13:43 -0600353} // namespace manager
354} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700355} // namespace phosphor