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