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