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