1 /*
2 // Copyright (c) 2018-2025 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #include "power_control.hpp"
17
18 #include <sys/sysinfo.h>
19 #include <systemd/sd-journal.h>
20
21 #include <boost/asio/io_context.hpp>
22 #include <boost/asio/posix/stream_descriptor.hpp>
23 #include <boost/asio/steady_timer.hpp>
24 #include <boost/container/flat_map.hpp>
25 #include <boost/container/flat_set.hpp>
26 #include <gpiod.hpp>
27 #include <nlohmann/json.hpp>
28 #include <phosphor-logging/lg2.hpp>
29 #include <sdbusplus/asio/object_server.hpp>
30 #include <sdbusplus/asio/property.hpp>
31
32 #include <filesystem>
33 #include <fstream>
34 #include <optional>
35 #include <regex>
36 #include <string_view>
37
38 namespace power_control
39 {
40 static boost::asio::io_context io;
41 std::shared_ptr<sdbusplus::asio::connection> conn;
42 PersistentState appState;
43 PowerRestoreController powerRestore(io);
44
45 static std::string node = "0";
46 static const std::string appName = "power-control";
47
48 enum class DbusConfigType
49 {
50 name = 1,
51 path,
52 interface,
53 property
54 };
55
56 // Mandatory config parameters for dbus inputs
57 boost::container::flat_map<DbusConfigType, std::string> dbusParams = {
58 {DbusConfigType::name, "DbusName"},
59 {DbusConfigType::path, "Path"},
60 {DbusConfigType::interface, "Interface"},
61 {DbusConfigType::property, "Property"}};
62
63 enum class ConfigType
64 {
65 GPIO = 1,
66 DBUS
67 };
68
69 struct ConfigData
70 {
71 std::string name;
72 std::string lineName;
73 std::string dbusName;
74 std::string path;
75 std::string interface;
76 std::optional<std::regex> matchRegex;
77 bool polarity;
78 ConfigType type;
79 };
80
81 static ConfigData powerOutConfig;
82 static ConfigData powerOkConfig;
83 static ConfigData resetOutConfig;
84 static ConfigData nmiOutConfig;
85 static ConfigData sioPwrGoodConfig;
86 static ConfigData sioOnControlConfig;
87 static ConfigData sioS5Config;
88 static ConfigData postCompleteConfig;
89 static ConfigData powerButtonConfig;
90 static ConfigData resetButtonConfig;
91 static ConfigData idButtonConfig;
92 static ConfigData nmiButtonConfig;
93 static ConfigData slotPowerConfig;
94 static ConfigData hpmStbyEnConfig;
95
96 // map for storing list of gpio parameters whose config are to be read from x86
97 // power control json config
98 boost::container::flat_map<std::string, ConfigData*> powerSignalMap = {
99 {"PowerOut", &powerOutConfig},
100 {"PowerOk", &powerOkConfig},
101 {"ResetOut", &resetOutConfig},
102 {"NMIOut", &nmiOutConfig},
103 {"SioPowerGood", &sioPwrGoodConfig},
104 {"SioOnControl", &sioOnControlConfig},
105 {"SIOS5", &sioS5Config},
106 {"PostComplete", &postCompleteConfig},
107 {"PowerButton", &powerButtonConfig},
108 {"ResetButton", &resetButtonConfig},
109 {"IdButton", &idButtonConfig},
110 {"NMIButton", &nmiButtonConfig},
111 {"SlotPower", &slotPowerConfig},
112 {"HpmStbyEn", &hpmStbyEnConfig}};
113
114 static std::string hostDbusName = "xyz.openbmc_project.State.Host";
115 static std::string chassisDbusName = "xyz.openbmc_project.State.Chassis";
116 static std::string osDbusName = "xyz.openbmc_project.State.OperatingSystem";
117 static std::string buttonDbusName = "xyz.openbmc_project.Chassis.Buttons";
118 static std::string nmiDbusName = "xyz.openbmc_project.Control.Host.NMI";
119 static std::string rstCauseDbusName =
120 "xyz.openbmc_project.Control.Host.RestartCause";
121 static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
122 static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
123 #ifdef CHASSIS_SYSTEM_RESET
124 static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSysIface;
125 static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSlotIface;
126 #endif
127 static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
128 static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
129 static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
130 static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
131 static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
132 static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
133 static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
134
135 static gpiod::line powerButtonMask;
136 static gpiod::line resetButtonMask;
137 static bool nmiButtonMasked = false;
138 #if IGNORE_SOFT_RESETS_DURING_POST
139 static bool ignoreNextSoftReset = false;
140 #endif
141
142 // This map contains all timer values that are to be read from json config
143 boost::container::flat_map<std::string, int> TimerMap = {
144 {"PowerPulseMs", 200},
145 {"ForceOffPulseMs", 15000},
146 {"ResetPulseMs", 500},
147 {"PowerCycleMs", 5000},
148 {"SioPowerGoodWatchdogMs", 1000},
149 {"PowerOKWatchdogMs", 8000},
150 {"GracefulPowerOffS", (5 * 60)},
151 {"WarmResetCheckMs", 500},
152 {"PowerOffSaveMs", 7000},
153 {"SlotPowerCycleMs", 200},
154 {"DbusGetPropertyRetry", 1000}};
155
156 static bool nmiEnabled = true;
157 static bool nmiWhenPoweredOff = true;
158 static bool sioEnabled = true;
159
160 // Timers
161 // Time holding GPIOs asserted
162 static boost::asio::steady_timer gpioAssertTimer(io);
163 // Time between off and on during a power cycle
164 static boost::asio::steady_timer powerCycleTimer(io);
165 // Time OS gracefully powering off
166 static boost::asio::steady_timer gracefulPowerOffTimer(io);
167 // Time the warm reset check
168 static boost::asio::steady_timer warmResetCheckTimer(io);
169 // Time power OK assertion on power-on
170 static boost::asio::steady_timer powerOKWatchdogTimer(io);
171 // Time SIO power good assertion on power-on
172 static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
173 // Time power-off state save for power loss tracking
174 static boost::asio::steady_timer powerStateSaveTimer(io);
175 // POH timer
176 static boost::asio::steady_timer pohCounterTimer(io);
177 // Time when to allow restart cause updates
178 static boost::asio::steady_timer restartCauseTimer(io);
179 static boost::asio::steady_timer slotPowerCycleTimer(io);
180
181 // Map containing timers used for D-Bus get-property retries
182 static boost::container::flat_map<std::string, boost::asio::steady_timer>
183 dBusRetryTimers;
184
185 // GPIO Lines and Event Descriptors
186 static gpiod::line powerOKLine;
187 static boost::asio::posix::stream_descriptor powerOKEvent(io);
188 static gpiod::line sioPowerGoodLine;
189 static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
190 static gpiod::line sioOnControlLine;
191 static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
192 static gpiod::line sioS5Line;
193 static boost::asio::posix::stream_descriptor sioS5Event(io);
194 static gpiod::line powerButtonLine;
195 static boost::asio::posix::stream_descriptor powerButtonEvent(io);
196 static gpiod::line resetButtonLine;
197 static boost::asio::posix::stream_descriptor resetButtonEvent(io);
198 static gpiod::line nmiButtonLine;
199 static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
200 static gpiod::line idButtonLine;
201 static boost::asio::posix::stream_descriptor idButtonEvent(io);
202 static gpiod::line postCompleteLine;
203 static boost::asio::posix::stream_descriptor postCompleteEvent(io);
204 static gpiod::line nmiOutLine;
205 static gpiod::line slotPowerLine;
206
207 static constexpr uint8_t beepPowerFail = 8;
208
beep(const uint8_t & beepPriority)209 static void beep(const uint8_t& beepPriority)
210 {
211 lg2::info("Beep with priority: {BEEP_PRIORITY}", "BEEP_PRIORITY",
212 beepPriority);
213
214 conn->async_method_call(
215 [](boost::system::error_code ec) {
216 if (ec)
217 {
218 lg2::error(
219 "beep returned error with async_method_call (ec = {ERROR_MSG})",
220 "ERROR_MSG", ec.message());
221 return;
222 }
223 },
224 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
225 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
226 }
227
228 enum class OperatingSystemStateStage
229 {
230 Inactive,
231 Standby,
232 };
233 static OperatingSystemStateStage operatingSystemState;
getOperatingSystemStateStage(const OperatingSystemStateStage stage)234 static constexpr std::string_view getOperatingSystemStateStage(
235 const OperatingSystemStateStage stage)
236 {
237 switch (stage)
238 {
239 case OperatingSystemStateStage::Inactive:
240 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
241 case OperatingSystemStateStage::Standby:
242 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby";
243 default:
244 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
245 }
246 };
setOperatingSystemState(const OperatingSystemStateStage stage)247 static void setOperatingSystemState(const OperatingSystemStateStage stage)
248 {
249 if (!osIface)
250 {
251 return;
252 }
253
254 operatingSystemState = stage;
255 #if IGNORE_SOFT_RESETS_DURING_POST
256 // If POST complete has asserted set ignoreNextSoftReset to false to avoid
257 // masking soft resets after POST
258 if (operatingSystemState == OperatingSystemStateStage::Standby)
259 {
260 ignoreNextSoftReset = false;
261 }
262 #endif
263 osIface->set_property("OperatingSystemState",
264 std::string(getOperatingSystemStateStage(stage)));
265
266 lg2::info("Moving os state to {STATE} stage", "STATE",
267 getOperatingSystemStateStage(stage));
268 }
269
270 enum class PowerState
271 {
272 on,
273 waitForPowerOK,
274 waitForSIOPowerGood,
275 off,
276 transitionToOff,
277 gracefulTransitionToOff,
278 cycleOff,
279 transitionToCycleOff,
280 gracefulTransitionToCycleOff,
281 checkForWarmReset,
282 };
283 static PowerState powerState;
getPowerStateName(PowerState state)284 static constexpr std::string_view getPowerStateName(PowerState state)
285 {
286 switch (state)
287 {
288 case PowerState::on:
289 return "On";
290 case PowerState::waitForPowerOK:
291 return "Wait for Power OK";
292 case PowerState::waitForSIOPowerGood:
293 return "Wait for SIO Power Good";
294 case PowerState::off:
295 return "Off";
296 case PowerState::transitionToOff:
297 return "Transition to Off";
298 case PowerState::gracefulTransitionToOff:
299 return "Graceful Transition to Off";
300 case PowerState::cycleOff:
301 return "Power Cycle Off";
302 case PowerState::transitionToCycleOff:
303 return "Transition to Power Cycle Off";
304 case PowerState::gracefulTransitionToCycleOff:
305 return "Graceful Transition to Power Cycle Off";
306 case PowerState::checkForWarmReset:
307 return "Check for Warm Reset";
308 }
309 return {};
310 }
logStateTransition(const PowerState state)311 static void logStateTransition(const PowerState state)
312 {
313 std::string_view stateName = getPowerStateName(state);
314 if (stateName.empty())
315 {
316 lg2::error("Host{HOST}: Unknown power state: {STATE_INT}", "HOST", node,
317 "STATE_INT", static_cast<int>(state));
318 }
319 else
320 {
321 lg2::info("Host{HOST}: Moving to \"{STATE}\" state", "HOST", node,
322 "STATE", stateName);
323 }
324 }
325
326 enum class Event
327 {
328 powerOKAssert,
329 powerOKDeAssert,
330 sioPowerGoodAssert,
331 sioPowerGoodDeAssert,
332 sioS5Assert,
333 sioS5DeAssert,
334 pltRstAssert,
335 pltRstDeAssert,
336 postCompleteAssert,
337 postCompleteDeAssert,
338 powerButtonPressed,
339 resetButtonPressed,
340 powerCycleTimerExpired,
341 powerOKWatchdogTimerExpired,
342 sioPowerGoodWatchdogTimerExpired,
343 gracefulPowerOffTimerExpired,
344 powerOnRequest,
345 powerOffRequest,
346 powerCycleRequest,
347 resetRequest,
348 gracefulPowerOffRequest,
349 gracefulPowerCycleRequest,
350 warmResetDetected,
351 };
getEventName(Event event)352 static constexpr std::string_view getEventName(Event event)
353 {
354 switch (event)
355 {
356 case Event::powerOKAssert:
357 return "power OK assert";
358 case Event::powerOKDeAssert:
359 return "power OK de-assert";
360 case Event::sioPowerGoodAssert:
361 return "SIO power good assert";
362 case Event::sioPowerGoodDeAssert:
363 return "SIO power good de-assert";
364 case Event::sioS5Assert:
365 return "SIO S5 assert";
366 case Event::sioS5DeAssert:
367 return "SIO S5 de-assert";
368 case Event::pltRstAssert:
369 return "PLT_RST assert";
370 case Event::pltRstDeAssert:
371 return "PLT_RST de-assert";
372 case Event::postCompleteAssert:
373 return "POST Complete assert";
374 case Event::postCompleteDeAssert:
375 return "POST Complete de-assert";
376 case Event::powerButtonPressed:
377 return "power button pressed";
378 case Event::resetButtonPressed:
379 return "reset button pressed";
380 case Event::powerCycleTimerExpired:
381 return "power cycle timer expired";
382 case Event::powerOKWatchdogTimerExpired:
383 return "power OK watchdog timer expired";
384 case Event::sioPowerGoodWatchdogTimerExpired:
385 return "SIO power good watchdog timer expired";
386 case Event::gracefulPowerOffTimerExpired:
387 return "graceful power-off timer expired";
388 case Event::powerOnRequest:
389 return "power-on request";
390 case Event::powerOffRequest:
391 return "power-off request";
392 case Event::powerCycleRequest:
393 return "power-cycle request";
394 case Event::resetRequest:
395 return "reset request";
396 case Event::gracefulPowerOffRequest:
397 return "graceful power-off request";
398 case Event::gracefulPowerCycleRequest:
399 return "graceful power-cycle request";
400 case Event::warmResetDetected:
401 return "warm reset detected";
402 }
403 return {};
404 }
logEvent(const std::string_view stateHandler,const Event event)405 static void logEvent(const std::string_view stateHandler, const Event event)
406 {
407 std::string_view eventName = getEventName(event);
408 if (eventName.empty())
409 {
410 lg2::error("{STATE_HANDLER}: Unknown event received: {EVENT_INT}",
411 "STATE_HANDLER", stateHandler, "EVENT_INT",
412 static_cast<int>(event));
413 }
414 else
415 {
416 lg2::info("{STATE_HANDLER}: {EVENT} event received", "STATE_HANDLER",
417 stateHandler, "EVENT", eventName);
418 }
419 }
420
421 // Power state handlers
422 static void powerStateOn(const Event event);
423 static void powerStateWaitForPowerOK(const Event event);
424 static void powerStateWaitForSIOPowerGood(const Event event);
425 static void powerStateOff(const Event event);
426 static void powerStateTransitionToOff(const Event event);
427 static void powerStateGracefulTransitionToOff(const Event event);
428 static void powerStateCycleOff(const Event event);
429 static void powerStateTransitionToCycleOff(const Event event);
430 static void powerStateGracefulTransitionToCycleOff(const Event event);
431 static void powerStateCheckForWarmReset(const Event event);
432
getPowerStateHandler(PowerState state)433 static std::function<void(const Event)> getPowerStateHandler(PowerState state)
434 {
435 switch (state)
436 {
437 case PowerState::on:
438 return powerStateOn;
439 case PowerState::waitForPowerOK:
440 return powerStateWaitForPowerOK;
441 case PowerState::waitForSIOPowerGood:
442 return powerStateWaitForSIOPowerGood;
443 case PowerState::off:
444 return powerStateOff;
445 case PowerState::transitionToOff:
446 return powerStateTransitionToOff;
447 case PowerState::gracefulTransitionToOff:
448 return powerStateGracefulTransitionToOff;
449 case PowerState::cycleOff:
450 return powerStateCycleOff;
451 case PowerState::transitionToCycleOff:
452 return powerStateTransitionToCycleOff;
453 case PowerState::gracefulTransitionToCycleOff:
454 return powerStateGracefulTransitionToCycleOff;
455 case PowerState::checkForWarmReset:
456 return powerStateCheckForWarmReset;
457 default:
458 return nullptr;
459 }
460 };
461
sendPowerControlEvent(const Event event)462 static void sendPowerControlEvent(const Event event)
463 {
464 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
465 if (handler == nullptr)
466 {
467 lg2::error("Failed to find handler for power state: {STATE}", "STATE",
468 static_cast<int>(powerState));
469 return;
470 }
471 handler(event);
472 }
473
getCurrentTimeMs()474 static uint64_t getCurrentTimeMs()
475 {
476 struct timespec time = {};
477
478 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
479 {
480 return 0;
481 }
482 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
483 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
484
485 return currentTimeMs;
486 }
487
getHostState(const PowerState state)488 static constexpr std::string_view getHostState(const PowerState state)
489 {
490 switch (state)
491 {
492 case PowerState::on:
493 case PowerState::gracefulTransitionToOff:
494 case PowerState::gracefulTransitionToCycleOff:
495 return "xyz.openbmc_project.State.Host.HostState.Running";
496 case PowerState::waitForPowerOK:
497 case PowerState::waitForSIOPowerGood:
498 case PowerState::off:
499 case PowerState::transitionToOff:
500 case PowerState::transitionToCycleOff:
501 case PowerState::cycleOff:
502 case PowerState::checkForWarmReset:
503 return "xyz.openbmc_project.State.Host.HostState.Off";
504 default:
505 return "";
506 }
507 };
getChassisState(const PowerState state)508 static constexpr std::string_view getChassisState(const PowerState state)
509 {
510 switch (state)
511 {
512 case PowerState::on:
513 case PowerState::transitionToOff:
514 case PowerState::gracefulTransitionToOff:
515 case PowerState::transitionToCycleOff:
516 case PowerState::gracefulTransitionToCycleOff:
517 case PowerState::checkForWarmReset:
518 return "xyz.openbmc_project.State.Chassis.PowerState.On";
519 case PowerState::waitForPowerOK:
520 case PowerState::waitForSIOPowerGood:
521 case PowerState::off:
522 case PowerState::cycleOff:
523 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
524 default:
525 return "";
526 }
527 };
528 #ifdef CHASSIS_SYSTEM_RESET
529 enum class SlotPowerState
530 {
531 on,
532 off,
533 };
534 static SlotPowerState slotPowerState;
getSlotState(const SlotPowerState state)535 static constexpr std::string_view getSlotState(const SlotPowerState state)
536 {
537 switch (state)
538 {
539 case SlotPowerState::on:
540 return "xyz.openbmc_project.State.Chassis.PowerState.On";
541 case SlotPowerState::off:
542 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
543 default:
544 return "";
545 }
546 };
setSlotPowerState(const SlotPowerState state)547 static void setSlotPowerState(const SlotPowerState state)
548 {
549 slotPowerState = state;
550 chassisSlotIface->set_property("CurrentPowerState",
551 std::string(getSlotState(slotPowerState)));
552 chassisSlotIface->set_property("LastStateChangeTime", getCurrentTimeMs());
553 }
554 #endif
savePowerState(const PowerState state)555 static void savePowerState(const PowerState state)
556 {
557 powerStateSaveTimer.expires_after(
558 std::chrono::milliseconds(TimerMap["PowerOffSaveMs"]));
559 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
560 if (ec)
561 {
562 // operation_aborted is expected if timer is canceled before
563 // completion.
564 if (ec != boost::asio::error::operation_aborted)
565 {
566 lg2::error("Power-state save async_wait failed: {ERROR_MSG}",
567 "ERROR_MSG", ec.message());
568 }
569 return;
570 }
571 appState.set(PersistentState::Params::PowerState,
572 std::string{getChassisState(state)});
573 });
574 }
setPowerState(const PowerState state)575 static void setPowerState(const PowerState state)
576 {
577 powerState = state;
578 logStateTransition(state);
579
580 hostIface->set_property("CurrentHostState",
581 std::string(getHostState(powerState)));
582
583 chassisIface->set_property("CurrentPowerState",
584 std::string(getChassisState(powerState)));
585 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
586
587 // Save the power state for the restore policy
588 savePowerState(state);
589 }
590
591 enum class RestartCause
592 {
593 command,
594 resetButton,
595 powerButton,
596 watchdog,
597 powerPolicyOn,
598 powerPolicyRestore,
599 softReset,
600 };
601 static boost::container::flat_set<RestartCause> causeSet;
getRestartCause(RestartCause cause)602 static std::string getRestartCause(RestartCause cause)
603 {
604 switch (cause)
605 {
606 case RestartCause::command:
607 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
608 case RestartCause::resetButton:
609 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
610 case RestartCause::powerButton:
611 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
612 case RestartCause::watchdog:
613 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
614 case RestartCause::powerPolicyOn:
615 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn";
616 case RestartCause::powerPolicyRestore:
617 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyPreviousState";
618 case RestartCause::softReset:
619 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
620 default:
621 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
622 }
623 }
addRestartCause(const RestartCause cause)624 static void addRestartCause(const RestartCause cause)
625 {
626 // Add this to the set of causes for this restart
627 causeSet.insert(cause);
628 }
clearRestartCause()629 static void clearRestartCause()
630 {
631 // Clear the set for the next restart
632 causeSet.clear();
633 }
setRestartCauseProperty(const std::string & cause)634 static void setRestartCauseProperty(const std::string& cause)
635 {
636 if (!restartCauseIface)
637 {
638 return;
639 }
640 lg2::info("RestartCause set to {RESTART_CAUSE}", "RESTART_CAUSE", cause);
641 restartCauseIface->set_property("RestartCause", cause);
642 }
643
644 #ifdef USE_ACBOOT
resetACBootProperty()645 static void resetACBootProperty()
646 {
647 if ((causeSet.contains(RestartCause::command)) ||
648 (causeSet.contains(RestartCause::softReset)))
649 {
650 conn->async_method_call(
651 [](boost::system::error_code ec) {
652 if (ec)
653 {
654 lg2::error("failed to reset ACBoot property");
655 }
656 },
657 "xyz.openbmc_project.Settings",
658 "/xyz/openbmc_project/control/host0/ac_boot",
659 "org.freedesktop.DBus.Properties", "Set",
660 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
661 std::variant<std::string>{"False"});
662 }
663 }
664 #endif // USE_ACBOOT
665
setRestartCause()666 static void setRestartCause()
667 {
668 // Determine the actual restart cause based on the set of causes
669 std::string restartCause =
670 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
671 if (causeSet.contains(RestartCause::watchdog))
672 {
673 restartCause = getRestartCause(RestartCause::watchdog);
674 }
675 else if (causeSet.contains(RestartCause::command))
676 {
677 restartCause = getRestartCause(RestartCause::command);
678 }
679 else if (causeSet.contains(RestartCause::resetButton))
680 {
681 restartCause = getRestartCause(RestartCause::resetButton);
682 }
683 else if (causeSet.contains(RestartCause::powerButton))
684 {
685 restartCause = getRestartCause(RestartCause::powerButton);
686 }
687 else if (causeSet.contains(RestartCause::powerPolicyOn))
688 {
689 restartCause = getRestartCause(RestartCause::powerPolicyOn);
690 }
691 else if (causeSet.contains(RestartCause::powerPolicyRestore))
692 {
693 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
694 }
695 else if (causeSet.contains(RestartCause::softReset))
696 {
697 #if IGNORE_SOFT_RESETS_DURING_POST
698 if (ignoreNextSoftReset)
699 {
700 ignoreNextSoftReset = false;
701 return;
702 }
703 #endif
704 restartCause = getRestartCause(RestartCause::softReset);
705 }
706
707 setRestartCauseProperty(restartCause);
708 }
709
systemPowerGoodFailedLog()710 static void systemPowerGoodFailedLog()
711 {
712 sd_journal_send(
713 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
714 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
715 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
716 TimerMap["SioPowerGoodWatchdogMs"], NULL);
717 }
718
powerOKFailedLog()719 static void powerOKFailedLog()
720 {
721 sd_journal_send("MESSAGE=PowerControl: power okay failed to assert",
722 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
723 "OpenBMC.0.1.SystemPowerOnFailed", NULL);
724 }
725
powerRestorePolicyLog()726 static void powerRestorePolicyLog()
727 {
728 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
729 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
730 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
731 }
732
powerButtonPressLog()733 static void powerButtonPressLog()
734 {
735 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
736 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
737 "OpenBMC.0.1.PowerButtonPressed", NULL);
738 }
739
resetButtonPressLog()740 static void resetButtonPressLog()
741 {
742 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
743 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
744 "OpenBMC.0.1.ResetButtonPressed", NULL);
745 }
746
nmiButtonPressLog()747 static void nmiButtonPressLog()
748 {
749 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
750 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
751 "OpenBMC.0.1.NMIButtonPressed", NULL);
752 }
753
nmiDiagIntLog()754 static void nmiDiagIntLog()
755 {
756 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
757 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
758 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
759 }
760
PersistentState()761 PersistentState::PersistentState()
762 {
763 // create the power control directory if it doesn't exist
764 std::error_code ec;
765 if (!(std::filesystem::create_directories(powerControlDir, ec)))
766 {
767 if (ec.value() != 0)
768 {
769 lg2::error("failed to create {DIR_NAME}: {ERROR_MSG}", "DIR_NAME",
770 powerControlDir.string(), "ERROR_MSG", ec.message());
771 throw std::runtime_error("Failed to create state directory");
772 }
773 }
774
775 // read saved state, it's ok, if the file doesn't exists
776 std::ifstream appStateStream(powerControlDir / stateFile);
777 if (!appStateStream.is_open())
778 {
779 lg2::info("Cannot open state file \'{PATH}\'", "PATH",
780 std::string(powerControlDir / stateFile));
781 stateData = nlohmann::json({});
782 return;
783 }
784 try
785 {
786 appStateStream >> stateData;
787 if (stateData.is_discarded())
788 {
789 lg2::info("Cannot parse state file \'{PATH}\'", "PATH",
790 std::string(powerControlDir / stateFile));
791 stateData = nlohmann::json({});
792 return;
793 }
794 }
795 catch (const std::exception& ex)
796 {
797 lg2::info("Cannot read state file \'{PATH}\'", "PATH",
798 std::string(powerControlDir / stateFile));
799 stateData = nlohmann::json({});
800 return;
801 }
802 }
~PersistentState()803 PersistentState::~PersistentState()
804 {
805 saveState();
806 }
get(Params parameter)807 const std::string PersistentState::get(Params parameter)
808 {
809 auto val = stateData.find(getName(parameter));
810 if (val != stateData.end())
811 {
812 return val->get<std::string>();
813 }
814 return getDefault(parameter);
815 }
set(Params parameter,const std::string & value)816 void PersistentState::set(Params parameter, const std::string& value)
817 {
818 stateData[getName(parameter)] = value;
819 saveState();
820 }
821
getName(const Params parameter)822 const std::string PersistentState::getName(const Params parameter)
823 {
824 switch (parameter)
825 {
826 case Params::PowerState:
827 return "PowerState";
828 }
829 return "";
830 }
getDefault(const Params parameter)831 const std::string PersistentState::getDefault(const Params parameter)
832 {
833 switch (parameter)
834 {
835 case Params::PowerState:
836 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
837 }
838 return "";
839 }
saveState()840 void PersistentState::saveState()
841 {
842 std::ofstream appStateStream(powerControlDir / stateFile, std::ios::trunc);
843 if (!appStateStream.is_open())
844 {
845 lg2::error("Cannot write state file \'{PATH}\'", "PATH",
846 std::string(powerControlDir / stateFile));
847 return;
848 }
849 appStateStream << stateData.dump(indentationSize);
850 }
851
852 static constexpr const char* setingsService = "xyz.openbmc_project.Settings";
853 static constexpr const char* powerRestorePolicyIface =
854 "xyz.openbmc_project.Control.Power.RestorePolicy";
855 #ifdef USE_ACBOOT
856 static constexpr const char* powerACBootObject =
857 "/xyz/openbmc_project/control/host0/ac_boot";
858 static constexpr const char* powerACBootIface =
859 "xyz.openbmc_project.Common.ACBoot";
860 #endif // USE_ACBOOT
861
862 namespace match_rules = sdbusplus::bus::match::rules;
863
powerRestoreConfigHandler(sd_bus_message * m,void * context,sd_bus_error *)864 static int powerRestoreConfigHandler(sd_bus_message* m, void* context,
865 sd_bus_error*)
866 {
867 if (context == nullptr || m == nullptr)
868 {
869 throw std::runtime_error("Invalid match");
870 }
871 sdbusplus::message_t message(m);
872 PowerRestoreController* powerRestore =
873 static_cast<PowerRestoreController*>(context);
874
875 if (std::string(message.get_member()) == "InterfacesAdded")
876 {
877 sdbusplus::message::object_path path;
878 boost::container::flat_map<std::string, dbusPropertiesList> data;
879
880 message.read(path, data);
881
882 for (auto& [iface, properties] : data)
883 {
884 if ((iface == powerRestorePolicyIface)
885 #ifdef USE_ACBOOT
886 || (iface == powerACBootIface)
887 #endif // USE_ACBOOT
888 )
889 {
890 powerRestore->setProperties(properties);
891 }
892 }
893 }
894 else if (std::string(message.get_member()) == "PropertiesChanged")
895 {
896 std::string interfaceName;
897 dbusPropertiesList propertiesChanged;
898
899 message.read(interfaceName, propertiesChanged);
900
901 powerRestore->setProperties(propertiesChanged);
902 }
903 return 1;
904 }
905
run()906 void PowerRestoreController::run()
907 {
908 std::string powerRestorePolicyObject =
909 "/xyz/openbmc_project/control/host" + node + "/power_restore_policy";
910 powerRestorePolicyLog();
911 // this list only needs to be created once
912 if (matches.empty())
913 {
914 matches.emplace_back(
915 *conn,
916 match_rules::interfacesAdded() +
917 match_rules::argNpath(0, powerRestorePolicyObject) +
918 match_rules::sender(setingsService),
919 powerRestoreConfigHandler, this);
920 #ifdef USE_ACBOOT
921 matches.emplace_back(*conn,
922 match_rules::interfacesAdded() +
923 match_rules::argNpath(0, powerACBootObject) +
924 match_rules::sender(setingsService),
925 powerRestoreConfigHandler, this);
926 matches.emplace_back(*conn,
927 match_rules::propertiesChanged(powerACBootObject,
928 powerACBootIface) +
929 match_rules::sender(setingsService),
930 powerRestoreConfigHandler, this);
931 #endif // USE_ACBOOT
932 }
933
934 // Check if it's already on DBus
935 conn->async_method_call(
936 [this](boost::system::error_code ec,
937 const dbusPropertiesList properties) {
938 if (ec)
939 {
940 return;
941 }
942 setProperties(properties);
943 },
944 setingsService, powerRestorePolicyObject,
945 "org.freedesktop.DBus.Properties", "GetAll", powerRestorePolicyIface);
946
947 #ifdef USE_ACBOOT
948 // Check if it's already on DBus
949 conn->async_method_call(
950 [this](boost::system::error_code ec,
951 const dbusPropertiesList properties) {
952 if (ec)
953 {
954 return;
955 }
956 setProperties(properties);
957 },
958 setingsService, powerACBootObject, "org.freedesktop.DBus.Properties",
959 "GetAll", powerACBootIface);
960 #endif
961 }
962
setProperties(const dbusPropertiesList & props)963 void PowerRestoreController::setProperties(const dbusPropertiesList& props)
964 {
965 for (auto& [property, propValue] : props)
966 {
967 if (property == "PowerRestorePolicy")
968 {
969 const std::string* value = std::get_if<std::string>(&propValue);
970 if (value == nullptr)
971 {
972 lg2::error("Unable to read Power Restore Policy");
973 continue;
974 }
975 powerRestorePolicy = *value;
976 }
977 else if (property == "PowerRestoreDelay")
978 {
979 const uint64_t* value = std::get_if<uint64_t>(&propValue);
980 if (value == nullptr)
981 {
982 lg2::error("Unable to read Power Restore Delay");
983 continue;
984 }
985 powerRestoreDelay = *value / 1000000; // usec to sec
986 }
987 #ifdef USE_ACBOOT
988 else if (property == "ACBoot")
989 {
990 const std::string* value = std::get_if<std::string>(&propValue);
991 if (value == nullptr)
992 {
993 lg2::error("Unable to read AC Boot status");
994 continue;
995 }
996 acBoot = *value;
997 }
998 #endif // USE_ACBOOT
999 }
1000 invokeIfReady();
1001 }
1002
invokeIfReady()1003 void PowerRestoreController::invokeIfReady()
1004 {
1005 if ((powerRestorePolicy.empty()) || (powerRestoreDelay < 0))
1006 {
1007 return;
1008 }
1009 #ifdef USE_ACBOOT
1010 if (acBoot.empty() || acBoot == "Unknown")
1011 {
1012 return;
1013 }
1014 #endif
1015
1016 matches.clear();
1017 if (!timerFired)
1018 {
1019 // Calculate the delay from now to meet the requested delay
1020 // Subtract the approximate uboot time
1021 static constexpr const int ubootSeconds = 20;
1022 int delay = powerRestoreDelay - ubootSeconds;
1023 // Subtract the time since boot
1024 struct sysinfo info = {};
1025 if (sysinfo(&info) == 0)
1026 {
1027 delay -= info.uptime;
1028 }
1029
1030 if (delay > 0)
1031 {
1032 powerRestoreTimer.expires_after(std::chrono::seconds(delay));
1033 lg2::info("Power Restore delay of {DELAY} seconds started", "DELAY",
1034 delay);
1035 powerRestoreTimer.async_wait([this](const boost::system::error_code
1036 ec) {
1037 if (ec)
1038 {
1039 // operation_aborted is expected if timer is canceled before
1040 // completion.
1041 if (ec == boost::asio::error::operation_aborted)
1042 {
1043 return;
1044 }
1045 lg2::error(
1046 "power restore policy async_wait failed: {ERROR_MSG}",
1047 "ERROR_MSG", ec.message());
1048 }
1049 else
1050 {
1051 lg2::info("Power Restore delay timer expired");
1052 }
1053 invoke();
1054 });
1055 timerFired = true;
1056 }
1057 else
1058 {
1059 invoke();
1060 }
1061 }
1062 }
1063
invoke()1064 void PowerRestoreController::invoke()
1065 {
1066 // we want to run Power Restore only once
1067 if (policyInvoked)
1068 {
1069 return;
1070 }
1071 policyInvoked = true;
1072
1073 lg2::info("Invoking Power Restore Policy {POLICY}", "POLICY",
1074 powerRestorePolicy);
1075 if (powerRestorePolicy ==
1076 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
1077 {
1078 sendPowerControlEvent(Event::powerOnRequest);
1079 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
1080 }
1081 else if (powerRestorePolicy ==
1082 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore")
1083 {
1084 if (wasPowerDropped())
1085 {
1086 lg2::info("Power was dropped, restoring Host On state");
1087 sendPowerControlEvent(Event::powerOnRequest);
1088 setRestartCauseProperty(
1089 getRestartCause(RestartCause::powerPolicyRestore));
1090 }
1091 else
1092 {
1093 lg2::info("No power drop, restoring Host Off state");
1094 }
1095 }
1096 // We're done with the previous power state for the restore policy, so store
1097 // the current state
1098 savePowerState(powerState);
1099 }
1100
wasPowerDropped()1101 bool PowerRestoreController::wasPowerDropped()
1102 {
1103 std::string state = appState.get(PersistentState::Params::PowerState);
1104 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
1105 }
1106
waitForGPIOEvent(const std::string & name,const std::function<void (bool)> & eventHandler,gpiod::line & line,boost::asio::posix::stream_descriptor & event)1107 static void waitForGPIOEvent(
1108 const std::string& name, const std::function<void(bool)>& eventHandler,
1109 gpiod::line& line, boost::asio::posix::stream_descriptor& event)
1110 {
1111 event.async_wait(
1112 boost::asio::posix::stream_descriptor::wait_read,
1113 [&name, eventHandler, &line,
1114 &event](const boost::system::error_code ec) {
1115 if (ec)
1116 {
1117 lg2::error("{GPIO_NAME} fd handler error: {ERROR_MSG}",
1118 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1119 // TODO: throw here to force power-control to
1120 // restart?
1121 return;
1122 }
1123 gpiod::line_event line_event = line.event_read();
1124 eventHandler(line_event.event_type ==
1125 gpiod::line_event::RISING_EDGE);
1126 waitForGPIOEvent(name, eventHandler, line, event);
1127 });
1128 }
1129
requestGPIOEvents(const std::string & name,const std::function<void (bool)> & handler,gpiod::line & gpioLine,boost::asio::posix::stream_descriptor & gpioEventDescriptor)1130 static bool requestGPIOEvents(
1131 const std::string& name, const std::function<void(bool)>& handler,
1132 gpiod::line& gpioLine,
1133 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1134 {
1135 // Find the GPIO line
1136 gpioLine = gpiod::find_line(name);
1137 if (!gpioLine)
1138 {
1139 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
1140 return false;
1141 }
1142
1143 try
1144 {
1145 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});
1146 }
1147 catch (const std::exception& e)
1148 {
1149 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}",
1150 "GPIO_NAME", name, "ERROR", e);
1151 return false;
1152 }
1153
1154 int gpioLineFd = gpioLine.event_get_fd();
1155 if (gpioLineFd < 0)
1156 {
1157 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name);
1158 return false;
1159 }
1160
1161 gpioEventDescriptor.assign(gpioLineFd);
1162
1163 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor);
1164 return true;
1165 }
1166
setGPIOOutput(const std::string & name,const int value,gpiod::line & gpioLine)1167 static bool setGPIOOutput(const std::string& name, const int value,
1168 gpiod::line& gpioLine)
1169 {
1170 // Find the GPIO line
1171 gpioLine = gpiod::find_line(name);
1172 if (!gpioLine)
1173 {
1174 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
1175 return false;
1176 }
1177
1178 // Request GPIO output to specified value
1179 try
1180 {
1181 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}},
1182 value);
1183 }
1184 catch (const std::exception& e)
1185 {
1186 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
1187 name, "ERROR", e);
1188 return false;
1189 }
1190
1191 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1192 "GPIO_VALUE", value);
1193 return true;
1194 }
1195
setMaskedGPIOOutputForMs(gpiod::line & maskedGPIOLine,const std::string & name,const int value,const int durationMs)1196 static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1197 const std::string& name, const int value,
1198 const int durationMs)
1199 {
1200 // Set the masked GPIO line to the specified value
1201 maskedGPIOLine.set_value(value);
1202 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1203 "GPIO_VALUE", value);
1204 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1205 gpioAssertTimer.async_wait(
1206 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
1207 // Set the masked GPIO line back to the opposite value
1208 maskedGPIOLine.set_value(!value);
1209 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1210 if (ec)
1211 {
1212 // operation_aborted is expected if timer is canceled before
1213 // completion.
1214 if (ec != boost::asio::error::operation_aborted)
1215 {
1216 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1217 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1218 }
1219 }
1220 });
1221 return 0;
1222 }
1223
setGPIOOutputForMs(const ConfigData & config,const int value,const int durationMs)1224 static int setGPIOOutputForMs(const ConfigData& config, const int value,
1225 const int durationMs)
1226 {
1227 // If the requested GPIO is masked, use the mask line to set the output
1228 if (powerButtonMask && config.lineName == powerOutConfig.lineName)
1229 {
1230 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value,
1231 durationMs);
1232 }
1233 if (resetButtonMask && config.lineName == resetOutConfig.lineName)
1234 {
1235 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value,
1236 durationMs);
1237 }
1238
1239 // No mask set, so request and set the GPIO normally
1240 gpiod::line gpioLine;
1241 if (!setGPIOOutput(config.lineName, value, gpioLine))
1242 {
1243 return -1;
1244 }
1245 const std::string name = config.lineName;
1246
1247 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1248 gpioAssertTimer.async_wait(
1249 [gpioLine, value, name](const boost::system::error_code ec) {
1250 // Set the GPIO line back to the opposite value
1251 gpioLine.set_value(!value);
1252 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1253 if (ec)
1254 {
1255 // operation_aborted is expected if timer is canceled before
1256 // completion.
1257 if (ec != boost::asio::error::operation_aborted)
1258 {
1259 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1260 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1261 }
1262 }
1263 });
1264 return 0;
1265 }
1266
assertGPIOForMs(const ConfigData & config,const int durationMs)1267 static int assertGPIOForMs(const ConfigData& config, const int durationMs)
1268 {
1269 return setGPIOOutputForMs(config, config.polarity, durationMs);
1270 }
1271
powerOn()1272 static void powerOn()
1273 {
1274 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
1275 }
1276 #ifdef CHASSIS_SYSTEM_RESET
slotPowerOn()1277 static int slotPowerOn()
1278 {
1279 if (power_control::slotPowerState != power_control::SlotPowerState::on)
1280 {
1281 slotPowerLine.set_value(1);
1282
1283 if (slotPowerLine.get_value() > 0)
1284 {
1285 setSlotPowerState(SlotPowerState::on);
1286 lg2::info("Slot Power is switched On\n");
1287 }
1288 else
1289 {
1290 return -1;
1291 }
1292 }
1293 else
1294 {
1295 lg2::info("Slot Power is already in 'On' state\n");
1296 return -1;
1297 }
1298 return 0;
1299 }
slotPowerOff()1300 static int slotPowerOff()
1301 {
1302 if (power_control::slotPowerState != power_control::SlotPowerState::off)
1303 {
1304 slotPowerLine.set_value(0);
1305
1306 if (!(slotPowerLine.get_value() > 0))
1307 {
1308 setSlotPowerState(SlotPowerState::off);
1309 setPowerState(PowerState::off);
1310 lg2::info("Slot Power is switched Off\n");
1311 }
1312 else
1313 {
1314 return -1;
1315 }
1316 }
1317 else
1318 {
1319 lg2::info("Slot Power is already in 'Off' state\n");
1320 return -1;
1321 }
1322 return 0;
1323 }
slotPowerCycle()1324 static void slotPowerCycle()
1325 {
1326 lg2::info("Slot Power Cycle started\n");
1327 slotPowerOff();
1328 slotPowerCycleTimer.expires_after(
1329 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"]));
1330 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) {
1331 if (ec)
1332 {
1333 if (ec != boost::asio::error::operation_aborted)
1334 {
1335 lg2::error(
1336 "Slot Power cycle timer async_wait failed: {ERROR_MSG}",
1337 "ERROR_MSG", ec.message());
1338 }
1339 lg2::info("Slot Power cycle timer canceled\n");
1340 return;
1341 }
1342 lg2::info("Slot Power cycle timer completed\n");
1343 slotPowerOn();
1344 lg2::info("Slot Power Cycle Completed\n");
1345 });
1346 }
1347 #endif
gracefulPowerOff()1348 static void gracefulPowerOff()
1349 {
1350 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
1351 }
1352
forcePowerOff()1353 static void forcePowerOff()
1354 {
1355 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0)
1356 {
1357 return;
1358 }
1359
1360 // If the force off timer expires, then the power-button override failed
1361 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1362 if (ec)
1363 {
1364 // operation_aborted is expected if timer is canceled before
1365 // completion.
1366 if (ec != boost::asio::error::operation_aborted)
1367 {
1368 lg2::error("Force power off async_wait failed: {ERROR_MSG}",
1369 "ERROR_MSG", ec.message());
1370 }
1371 return;
1372 }
1373
1374 lg2::error("Power-button override failed. Not sure what to do now.");
1375 });
1376 }
1377
reset()1378 static void reset()
1379 {
1380 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]);
1381 }
1382
gracefulPowerOffTimerStart()1383 static void gracefulPowerOffTimerStart()
1384 {
1385 lg2::info("Graceful power-off timer started");
1386 gracefulPowerOffTimer.expires_after(
1387 std::chrono::seconds(TimerMap["GracefulPowerOffS"]));
1388 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1389 if (ec)
1390 {
1391 // operation_aborted is expected if timer is canceled before
1392 // completion.
1393 if (ec != boost::asio::error::operation_aborted)
1394 {
1395 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}",
1396 "ERROR_MSG", ec.message());
1397 }
1398 lg2::info("Graceful power-off timer canceled");
1399 return;
1400 }
1401 lg2::info("Graceful power-off timer completed");
1402 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1403 });
1404 }
1405
powerCycleTimerStart()1406 static void powerCycleTimerStart()
1407 {
1408 lg2::info("Power-cycle timer started");
1409 powerCycleTimer.expires_after(
1410 std::chrono::milliseconds(TimerMap["PowerCycleMs"]));
1411 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1412 if (ec)
1413 {
1414 // operation_aborted is expected if timer is canceled before
1415 // completion.
1416 if (ec != boost::asio::error::operation_aborted)
1417 {
1418 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}",
1419 "ERROR_MSG", ec.message());
1420 }
1421 lg2::info("Power-cycle timer canceled");
1422 return;
1423 }
1424 lg2::info("Power-cycle timer completed");
1425 sendPowerControlEvent(Event::powerCycleTimerExpired);
1426 });
1427 }
1428
powerOKWatchdogTimerStart()1429 static void powerOKWatchdogTimerStart()
1430 {
1431 lg2::info("power OK watchdog timer started");
1432 powerOKWatchdogTimer.expires_after(
1433 std::chrono::milliseconds(TimerMap["PowerOKWatchdogMs"]));
1434 powerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) {
1435 if (ec)
1436 {
1437 // operation_aborted is expected if timer is canceled before
1438 // completion.
1439 if (ec != boost::asio::error::operation_aborted)
1440 {
1441 lg2::error("power OK watchdog async_wait failed: {ERROR_MSG}",
1442 "ERROR_MSG", ec.message());
1443 }
1444 lg2::info("power OK watchdog timer canceled");
1445 return;
1446 }
1447 lg2::info("power OK watchdog timer expired");
1448 sendPowerControlEvent(Event::powerOKWatchdogTimerExpired);
1449 });
1450 }
1451
warmResetCheckTimerStart()1452 static void warmResetCheckTimerStart()
1453 {
1454 lg2::info("Warm reset check timer started");
1455 warmResetCheckTimer.expires_after(
1456 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"]));
1457 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1458 if (ec)
1459 {
1460 // operation_aborted is expected if timer is canceled before
1461 // completion.
1462 if (ec != boost::asio::error::operation_aborted)
1463 {
1464 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}",
1465 "ERROR_MSG", ec.message());
1466 }
1467 lg2::info("Warm reset check timer canceled");
1468 return;
1469 }
1470 lg2::info("Warm reset check timer completed");
1471 sendPowerControlEvent(Event::warmResetDetected);
1472 });
1473 }
1474
pohCounterTimerStart()1475 static void pohCounterTimerStart()
1476 {
1477 lg2::info("POH timer started");
1478 // Set the time-out as 1 hour, to align with POH command in ipmid
1479 pohCounterTimer.expires_after(std::chrono::hours(1));
1480 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1481 if (ec)
1482 {
1483 // operation_aborted is expected if timer is canceled before
1484 // completion.
1485 if (ec != boost::asio::error::operation_aborted)
1486 {
1487 lg2::error("POH timer async_wait failed: {ERROR_MSG}",
1488 "ERROR_MSG", ec.message());
1489 }
1490 lg2::info("POH timer canceled");
1491 return;
1492 }
1493
1494 if (getHostState(powerState) !=
1495 "xyz.openbmc_project.State.Host.HostState.Running")
1496 {
1497 return;
1498 }
1499
1500 conn->async_method_call(
1501 [](boost::system::error_code ec,
1502 const std::variant<uint32_t>& pohCounterProperty) {
1503 if (ec)
1504 {
1505 lg2::error("error getting poh counter");
1506 return;
1507 }
1508 const uint32_t* pohCounter =
1509 std::get_if<uint32_t>(&pohCounterProperty);
1510 if (pohCounter == nullptr)
1511 {
1512 lg2::error("unable to read poh counter");
1513 return;
1514 }
1515
1516 conn->async_method_call(
1517 [](boost::system::error_code ec) {
1518 if (ec)
1519 {
1520 lg2::error("failed to set poh counter");
1521 }
1522 },
1523 "xyz.openbmc_project.Settings",
1524 "/xyz/openbmc_project/state/chassis0",
1525 "org.freedesktop.DBus.Properties", "Set",
1526 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1527 std::variant<uint32_t>(*pohCounter + 1));
1528 },
1529 "xyz.openbmc_project.Settings",
1530 "/xyz/openbmc_project/state/chassis0",
1531 "org.freedesktop.DBus.Properties", "Get",
1532 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1533
1534 pohCounterTimerStart();
1535 });
1536 }
1537
currentHostStateMonitor()1538 static void currentHostStateMonitor()
1539 {
1540 if (getHostState(powerState) ==
1541 "xyz.openbmc_project.State.Host.HostState.Running")
1542 {
1543 pohCounterTimerStart();
1544 // Clear the restart cause set for the next restart
1545 clearRestartCause();
1546 }
1547 else
1548 {
1549 pohCounterTimer.cancel();
1550 // Set the restart cause set for this restart
1551 setRestartCause();
1552 }
1553
1554 std::string objectPath = "/xyz/openbmc_project/state/host" + node;
1555
1556 static auto match = sdbusplus::bus::match_t(
1557 *conn,
1558 "type='signal',member='PropertiesChanged', "
1559 "interface='org.freedesktop.DBus.Properties', "
1560 "path='" +
1561 objectPath +
1562 "',"
1563 "arg0='xyz.openbmc_project.State.Host'",
1564 [](sdbusplus::message_t& message) {
1565 std::string intfName;
1566 std::map<std::string, std::variant<std::string>> properties;
1567
1568 try
1569 {
1570 message.read(intfName, properties);
1571 }
1572 catch (const std::exception& e)
1573 {
1574 lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1575 return;
1576 }
1577 if (properties.empty())
1578 {
1579 lg2::error("ERROR: Empty PropertiesChanged signal received");
1580 return;
1581 }
1582
1583 // We only want to check for CurrentHostState
1584 if (properties.begin()->first != "CurrentHostState")
1585 {
1586 return;
1587 }
1588 std::string* currentHostState =
1589 std::get_if<std::string>(&(properties.begin()->second));
1590 if (currentHostState == nullptr)
1591 {
1592 lg2::error("{PROPERTY} property invalid", "PROPERTY",
1593 properties.begin()->first);
1594 return;
1595 }
1596
1597 if (*currentHostState ==
1598 "xyz.openbmc_project.State.Host.HostState.Running")
1599 {
1600 pohCounterTimerStart();
1601 // Clear the restart cause set for the next restart
1602 clearRestartCause();
1603 sd_journal_send("MESSAGE=Host system DC power is on",
1604 "PRIORITY=%i", LOG_INFO,
1605 "REDFISH_MESSAGE_ID=%s",
1606 "OpenBMC.0.1.DCPowerOn", NULL);
1607 }
1608 else
1609 {
1610 pohCounterTimer.cancel();
1611 // POST_COMPLETE GPIO event is not working in some platforms
1612 // when power state is changed to OFF. This resulted in
1613 // 'OperatingSystemState' to stay at 'Standby', even though
1614 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1615 // if HostState is trurned to OFF.
1616 setOperatingSystemState(OperatingSystemStateStage::Inactive);
1617
1618 // Set the restart cause set for this restart
1619 setRestartCause();
1620 #ifdef USE_ACBOOT
1621 resetACBootProperty();
1622 #endif // USE_ACBOOT
1623 sd_journal_send("MESSAGE=Host system DC power is off",
1624 "PRIORITY=%i", LOG_INFO,
1625 "REDFISH_MESSAGE_ID=%s",
1626 "OpenBMC.0.1.DCPowerOff", NULL);
1627 }
1628 });
1629 }
1630
sioPowerGoodWatchdogTimerStart()1631 static void sioPowerGoodWatchdogTimerStart()
1632 {
1633 lg2::info("SIO power good watchdog timer started");
1634 sioPowerGoodWatchdogTimer.expires_after(
1635 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
1636 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code
1637 ec) {
1638 if (ec)
1639 {
1640 // operation_aborted is expected if timer is canceled before
1641 // completion.
1642 if (ec != boost::asio::error::operation_aborted)
1643 {
1644 lg2::error(
1645 "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1646 "ERROR_MSG", ec.message());
1647 }
1648 lg2::info("SIO power good watchdog timer canceled");
1649 return;
1650 }
1651 lg2::info("SIO power good watchdog timer completed");
1652 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1653 });
1654 }
1655
powerStateOn(const Event event)1656 static void powerStateOn(const Event event)
1657 {
1658 logEvent(__FUNCTION__, event);
1659 switch (event)
1660 {
1661 case Event::powerOKDeAssert:
1662 setPowerState(PowerState::off);
1663 // DC power is unexpectedly lost, beep
1664 beep(beepPowerFail);
1665 break;
1666 case Event::sioS5Assert:
1667 setPowerState(PowerState::transitionToOff);
1668 #if IGNORE_SOFT_RESETS_DURING_POST
1669 // Only recognize soft resets once host gets past POST COMPLETE
1670 if (operatingSystemState != OperatingSystemStateStage::Standby)
1671 {
1672 ignoreNextSoftReset = true;
1673 }
1674 #endif
1675 addRestartCause(RestartCause::softReset);
1676 break;
1677 #if USE_PLT_RST
1678 case Event::pltRstAssert:
1679 #else
1680 case Event::postCompleteDeAssert:
1681 #endif
1682 setPowerState(PowerState::checkForWarmReset);
1683 #if IGNORE_SOFT_RESETS_DURING_POST
1684 // Only recognize soft resets once host gets past POST COMPLETE
1685 if (operatingSystemState != OperatingSystemStateStage::Standby)
1686 {
1687 ignoreNextSoftReset = true;
1688 }
1689 #endif
1690 addRestartCause(RestartCause::softReset);
1691 warmResetCheckTimerStart();
1692 break;
1693 case Event::powerButtonPressed:
1694 setPowerState(PowerState::gracefulTransitionToOff);
1695 gracefulPowerOffTimerStart();
1696 break;
1697 case Event::powerOffRequest:
1698 setPowerState(PowerState::transitionToOff);
1699 forcePowerOff();
1700 break;
1701 case Event::gracefulPowerOffRequest:
1702 setPowerState(PowerState::gracefulTransitionToOff);
1703 gracefulPowerOffTimerStart();
1704 gracefulPowerOff();
1705 break;
1706 case Event::powerCycleRequest:
1707 setPowerState(PowerState::transitionToCycleOff);
1708 forcePowerOff();
1709 break;
1710 case Event::gracefulPowerCycleRequest:
1711 setPowerState(PowerState::gracefulTransitionToCycleOff);
1712 gracefulPowerOffTimerStart();
1713 gracefulPowerOff();
1714 break;
1715 case Event::resetButtonPressed:
1716 setPowerState(PowerState::checkForWarmReset);
1717 warmResetCheckTimerStart();
1718 break;
1719 case Event::resetRequest:
1720 reset();
1721 break;
1722 default:
1723 lg2::info("No action taken.");
1724 break;
1725 }
1726 }
1727
powerStateWaitForPowerOK(const Event event)1728 static void powerStateWaitForPowerOK(const Event event)
1729 {
1730 logEvent(__FUNCTION__, event);
1731 switch (event)
1732 {
1733 case Event::powerOKAssert:
1734 {
1735 // Cancel any GPIO assertions held during the transition
1736 gpioAssertTimer.cancel();
1737 powerOKWatchdogTimer.cancel();
1738 if (sioEnabled == true)
1739 {
1740 sioPowerGoodWatchdogTimerStart();
1741 setPowerState(PowerState::waitForSIOPowerGood);
1742 }
1743 else
1744 {
1745 setPowerState(PowerState::on);
1746 }
1747 break;
1748 }
1749 case Event::powerOKWatchdogTimerExpired:
1750 setPowerState(PowerState::off);
1751 powerOKFailedLog();
1752 break;
1753 case Event::sioPowerGoodAssert:
1754 powerOKWatchdogTimer.cancel();
1755 setPowerState(PowerState::on);
1756 break;
1757 default:
1758 lg2::info("No action taken.");
1759 break;
1760 }
1761 }
1762
powerStateWaitForSIOPowerGood(const Event event)1763 static void powerStateWaitForSIOPowerGood(const Event event)
1764 {
1765 logEvent(__FUNCTION__, event);
1766 switch (event)
1767 {
1768 case Event::sioPowerGoodAssert:
1769 sioPowerGoodWatchdogTimer.cancel();
1770 setPowerState(PowerState::on);
1771 break;
1772 case Event::sioPowerGoodWatchdogTimerExpired:
1773 setPowerState(PowerState::off);
1774 systemPowerGoodFailedLog();
1775 break;
1776 default:
1777 lg2::info("No action taken.");
1778 break;
1779 }
1780 }
1781
powerStateOff(const Event event)1782 static void powerStateOff(const Event event)
1783 {
1784 logEvent(__FUNCTION__, event);
1785 switch (event)
1786 {
1787 case Event::powerOKAssert:
1788 {
1789 if (sioEnabled == true)
1790 {
1791 sioPowerGoodWatchdogTimerStart();
1792 setPowerState(PowerState::waitForSIOPowerGood);
1793 }
1794 else
1795 {
1796 setPowerState(PowerState::on);
1797 }
1798 break;
1799 }
1800 case Event::sioS5DeAssert:
1801 powerOKWatchdogTimerStart();
1802 setPowerState(PowerState::waitForPowerOK);
1803 break;
1804 case Event::sioPowerGoodAssert:
1805 setPowerState(PowerState::on);
1806 break;
1807 case Event::powerButtonPressed:
1808 powerOKWatchdogTimerStart();
1809 setPowerState(PowerState::waitForPowerOK);
1810 break;
1811 case Event::powerOnRequest:
1812 powerOKWatchdogTimerStart();
1813 setPowerState(PowerState::waitForPowerOK);
1814 powerOn();
1815 break;
1816 default:
1817 lg2::info("No action taken.");
1818 break;
1819 }
1820 }
1821
powerStateTransitionToOff(const Event event)1822 static void powerStateTransitionToOff(const Event event)
1823 {
1824 logEvent(__FUNCTION__, event);
1825 switch (event)
1826 {
1827 case Event::powerOKDeAssert:
1828 // Cancel any GPIO assertions held during the transition
1829 gpioAssertTimer.cancel();
1830 setPowerState(PowerState::off);
1831 break;
1832 default:
1833 lg2::info("No action taken.");
1834 break;
1835 }
1836 }
1837
powerStateGracefulTransitionToOff(const Event event)1838 static void powerStateGracefulTransitionToOff(const Event event)
1839 {
1840 logEvent(__FUNCTION__, event);
1841 switch (event)
1842 {
1843 case Event::powerOKDeAssert:
1844 gracefulPowerOffTimer.cancel();
1845 setPowerState(PowerState::off);
1846 break;
1847 case Event::gracefulPowerOffTimerExpired:
1848 setPowerState(PowerState::on);
1849 break;
1850 case Event::powerOffRequest:
1851 gracefulPowerOffTimer.cancel();
1852 setPowerState(PowerState::transitionToOff);
1853 forcePowerOff();
1854 break;
1855 case Event::powerCycleRequest:
1856 gracefulPowerOffTimer.cancel();
1857 setPowerState(PowerState::transitionToCycleOff);
1858 forcePowerOff();
1859 break;
1860 case Event::resetRequest:
1861 gracefulPowerOffTimer.cancel();
1862 setPowerState(PowerState::on);
1863 reset();
1864 break;
1865 default:
1866 lg2::info("No action taken.");
1867 break;
1868 }
1869 }
1870
powerStateCycleOff(const Event event)1871 static void powerStateCycleOff(const Event event)
1872 {
1873 logEvent(__FUNCTION__, event);
1874 switch (event)
1875 {
1876 case Event::powerOKAssert:
1877 {
1878 powerCycleTimer.cancel();
1879 if (sioEnabled == true)
1880 {
1881 sioPowerGoodWatchdogTimerStart();
1882 setPowerState(PowerState::waitForSIOPowerGood);
1883 }
1884 else
1885 {
1886 setPowerState(PowerState::on);
1887 }
1888 break;
1889 }
1890 case Event::sioS5DeAssert:
1891 powerCycleTimer.cancel();
1892 powerOKWatchdogTimerStart();
1893 setPowerState(PowerState::waitForPowerOK);
1894 break;
1895 case Event::powerButtonPressed:
1896 powerCycleTimer.cancel();
1897 powerOKWatchdogTimerStart();
1898 setPowerState(PowerState::waitForPowerOK);
1899 break;
1900 case Event::powerCycleTimerExpired:
1901 powerOKWatchdogTimerStart();
1902 setPowerState(PowerState::waitForPowerOK);
1903 powerOn();
1904 break;
1905 default:
1906 lg2::info("No action taken.");
1907 break;
1908 }
1909 }
1910
powerStateTransitionToCycleOff(const Event event)1911 static void powerStateTransitionToCycleOff(const Event event)
1912 {
1913 logEvent(__FUNCTION__, event);
1914 switch (event)
1915 {
1916 case Event::powerOKDeAssert:
1917 // Cancel any GPIO assertions held during the transition
1918 gpioAssertTimer.cancel();
1919 setPowerState(PowerState::cycleOff);
1920 powerCycleTimerStart();
1921 break;
1922 default:
1923 lg2::info("No action taken.");
1924 break;
1925 }
1926 }
1927
powerStateGracefulTransitionToCycleOff(const Event event)1928 static void powerStateGracefulTransitionToCycleOff(const Event event)
1929 {
1930 logEvent(__FUNCTION__, event);
1931 switch (event)
1932 {
1933 case Event::powerOKDeAssert:
1934 gracefulPowerOffTimer.cancel();
1935 setPowerState(PowerState::cycleOff);
1936 powerCycleTimerStart();
1937 break;
1938 case Event::gracefulPowerOffTimerExpired:
1939 setPowerState(PowerState::on);
1940 break;
1941 case Event::powerOffRequest:
1942 gracefulPowerOffTimer.cancel();
1943 setPowerState(PowerState::transitionToOff);
1944 forcePowerOff();
1945 break;
1946 case Event::powerCycleRequest:
1947 gracefulPowerOffTimer.cancel();
1948 setPowerState(PowerState::transitionToCycleOff);
1949 forcePowerOff();
1950 break;
1951 case Event::resetRequest:
1952 gracefulPowerOffTimer.cancel();
1953 setPowerState(PowerState::on);
1954 reset();
1955 break;
1956 default:
1957 lg2::info("No action taken.");
1958 break;
1959 }
1960 }
1961
powerStateCheckForWarmReset(const Event event)1962 static void powerStateCheckForWarmReset(const Event event)
1963 {
1964 logEvent(__FUNCTION__, event);
1965 switch (event)
1966 {
1967 case Event::sioS5Assert:
1968 warmResetCheckTimer.cancel();
1969 setPowerState(PowerState::transitionToOff);
1970 break;
1971 case Event::warmResetDetected:
1972 setPowerState(PowerState::on);
1973 break;
1974 case Event::powerOKDeAssert:
1975 warmResetCheckTimer.cancel();
1976 setPowerState(PowerState::off);
1977 // DC power is unexpectedly lost, beep
1978 beep(beepPowerFail);
1979 break;
1980 default:
1981 lg2::info("No action taken.");
1982 break;
1983 }
1984 }
1985
powerOKHandler(bool state)1986 static void powerOKHandler(bool state)
1987 {
1988 Event powerControlEvent = (state == powerOkConfig.polarity)
1989 ? Event::powerOKAssert
1990 : Event::powerOKDeAssert;
1991 sendPowerControlEvent(powerControlEvent);
1992 }
1993
sioPowerGoodHandler(bool state)1994 static void sioPowerGoodHandler(bool state)
1995 {
1996 Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
1997 ? Event::sioPowerGoodAssert
1998 : Event::sioPowerGoodDeAssert;
1999 sendPowerControlEvent(powerControlEvent);
2000 }
2001
sioOnControlHandler(bool state)2002 static void sioOnControlHandler(bool state)
2003 {
2004 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2005 static_cast<int>(state));
2006 }
2007
sioS5Handler(bool state)2008 static void sioS5Handler(bool state)
2009 {
2010 Event powerControlEvent = (state == sioS5Config.polarity)
2011 ? Event::sioS5Assert
2012 : Event::sioS5DeAssert;
2013 sendPowerControlEvent(powerControlEvent);
2014 }
2015
powerButtonHandler(bool state)2016 static void powerButtonHandler(bool state)
2017 {
2018 bool asserted = state == powerButtonConfig.polarity;
2019 powerButtonIface->set_property("ButtonPressed", asserted);
2020 if (asserted)
2021 {
2022 powerButtonPressLog();
2023 if (!powerButtonMask)
2024 {
2025 sendPowerControlEvent(Event::powerButtonPressed);
2026 addRestartCause(RestartCause::powerButton);
2027 }
2028 else
2029 {
2030 lg2::info("power button press masked");
2031 }
2032 }
2033 #if USE_BUTTON_PASSTHROUGH
2034 gpiod::line gpioLine;
2035 bool outputState =
2036 asserted ? powerOutConfig.polarity : (!powerOutConfig.polarity);
2037 if (!setGPIOOutput(powerOutConfig.lineName, outputState, gpioLine))
2038 {
2039 lg2::error("{GPIO_NAME} power button passthrough failed", "GPIO_NAME",
2040 powerOutConfig.lineName);
2041 }
2042 #endif
2043 }
2044
resetButtonHandler(bool state)2045 static void resetButtonHandler(bool state)
2046 {
2047 bool asserted = state == resetButtonConfig.polarity;
2048 resetButtonIface->set_property("ButtonPressed", asserted);
2049 if (asserted)
2050 {
2051 resetButtonPressLog();
2052 if (!resetButtonMask)
2053 {
2054 sendPowerControlEvent(Event::resetButtonPressed);
2055 addRestartCause(RestartCause::resetButton);
2056 }
2057 else
2058 {
2059 lg2::info("reset button press masked");
2060 }
2061 }
2062 #if USE_BUTTON_PASSTHROUGH
2063 gpiod::line gpioLine;
2064 bool outputState =
2065 asserted ? resetOutConfig.polarity : (!resetOutConfig.polarity);
2066 if (!setGPIOOutput(resetOutConfig.lineName, outputState, gpioLine))
2067 {
2068 lg2::error("{GPIO_NAME} reset button passthrough failed", "GPIO_NAME",
2069 resetOutConfig.lineName);
2070 }
2071 #endif
2072 }
2073
2074 #ifdef CHASSIS_SYSTEM_RESET
2075 static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2076 static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2077 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2078 static constexpr auto systemTargetName = "chassis-system-reset.target";
2079
systemReset()2080 void systemReset()
2081 {
2082 conn->async_method_call(
2083 [](boost::system::error_code ec) {
2084 if (ec)
2085 {
2086 lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2087 ec.message());
2088 }
2089 },
2090 systemdBusname, systemdPath, systemdInterface, "StartUnit",
2091 systemTargetName, "replace");
2092 }
2093 #endif
2094
nmiSetEnableProperty(bool value)2095 static void nmiSetEnableProperty(bool value)
2096 {
2097 conn->async_method_call(
2098 [](boost::system::error_code ec) {
2099 if (ec)
2100 {
2101 lg2::error("failed to set NMI source");
2102 }
2103 },
2104 "xyz.openbmc_project.Settings",
2105 "/xyz/openbmc_project/Chassis/Control/NMISource",
2106 "org.freedesktop.DBus.Properties", "Set",
2107 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2108 std::variant<bool>{value});
2109 }
2110
nmiReset(void)2111 static void nmiReset(void)
2112 {
2113 const static constexpr int nmiOutPulseTimeMs = 200;
2114
2115 lg2::info("NMI out action");
2116 nmiOutLine.set_value(nmiOutConfig.polarity);
2117 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
2118 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
2119 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2120 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2121 // restore the NMI_OUT GPIO line back to the opposite value
2122 nmiOutLine.set_value(!nmiOutConfig.polarity);
2123 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
2124 if (ec)
2125 {
2126 // operation_aborted is expected if timer is canceled before
2127 // completion.
2128 if (ec != boost::asio::error::operation_aborted)
2129 {
2130 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2131 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2132 ec.message());
2133 }
2134 }
2135 });
2136 // log to redfish
2137 nmiDiagIntLog();
2138 lg2::info("NMI out action completed");
2139 // reset Enable Property
2140 nmiSetEnableProperty(false);
2141 }
2142
nmiSourcePropertyMonitor(void)2143 static void nmiSourcePropertyMonitor(void)
2144 {
2145 lg2::info("NMI Source Property Monitor");
2146
2147 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2148 std::make_unique<sdbusplus::bus::match_t>(
2149 *conn,
2150 "type='signal',interface='org.freedesktop.DBus.Properties',"
2151 "member='PropertiesChanged',"
2152 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
2153 [](sdbusplus::message_t& msg) {
2154 std::string interfaceName;
2155 boost::container::flat_map<std::string,
2156 std::variant<bool, std::string>>
2157 propertiesChanged;
2158 std::string state;
2159 bool value = true;
2160 try
2161 {
2162 msg.read(interfaceName, propertiesChanged);
2163 if (propertiesChanged.begin()->first == "Enabled")
2164 {
2165 value =
2166 std::get<bool>(propertiesChanged.begin()->second);
2167 lg2::info(
2168 "NMI Enabled propertiesChanged value: {VALUE}",
2169 "VALUE", value);
2170 nmiEnabled = value;
2171 if (nmiEnabled)
2172 {
2173 nmiReset();
2174 }
2175 }
2176 }
2177 catch (const std::exception& e)
2178 {
2179 lg2::error("Unable to read NMI source: {ERROR}", "ERROR",
2180 e);
2181 return;
2182 }
2183 });
2184 }
2185
setNmiSource()2186 static void setNmiSource()
2187 {
2188 conn->async_method_call(
2189 [](boost::system::error_code ec) {
2190 if (ec)
2191 {
2192 lg2::error("failed to set NMI source");
2193 }
2194 },
2195 "xyz.openbmc_project.Settings",
2196 "/xyz/openbmc_project/Chassis/Control/NMISource",
2197 "org.freedesktop.DBus.Properties", "Set",
2198 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
2199 std::variant<std::string>{
2200 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
2201 // set Enable Property
2202 nmiSetEnableProperty(true);
2203 }
2204
nmiButtonHandler(bool state)2205 static void nmiButtonHandler(bool state)
2206 {
2207 // Don't handle event if host not running and config doesn't force it
2208 if (!nmiWhenPoweredOff &&
2209 getHostState(powerState) !=
2210 "xyz.openbmc_project.State.Host.HostState.Running")
2211 {
2212 return;
2213 }
2214
2215 bool asserted = state == nmiButtonConfig.polarity;
2216 nmiButtonIface->set_property("ButtonPressed", asserted);
2217 if (asserted)
2218 {
2219 nmiButtonPressLog();
2220 if (nmiButtonMasked)
2221 {
2222 lg2::info("NMI button press masked");
2223 }
2224 else
2225 {
2226 setNmiSource();
2227 }
2228 }
2229 }
2230
idButtonHandler(bool state)2231 static void idButtonHandler(bool state)
2232 {
2233 bool asserted = state == idButtonConfig.polarity;
2234 idButtonIface->set_property("ButtonPressed", asserted);
2235 }
2236
pltRstHandler(bool pltRst)2237 static void pltRstHandler(bool pltRst)
2238 {
2239 if (pltRst)
2240 {
2241 sendPowerControlEvent(Event::pltRstDeAssert);
2242 }
2243 else
2244 {
2245 sendPowerControlEvent(Event::pltRstAssert);
2246 }
2247 }
2248
hostMiscHandler(sdbusplus::message_t & msg)2249 [[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
2250 {
2251 std::string interfaceName;
2252 boost::container::flat_map<std::string, std::variant<bool>>
2253 propertiesChanged;
2254 try
2255 {
2256 msg.read(interfaceName, propertiesChanged);
2257 }
2258 catch (const std::exception& e)
2259 {
2260 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
2261 return;
2262 }
2263 if (propertiesChanged.empty())
2264 {
2265 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
2266 return;
2267 }
2268
2269 for (auto& [property, value] : propertiesChanged)
2270 {
2271 if (property == "ESpiPlatformReset")
2272 {
2273 bool* pltRst = std::get_if<bool>(&value);
2274 if (pltRst == nullptr)
2275 {
2276 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
2277 return;
2278 }
2279 pltRstHandler(*pltRst);
2280 }
2281 }
2282 }
2283
postCompleteHandler(bool state)2284 static void postCompleteHandler(bool state)
2285 {
2286 bool asserted = state == postCompleteConfig.polarity;
2287 if (asserted)
2288 {
2289 sendPowerControlEvent(Event::postCompleteAssert);
2290 setOperatingSystemState(OperatingSystemStateStage::Standby);
2291 }
2292 else
2293 {
2294 sendPowerControlEvent(Event::postCompleteDeAssert);
2295 setOperatingSystemState(OperatingSystemStateStage::Inactive);
2296 }
2297 }
2298
loadConfigValues()2299 static int loadConfigValues()
2300 {
2301 const std::string configFilePath =
2302 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2303 ".json";
2304 std::ifstream configFile(configFilePath.c_str());
2305 if (!configFile.is_open())
2306 {
2307 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2308 "PATH", configFilePath);
2309 return -1;
2310 }
2311 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
2312
2313 if (jsonData.is_discarded())
2314 {
2315 lg2::error("Power config readings JSON parser failure");
2316 return -1;
2317 }
2318 auto gpios = jsonData["gpio_configs"];
2319 auto timers = jsonData["timing_configs"];
2320
2321 ConfigData* tempGpioData;
2322
2323 for (nlohmann::json& gpioConfig : gpios)
2324 {
2325 if (!gpioConfig.contains("Name"))
2326 {
2327 lg2::error("The 'Name' field must be defined in Json file");
2328 return -1;
2329 }
2330
2331 // Iterate through the powersignal map to check if the gpio json config
2332 // entry is valid
2333 std::string gpioName = gpioConfig["Name"];
2334 auto signalMapIter = powerSignalMap.find(gpioName);
2335 if (signalMapIter == powerSignalMap.end())
2336 {
2337 lg2::error(
2338 "{GPIO_NAME} is not a recognized power-control signal name",
2339 "GPIO_NAME", gpioName);
2340 return -1;
2341 }
2342
2343 // assign the power signal name to the corresponding structure reference
2344 // from map then fillup the structure with coressponding json config
2345 // value
2346 tempGpioData = signalMapIter->second;
2347 tempGpioData->name = gpioName;
2348
2349 if (!gpioConfig.contains("Type"))
2350 {
2351 lg2::error("The \'Type\' field must be defined in Json file");
2352 return -1;
2353 }
2354
2355 std::string signalType = gpioConfig["Type"];
2356 if (signalType == "GPIO")
2357 {
2358 tempGpioData->type = ConfigType::GPIO;
2359 }
2360 else if (signalType == "DBUS")
2361 {
2362 tempGpioData->type = ConfigType::DBUS;
2363 }
2364 else
2365 {
2366 lg2::error("{TYPE} is not a recognized power-control signal type",
2367 "TYPE", signalType);
2368 return -1;
2369 }
2370
2371 if (tempGpioData->type == ConfigType::GPIO)
2372 {
2373 if (gpioConfig.contains("LineName"))
2374 {
2375 tempGpioData->lineName = gpioConfig["LineName"];
2376 }
2377 else
2378 {
2379 lg2::error(
2380 "The \'LineName\' field must be defined for GPIO configuration");
2381 return -1;
2382 }
2383 if (gpioConfig.contains("Polarity"))
2384 {
2385 std::string polarity = gpioConfig["Polarity"];
2386 if (polarity == "ActiveLow")
2387 {
2388 tempGpioData->polarity = false;
2389 }
2390 else if (polarity == "ActiveHigh")
2391 {
2392 tempGpioData->polarity = true;
2393 }
2394 else
2395 {
2396 lg2::error(
2397 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2398 "POLARITY", polarity);
2399 return -1;
2400 }
2401 }
2402 else
2403 {
2404 lg2::error("Polarity field not found for {GPIO_NAME}",
2405 "GPIO_NAME", tempGpioData->lineName);
2406 return -1;
2407 }
2408 }
2409 else
2410 {
2411 // if dbus based gpio config is defined read and update the dbus
2412 // params corresponding to the gpio config instance
2413 for (auto& [key, dbusParamName] : dbusParams)
2414 {
2415 if (!gpioConfig.contains(dbusParamName))
2416 {
2417 lg2::error(
2418 "The {DBUS_NAME} field must be defined for Dbus configuration ",
2419 "DBUS_NAME", dbusParamName);
2420 return -1;
2421 }
2422 }
2423 tempGpioData->dbusName =
2424 gpioConfig[dbusParams[DbusConfigType::name]];
2425 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
2426 tempGpioData->interface =
2427 gpioConfig[dbusParams[DbusConfigType::interface]];
2428 tempGpioData->lineName =
2429 gpioConfig[dbusParams[DbusConfigType::property]];
2430
2431 // dbus-based inputs must be active-high.
2432 tempGpioData->polarity = true;
2433
2434 // MatchRegex is optional
2435 auto item = gpioConfig.find("MatchRegex");
2436 if (item != gpioConfig.end())
2437 {
2438 try
2439 {
2440 tempGpioData->matchRegex = std::regex(*item);
2441 }
2442 catch (const std::regex_error& e)
2443 {
2444 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2445 gpioName, "ERR", e.what());
2446 return -1;
2447 }
2448 }
2449 }
2450 }
2451
2452 // read and store the timer values from json config to Timer Map
2453 for (auto& [key, timerValue] : TimerMap)
2454 {
2455 if (timers.contains(key.c_str()))
2456 {
2457 timerValue = timers[key.c_str()];
2458 }
2459 }
2460
2461 // If "events_configs" key is not in json config, fallback to null
2462 auto events = jsonData.value("event_configs",
2463 nlohmann::json(nlohmann::json::value_t::null));
2464 if (events.is_object())
2465 {
2466 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2467 }
2468
2469 return 0;
2470 }
2471
2472 template <typename T>
getMessageValue(sdbusplus::message_t & msg,const std::string & name)2473 static std::optional<T> getMessageValue(sdbusplus::message_t& msg,
2474 const std::string& name)
2475 {
2476 std::string event;
2477 std::string thresholdInterface;
2478 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2479
2480 msg.read(thresholdInterface, propertiesChanged);
2481 if (propertiesChanged.empty())
2482 {
2483 return std::nullopt;
2484 }
2485
2486 event = propertiesChanged.begin()->first;
2487 if (event.empty() || event != name)
2488 {
2489 return std::nullopt;
2490 }
2491
2492 return std::get<T>(propertiesChanged.begin()->second);
2493 }
2494
getDbusMsgGPIOState(sdbusplus::message_t & msg,const ConfigData & config,bool & value)2495 static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2496 const ConfigData& config, bool& value)
2497 {
2498 try
2499 {
2500 if (config.matchRegex.has_value())
2501 {
2502 std::optional<std::string> s =
2503 getMessageValue<std::string>(msg, config.lineName);
2504 if (!s.has_value())
2505 {
2506 return false;
2507 }
2508
2509 std::smatch m;
2510 value = std::regex_match(s.value(), m, config.matchRegex.value());
2511 }
2512 else
2513 {
2514 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2515 if (!v.has_value())
2516 {
2517 return false;
2518 }
2519 value = v.value();
2520 }
2521 return true;
2522 }
2523 catch (const std::exception& e)
2524 {
2525 lg2::error(
2526 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
2527 "DBUS_NAME", config.lineName, "ERROR", e);
2528 return false;
2529 }
2530 }
2531
dbusGPIOMatcher(const ConfigData & cfg,std::function<void (bool)> onMatch)2532 static sdbusplus::bus::match_t dbusGPIOMatcher(
2533 const ConfigData& cfg, std::function<void(bool)> onMatch)
2534 {
2535 auto pulseEventMatcherCallback =
2536 [&cfg, onMatch](sdbusplus::message_t& msg) {
2537 bool value = false;
2538 if (!getDbusMsgGPIOState(msg, cfg, value))
2539 {
2540 return;
2541 }
2542 onMatch(value);
2543 };
2544
2545 return sdbusplus::bus::match_t(
2546 static_cast<sdbusplus::bus_t&>(*conn),
2547 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2548 "PropertiesChanged',arg0='" +
2549 cfg.interface + "',path='" + cfg.path + "',sender='" +
2550 cfg.dbusName + "'",
2551 std::move(pulseEventMatcherCallback));
2552 }
2553
2554 // D-Bus property read functions
2555 void reschedulePropertyRead(const ConfigData& configData);
2556
getProperty(const ConfigData & configData)2557 int getProperty(const ConfigData& configData)
2558 {
2559 std::variant<bool> resp;
2560
2561 try
2562 {
2563 auto method = conn->new_method_call(
2564 configData.dbusName.c_str(), configData.path.c_str(),
2565 "org.freedesktop.DBus.Properties", "Get");
2566 method.append(configData.interface.c_str(),
2567 configData.lineName.c_str());
2568
2569 auto reply = conn->call(method);
2570 reply.read(resp);
2571 }
2572 catch (const sdbusplus::exception_t& e)
2573 {
2574 lg2::error(
2575 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}: {WHAT}",
2576 "PROPERTY", configData.lineName, "INTERFACE", configData.interface,
2577 "PATH", configData.path, "WHAT", e);
2578
2579 reschedulePropertyRead(configData);
2580 return -1;
2581 }
2582
2583 auto respValue = std::get_if<bool>(&resp);
2584 if (!respValue)
2585 {
2586 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2587 "PROPERTY", configData.lineName);
2588 return -1;
2589 }
2590 return (*respValue);
2591 }
2592
setInitialValue(const ConfigData & configData,bool initialValue)2593 void setInitialValue(const ConfigData& configData, bool initialValue)
2594 {
2595 if (configData.name == "PowerOk")
2596 {
2597 powerState = (initialValue ? PowerState::on : PowerState::off);
2598 hostIface->set_property("CurrentHostState",
2599 std::string(getHostState(powerState)));
2600 }
2601 else if (configData.name == "PowerButton")
2602 {
2603 powerButtonIface->set_property("ButtonPressed", !initialValue);
2604 }
2605 else if (configData.name == "ResetButton")
2606 {
2607 resetButtonIface->set_property("ButtonPressed", !initialValue);
2608 }
2609 else if (configData.name == "NMIButton")
2610 {
2611 nmiButtonIface->set_property("ButtonPressed", !initialValue);
2612 }
2613 else if (configData.name == "IdButton")
2614 {
2615 idButtonIface->set_property("ButtonPressed", !initialValue);
2616 }
2617 else if (configData.name == "PostComplete")
2618 {
2619 OperatingSystemStateStage osState =
2620 (initialValue == postCompleteConfig.polarity
2621 ? OperatingSystemStateStage::Standby
2622 : OperatingSystemStateStage::Inactive);
2623 setOperatingSystemState(osState);
2624 }
2625 else
2626 {
2627 lg2::error("Unknown name {NAME}", "NAME", configData.name);
2628 }
2629 }
2630
reschedulePropertyRead(const ConfigData & configData)2631 void reschedulePropertyRead(const ConfigData& configData)
2632 {
2633 auto item = dBusRetryTimers.find(configData.name);
2634
2635 if (item == dBusRetryTimers.end())
2636 {
2637 auto newItem = dBusRetryTimers.insert(
2638 {configData.name, boost::asio::steady_timer(io)});
2639
2640 if (!newItem.second)
2641 {
2642 lg2::error("Failed to add new timer for {NAME}", "NAME",
2643 configData.name);
2644 return;
2645 }
2646
2647 item = newItem.first;
2648 }
2649
2650 auto& timer = item->second;
2651 timer.expires_after(
2652 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2653 timer.async_wait([&configData](const boost::system::error_code ec) {
2654 if (ec)
2655 {
2656 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2657 configData.name, "MSG", ec.message());
2658 dBusRetryTimers.erase(configData.name);
2659 return;
2660 }
2661 sdbusplus::asio::getProperty<bool>(
2662 *conn, configData.dbusName, configData.path, configData.interface,
2663 configData.lineName,
2664 [&configData](boost::system::error_code ec, bool value) {
2665 if (ec)
2666 {
2667 lg2::error("Exception while reading {PROPERTY}: {WHAT}",
2668 "PROPERTY", configData.lineName, "WHAT",
2669 ec.message());
2670 reschedulePropertyRead(configData);
2671 return;
2672 }
2673
2674 setInitialValue(configData, value);
2675 dBusRetryTimers.erase(configData.name);
2676 });
2677 });
2678 }
2679 } // namespace power_control
2680
main(int argc,char * argv[])2681 int main(int argc, char* argv[])
2682 {
2683 using namespace power_control;
2684
2685 if (argc > 1)
2686 {
2687 node = argv[1];
2688 }
2689 lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2690 node);
2691
2692 conn = std::make_shared<sdbusplus::asio::connection>(io);
2693
2694 // Load GPIO's through json config file
2695 if (loadConfigValues() == -1)
2696 {
2697 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
2698 }
2699 /* Currently for single host based systems additional busname is added
2700 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2701 Going forward for single hosts the old bus name without zero numbering
2702 will be removed when all other applications adapted to the
2703 bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2704
2705 if (node == "0")
2706 {
2707 // Request all the dbus names
2708 conn->request_name(hostDbusName.c_str());
2709 conn->request_name(chassisDbusName.c_str());
2710 conn->request_name(osDbusName.c_str());
2711 conn->request_name(buttonDbusName.c_str());
2712 conn->request_name(nmiDbusName.c_str());
2713 conn->request_name(rstCauseDbusName.c_str());
2714 }
2715
2716 hostDbusName += node;
2717 chassisDbusName += node;
2718 osDbusName += node;
2719 buttonDbusName += node;
2720 nmiDbusName += node;
2721 rstCauseDbusName += node;
2722
2723 // Request all the dbus names
2724 conn->request_name(hostDbusName.c_str());
2725 conn->request_name(chassisDbusName.c_str());
2726 conn->request_name(osDbusName.c_str());
2727 conn->request_name(buttonDbusName.c_str());
2728 conn->request_name(nmiDbusName.c_str());
2729 conn->request_name(rstCauseDbusName.c_str());
2730
2731 if (sioPwrGoodConfig.lineName.empty() ||
2732 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
2733 {
2734 sioEnabled = false;
2735 lg2::info("SIO control GPIOs not defined, disable SIO support.");
2736 }
2737
2738 // Request power OK GPIO events
2739 if (powerOkConfig.type == ConfigType::GPIO)
2740 {
2741 if (!requestGPIOEvents(powerOkConfig.lineName, powerOKHandler,
2742 powerOKLine, powerOKEvent))
2743 {
2744 return -1;
2745 }
2746 }
2747 else if (powerOkConfig.type == ConfigType::DBUS)
2748 {
2749 static sdbusplus::bus::match_t powerOkEventMonitor =
2750 power_control::dbusGPIOMatcher(powerOkConfig, powerOKHandler);
2751 }
2752 else
2753 {
2754 lg2::error("PowerOk name should be configured from json config file");
2755 return -1;
2756 }
2757
2758 if (sioEnabled == true)
2759 {
2760 // Request SIO_POWER_GOOD GPIO events
2761 if (sioPwrGoodConfig.type == ConfigType::GPIO)
2762 {
2763 if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
2764 sioPowerGoodHandler, sioPowerGoodLine,
2765 sioPowerGoodEvent))
2766 {
2767 return -1;
2768 }
2769 }
2770 else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2771 {
2772 static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
2773 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2774 sioPowerGoodHandler);
2775 }
2776 else
2777 {
2778 lg2::error(
2779 "sioPwrGood name should be configured from json config file");
2780 return -1;
2781 }
2782
2783 // Request SIO_ONCONTROL GPIO events
2784 if (sioOnControlConfig.type == ConfigType::GPIO)
2785 {
2786 if (!requestGPIOEvents(sioOnControlConfig.lineName,
2787 sioOnControlHandler, sioOnControlLine,
2788 sioOnControlEvent))
2789 {
2790 return -1;
2791 }
2792 }
2793 else if (sioOnControlConfig.type == ConfigType::DBUS)
2794 {
2795 static sdbusplus::bus::match_t sioOnControlEventMonitor =
2796 power_control::dbusGPIOMatcher(sioOnControlConfig,
2797 sioOnControlHandler);
2798 }
2799 else
2800 {
2801 lg2::error(
2802 "sioOnControl name should be configured from jsonconfig file\n");
2803 return -1;
2804 }
2805
2806 // Request SIO_S5 GPIO events
2807 if (sioS5Config.type == ConfigType::GPIO)
2808 {
2809 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
2810 sioS5Line, sioS5Event))
2811 {
2812 return -1;
2813 }
2814 }
2815 else if (sioS5Config.type == ConfigType::DBUS)
2816 {
2817 static sdbusplus::bus::match_t sioS5EventMonitor =
2818 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
2819 }
2820 else
2821 {
2822 lg2::error("sioS5 name should be configured from json config file");
2823 return -1;
2824 }
2825 }
2826
2827 // Request POWER_BUTTON GPIO events
2828 if (powerButtonConfig.type == ConfigType::GPIO)
2829 {
2830 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2831 powerButtonLine, powerButtonEvent))
2832 {
2833 return -1;
2834 }
2835 }
2836 else if (powerButtonConfig.type == ConfigType::DBUS)
2837 {
2838 static sdbusplus::bus::match_t powerButtonEventMonitor =
2839 power_control::dbusGPIOMatcher(powerButtonConfig,
2840 powerButtonHandler);
2841 }
2842
2843 // Request RESET_BUTTON GPIO events
2844 if (resetButtonConfig.type == ConfigType::GPIO)
2845 {
2846 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2847 resetButtonLine, resetButtonEvent))
2848 {
2849 return -1;
2850 }
2851 }
2852 else if (resetButtonConfig.type == ConfigType::DBUS)
2853 {
2854 static sdbusplus::bus::match_t resetButtonEventMonitor =
2855 power_control::dbusGPIOMatcher(resetButtonConfig,
2856 resetButtonHandler);
2857 }
2858
2859 // Request NMI_BUTTON GPIO events
2860 if (nmiButtonConfig.type == ConfigType::GPIO)
2861 {
2862 if (!nmiButtonConfig.lineName.empty())
2863 {
2864 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
2865 nmiButtonLine, nmiButtonEvent);
2866 }
2867 }
2868 else if (nmiButtonConfig.type == ConfigType::DBUS)
2869 {
2870 static sdbusplus::bus::match_t nmiButtonEventMonitor =
2871 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
2872 }
2873
2874 // Request ID_BUTTON GPIO events
2875 if (idButtonConfig.type == ConfigType::GPIO)
2876 {
2877 if (!idButtonConfig.lineName.empty())
2878 {
2879 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
2880 idButtonLine, idButtonEvent);
2881 }
2882 }
2883 else if (idButtonConfig.type == ConfigType::DBUS)
2884 {
2885 static sdbusplus::bus::match_t idButtonEventMonitor =
2886 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
2887 }
2888
2889 #ifdef USE_PLT_RST
2890 sdbusplus::bus::match_t pltRstMatch(
2891 *conn,
2892 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2893 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
2894 hostMiscHandler);
2895 #endif
2896
2897 bool postCompleteConfigured = false;
2898 // Request POST_COMPLETE GPIO events
2899 if (postCompleteConfig.type == ConfigType::GPIO)
2900 {
2901 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2902 postCompleteLine, postCompleteEvent))
2903 {
2904 return -1;
2905 }
2906 postCompleteConfigured = true;
2907 }
2908 else if (postCompleteConfig.type == ConfigType::DBUS)
2909 {
2910 static sdbusplus::bus::match_t postCompleteEventMonitor =
2911 power_control::dbusGPIOMatcher(postCompleteConfig,
2912 postCompleteHandler);
2913 postCompleteConfigured = true;
2914 }
2915
2916 // initialize NMI_OUT GPIO.
2917 if (!nmiOutConfig.lineName.empty())
2918 {
2919 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2920 nmiOutLine);
2921 }
2922
2923 // Initialize POWER_OUT and RESET_OUT GPIO.
2924 gpiod::line line;
2925 if (!powerOutConfig.lineName.empty())
2926 {
2927 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2928 line))
2929 {
2930 return -1;
2931 }
2932 }
2933 else
2934 {
2935 lg2::error("powerOut name should be configured from json config file");
2936 return -1;
2937 }
2938
2939 if (!resetOutConfig.lineName.empty())
2940 {
2941 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2942 line))
2943 {
2944 return -1;
2945 }
2946 }
2947 else
2948 {
2949 lg2::error("ResetOut name should be configured from json config file");
2950 return -1;
2951 }
2952 // Release line
2953 line.reset();
2954
2955 // Initialize the power state and operating system state
2956 powerState = PowerState::off;
2957 operatingSystemState = OperatingSystemStateStage::Inactive;
2958 // Check power good
2959
2960 if (powerOkConfig.type == ConfigType::GPIO)
2961 {
2962 if (powerOKLine.get_value() > 0 ||
2963 (sioEnabled &&
2964 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
2965 {
2966 powerState = PowerState::on;
2967 }
2968 }
2969 else
2970 {
2971 if (getProperty(powerOkConfig))
2972 {
2973 powerState = PowerState::on;
2974 }
2975 }
2976 // Check if we need to start the Power Restore policy
2977 if (powerState != PowerState::on)
2978 {
2979 powerRestore.run();
2980 }
2981
2982 if (nmiOutLine)
2983 nmiSourcePropertyMonitor();
2984
2985 lg2::info("Initializing power state.");
2986 logStateTransition(powerState);
2987
2988 // Power Control Service
2989 sdbusplus::asio::object_server hostServer =
2990 sdbusplus::asio::object_server(conn);
2991
2992 // Power Control Interface
2993 hostIface =
2994 hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
2995 "xyz.openbmc_project.State.Host");
2996 // Interface for IPMI/Redfish initiated host state transitions
2997 hostIface->register_property(
2998 "RequestedHostTransition",
2999 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
3000 [](const std::string& requested, std::string& resp) {
3001 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
3002 {
3003 // if power button is masked, ignore this
3004 if (!powerButtonMask)
3005 {
3006 sendPowerControlEvent(Event::gracefulPowerOffRequest);
3007 addRestartCause(RestartCause::command);
3008 }
3009 else
3010 {
3011 lg2::info("Power Button Masked.");
3012 throw std::invalid_argument("Transition Request Masked");
3013 return 0;
3014 }
3015 }
3016 else if (requested ==
3017 "xyz.openbmc_project.State.Host.Transition.On")
3018 {
3019 // if power button is masked, ignore this
3020 if (!powerButtonMask)
3021 {
3022 sendPowerControlEvent(Event::powerOnRequest);
3023 addRestartCause(RestartCause::command);
3024 }
3025 else
3026 {
3027 lg2::info("Power Button Masked.");
3028 throw std::invalid_argument("Transition Request Masked");
3029 return 0;
3030 }
3031 }
3032 else if (requested ==
3033 "xyz.openbmc_project.State.Host.Transition.Reboot")
3034 {
3035 // if power button is masked, ignore this
3036 if (!powerButtonMask)
3037 {
3038 sendPowerControlEvent(Event::powerCycleRequest);
3039 addRestartCause(RestartCause::command);
3040 }
3041 else
3042 {
3043 lg2::info("Power Button Masked.");
3044 throw std::invalid_argument("Transition Request Masked");
3045 return 0;
3046 }
3047 }
3048 else if (
3049 requested ==
3050 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3051 {
3052 // if reset button is masked, ignore this
3053 if (!resetButtonMask)
3054 {
3055 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3056 addRestartCause(RestartCause::command);
3057 }
3058 else
3059 {
3060 lg2::info("Reset Button Masked.");
3061 throw std::invalid_argument("Transition Request Masked");
3062 return 0;
3063 }
3064 }
3065 else if (
3066 requested ==
3067 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3068 {
3069 // if reset button is masked, ignore this
3070 if (!resetButtonMask)
3071 {
3072 sendPowerControlEvent(Event::resetRequest);
3073 addRestartCause(RestartCause::command);
3074 }
3075 else
3076 {
3077 lg2::info("Reset Button Masked.");
3078 throw std::invalid_argument("Transition Request Masked");
3079 return 0;
3080 }
3081 }
3082 else
3083 {
3084 lg2::error("Unrecognized host state transition request.");
3085 throw std::invalid_argument("Unrecognized Transition Request");
3086 return 0;
3087 }
3088 resp = requested;
3089 return 1;
3090 });
3091 hostIface->register_property("CurrentHostState",
3092 std::string(getHostState(powerState)));
3093
3094 hostIface->initialize();
3095
3096 // Chassis Control Service
3097 sdbusplus::asio::object_server chassisServer =
3098 sdbusplus::asio::object_server(conn);
3099
3100 // Chassis Control Interface
3101 chassisIface =
3102 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
3103 "xyz.openbmc_project.State.Chassis");
3104
3105 chassisIface->register_property(
3106 "RequestedPowerTransition",
3107 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3108 [](const std::string& requested, std::string& resp) {
3109 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
3110 {
3111 // if power button is masked, ignore this
3112 if (!powerButtonMask)
3113 {
3114 sendPowerControlEvent(Event::powerOffRequest);
3115 addRestartCause(RestartCause::command);
3116 }
3117 else
3118 {
3119 lg2::info("Power Button Masked.");
3120 throw std::invalid_argument("Transition Request Masked");
3121 return 0;
3122 }
3123 }
3124 else if (requested ==
3125 "xyz.openbmc_project.State.Chassis.Transition.On")
3126 {
3127 // if power button is masked, ignore this
3128 if (!powerButtonMask)
3129 {
3130 sendPowerControlEvent(Event::powerOnRequest);
3131 addRestartCause(RestartCause::command);
3132 }
3133 else
3134 {
3135 lg2::info("Power Button Masked.");
3136 throw std::invalid_argument("Transition Request Masked");
3137 return 0;
3138 }
3139 }
3140 else if (requested ==
3141 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3142 {
3143 // if power button is masked, ignore this
3144 if (!powerButtonMask)
3145 {
3146 sendPowerControlEvent(Event::powerCycleRequest);
3147 addRestartCause(RestartCause::command);
3148 }
3149 else
3150 {
3151 lg2::info("Power Button Masked.");
3152 throw std::invalid_argument("Transition Request Masked");
3153 return 0;
3154 }
3155 }
3156 else
3157 {
3158 lg2::error("Unrecognized chassis state transition request.");
3159 throw std::invalid_argument("Unrecognized Transition Request");
3160 return 0;
3161 }
3162 resp = requested;
3163 return 1;
3164 });
3165 chassisIface->register_property("CurrentPowerState",
3166 std::string(getChassisState(powerState)));
3167 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
3168
3169 chassisIface->initialize();
3170
3171 #ifdef CHASSIS_SYSTEM_RESET
3172 // Chassis System Service
3173 sdbusplus::asio::object_server chassisSysServer =
3174 sdbusplus::asio::object_server(conn);
3175
3176 // Chassis System Interface
3177 chassisSysIface = chassisSysServer.add_interface(
3178 "/xyz/openbmc_project/state/chassis_system0",
3179 "xyz.openbmc_project.State.Chassis");
3180
3181 chassisSysIface->register_property(
3182 "RequestedPowerTransition",
3183 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3184 [](const std::string& requested, std::string& resp) {
3185 if (requested ==
3186 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3187 {
3188 systemReset();
3189 addRestartCause(RestartCause::command);
3190 }
3191 else
3192 {
3193 lg2::error(
3194 "Unrecognized chassis system state transition request.");
3195 throw std::invalid_argument("Unrecognized Transition Request");
3196 return 0;
3197 }
3198 resp = requested;
3199 return 1;
3200 });
3201 chassisSysIface->register_property(
3202 "CurrentPowerState", std::string(getChassisState(powerState)));
3203 chassisSysIface->register_property("LastStateChangeTime",
3204 getCurrentTimeMs());
3205
3206 chassisSysIface->initialize();
3207
3208 if (!slotPowerConfig.lineName.empty())
3209 {
3210 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3211 {
3212 return -1;
3213 }
3214
3215 slotPowerState = SlotPowerState::off;
3216 if (slotPowerLine.get_value() > 0)
3217 {
3218 slotPowerState = SlotPowerState::on;
3219 }
3220
3221 chassisSlotIface = chassisSysServer.add_interface(
3222 "/xyz/openbmc_project/state/chassis_system" + node,
3223 "xyz.openbmc_project.State.Chassis");
3224 chassisSlotIface->register_property(
3225 "RequestedPowerTransition",
3226 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3227 [](const std::string& requested, std::string& resp) {
3228 if (requested ==
3229 "xyz.openbmc_project.State.Chassis.Transition.On")
3230 {
3231 slotPowerOn();
3232 }
3233 else if (requested ==
3234 "xyz.openbmc_project.State.Chassis.Transition.Off")
3235 {
3236 slotPowerOff();
3237 }
3238 else if (
3239 requested ==
3240 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3241 {
3242 slotPowerCycle();
3243 }
3244 else
3245 {
3246 lg2::error(
3247 "Unrecognized chassis system state transition request.\n");
3248 throw std::invalid_argument(
3249 "Unrecognized Transition Request");
3250 return 0;
3251 }
3252 resp = requested;
3253 return 1;
3254 });
3255 chassisSlotIface->register_property(
3256 "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3257 chassisSlotIface->register_property("LastStateChangeTime",
3258 getCurrentTimeMs());
3259 chassisSlotIface->initialize();
3260 }
3261 #endif
3262 // Buttons Service
3263 sdbusplus::asio::object_server buttonsServer =
3264 sdbusplus::asio::object_server(conn);
3265
3266 if (!powerButtonConfig.lineName.empty())
3267 {
3268 // Power Button Interface
3269 power_control::powerButtonIface = buttonsServer.add_interface(
3270 "/xyz/openbmc_project/chassis/buttons/power",
3271 "xyz.openbmc_project.Chassis.Buttons");
3272
3273 powerButtonIface->register_property(
3274 "ButtonMasked", false, [](const bool requested, bool& current) {
3275 if (requested)
3276 {
3277 if (powerButtonMask)
3278 {
3279 return 1;
3280 }
3281 if (!setGPIOOutput(powerOutConfig.lineName,
3282 !powerOutConfig.polarity,
3283 powerButtonMask))
3284 {
3285 throw std::runtime_error("Failed to request GPIO");
3286 return 0;
3287 }
3288 lg2::info("Power Button Masked.");
3289 }
3290 else
3291 {
3292 if (!powerButtonMask)
3293 {
3294 return 1;
3295 }
3296 lg2::info("Power Button Un-masked");
3297 powerButtonMask.reset();
3298 }
3299 // Update the mask setting
3300 current = requested;
3301 return 1;
3302 });
3303
3304 // Check power button state
3305 bool powerButtonPressed;
3306 if (powerButtonConfig.type == ConfigType::GPIO)
3307 {
3308 powerButtonPressed = powerButtonLine.get_value() == 0;
3309 }
3310 else
3311 {
3312 powerButtonPressed = getProperty(powerButtonConfig) == 0;
3313 }
3314
3315 powerButtonIface->register_property("ButtonPressed",
3316 powerButtonPressed);
3317
3318 powerButtonIface->initialize();
3319 }
3320
3321 if (!resetButtonConfig.lineName.empty())
3322 {
3323 // Reset Button Interface
3324
3325 resetButtonIface = buttonsServer.add_interface(
3326 "/xyz/openbmc_project/chassis/buttons/reset",
3327 "xyz.openbmc_project.Chassis.Buttons");
3328
3329 resetButtonIface->register_property(
3330 "ButtonMasked", false, [](const bool requested, bool& current) {
3331 if (requested)
3332 {
3333 if (resetButtonMask)
3334 {
3335 return 1;
3336 }
3337 if (!setGPIOOutput(resetOutConfig.lineName,
3338 !resetOutConfig.polarity,
3339 resetButtonMask))
3340 {
3341 throw std::runtime_error("Failed to request GPIO");
3342 return 0;
3343 }
3344 lg2::info("Reset Button Masked.");
3345 }
3346 else
3347 {
3348 if (!resetButtonMask)
3349 {
3350 return 1;
3351 }
3352 lg2::info("Reset Button Un-masked");
3353 resetButtonMask.reset();
3354 }
3355 // Update the mask setting
3356 current = requested;
3357 return 1;
3358 });
3359
3360 // Check reset button state
3361 bool resetButtonPressed;
3362 if (resetButtonConfig.type == ConfigType::GPIO)
3363 {
3364 resetButtonPressed = resetButtonLine.get_value() == 0;
3365 }
3366 else
3367 {
3368 resetButtonPressed = getProperty(resetButtonConfig) == 0;
3369 }
3370
3371 resetButtonIface->register_property("ButtonPressed",
3372 resetButtonPressed);
3373
3374 resetButtonIface->initialize();
3375 }
3376
3377 if (nmiButtonLine)
3378 {
3379 // NMI Button Interface
3380 nmiButtonIface = buttonsServer.add_interface(
3381 "/xyz/openbmc_project/chassis/buttons/nmi",
3382 "xyz.openbmc_project.Chassis.Buttons");
3383
3384 nmiButtonIface->register_property(
3385 "ButtonMasked", false, [](const bool requested, bool& current) {
3386 if (nmiButtonMasked == requested)
3387 {
3388 // NMI button mask is already set as requested, so no change
3389 return 1;
3390 }
3391 if (requested)
3392 {
3393 lg2::info("NMI Button Masked.");
3394 nmiButtonMasked = true;
3395 }
3396 else
3397 {
3398 lg2::info("NMI Button Un-masked.");
3399 nmiButtonMasked = false;
3400 }
3401 // Update the mask setting
3402 current = nmiButtonMasked;
3403 return 1;
3404 });
3405
3406 // Check NMI button state
3407 bool nmiButtonPressed;
3408 if (nmiButtonConfig.type == ConfigType::GPIO)
3409 {
3410 nmiButtonPressed = nmiButtonLine.get_value() == 0;
3411 }
3412 else
3413 {
3414 nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3415 }
3416
3417 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
3418
3419 nmiButtonIface->initialize();
3420 }
3421
3422 if (nmiOutLine)
3423 {
3424 // NMI out Service
3425 sdbusplus::asio::object_server nmiOutServer =
3426 sdbusplus::asio::object_server(conn);
3427
3428 // NMI out Interface
3429 nmiOutIface = nmiOutServer.add_interface(
3430 "/xyz/openbmc_project/control/host" + node + "/nmi",
3431 "xyz.openbmc_project.Control.Host.NMI");
3432 nmiOutIface->register_method("NMI", nmiReset);
3433 nmiOutIface->initialize();
3434 }
3435
3436 if (idButtonLine)
3437 {
3438 // ID Button Interface
3439 idButtonIface = buttonsServer.add_interface(
3440 "/xyz/openbmc_project/chassis/buttons/id",
3441 "xyz.openbmc_project.Chassis.Buttons");
3442
3443 // Check ID button state
3444 bool idButtonPressed;
3445 if (idButtonConfig.type == ConfigType::GPIO)
3446 {
3447 idButtonPressed = idButtonLine.get_value() == 0;
3448 }
3449 else
3450 {
3451 idButtonPressed = getProperty(idButtonConfig) == 0;
3452 }
3453
3454 idButtonIface->register_property("ButtonPressed", idButtonPressed);
3455
3456 idButtonIface->initialize();
3457 }
3458
3459 if (postCompleteConfigured)
3460 {
3461 OperatingSystemStateStage osState;
3462 if (postCompleteConfig.type == ConfigType::GPIO)
3463 {
3464 osState =
3465 postCompleteLine.get_value() == postCompleteConfig.polarity
3466 ? OperatingSystemStateStage::Standby
3467 : OperatingSystemStateStage::Inactive;
3468 }
3469 else
3470 {
3471 osState = getProperty(postCompleteConfig) > 0
3472 ? OperatingSystemStateStage::Standby
3473 : OperatingSystemStateStage::Inactive;
3474 }
3475
3476 // OS State Service
3477 sdbusplus::asio::object_server osServer =
3478 sdbusplus::asio::object_server(conn);
3479
3480 // OS State Interface
3481 osIface = osServer.add_interface(
3482 "/xyz/openbmc_project/state/host" + node,
3483 "xyz.openbmc_project.State.OperatingSystem.Status");
3484
3485 // Get the initial OS state based on POST complete
3486 // Asserted, OS state is "Standby" (ready to boot)
3487 // De-Asserted, OS state is "Inactive"
3488
3489 osIface->register_property(
3490 "OperatingSystemState",
3491 std::string(getOperatingSystemStateStage(osState)));
3492
3493 osIface->initialize();
3494
3495 // Restart Cause Service
3496 sdbusplus::asio::object_server restartCauseServer =
3497 sdbusplus::asio::object_server(conn);
3498
3499 // Restart Cause Interface
3500 restartCauseIface = restartCauseServer.add_interface(
3501 "/xyz/openbmc_project/control/host" + node + "/restart_cause",
3502 "xyz.openbmc_project.Control.Host.RestartCause");
3503
3504 restartCauseIface->register_property(
3505 "RestartCause",
3506 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3507
3508 restartCauseIface->register_property(
3509 "RequestedRestartCause",
3510 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3511 [](const std::string& requested, std::string& resp) {
3512 if (requested ==
3513 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3514 {
3515 addRestartCause(RestartCause::watchdog);
3516 }
3517 else
3518 {
3519 throw std::invalid_argument(
3520 "Unrecognized RestartCause Request");
3521 return 0;
3522 }
3523
3524 lg2::info("RestartCause requested: {RESTART_CAUSE}",
3525 "RESTART_CAUSE", requested);
3526 resp = requested;
3527 return 1;
3528 });
3529
3530 restartCauseIface->initialize();
3531
3532 currentHostStateMonitor();
3533 }
3534 if (!hpmStbyEnConfig.lineName.empty())
3535 {
3536 // Set to indicate BMC's power control module is ready to take
3537 // the inputs [PWR_GOOD] from the HPM FPGA
3538 gpiod::line hpmLine;
3539 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3540 hpmLine))
3541 {
3542 return -1;
3543 }
3544 }
3545
3546 io.run();
3547
3548 return 0;
3549 }
3550