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