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