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