xref: /openbmc/x86-power-control/src/power_control.cpp (revision edc86f3524e9e6fe7bdd64a88886be003e24090b)
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 == powerOkConfig.polarity)
2018                                   ? Event::psPowerOKAssert
2019                                   : Event::psPowerOKDeAssert;
2020     sendPowerControlEvent(powerControlEvent);
2021 }
2022 
2023 static void sioPowerGoodHandler(bool state)
2024 {
2025     Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2026                                   ? Event::sioPowerGoodAssert
2027                                   : Event::sioPowerGoodDeAssert;
2028     sendPowerControlEvent(powerControlEvent);
2029 }
2030 
2031 static void sioOnControlHandler(bool state)
2032 {
2033     lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2034               static_cast<int>(state));
2035 }
2036 
2037 static void sioS5Handler(bool state)
2038 {
2039     Event powerControlEvent = (state == sioS5Config.polarity)
2040                                   ? Event::sioS5Assert
2041                                   : Event::sioS5DeAssert;
2042     sendPowerControlEvent(powerControlEvent);
2043 }
2044 
2045 static void powerButtonHandler(bool state)
2046 {
2047     bool asserted = state == powerButtonConfig.polarity;
2048     powerButtonIface->set_property("ButtonPressed", asserted);
2049     if (asserted)
2050     {
2051         powerButtonPressLog();
2052         if (!powerButtonMask)
2053         {
2054             sendPowerControlEvent(Event::powerButtonPressed);
2055             addRestartCause(RestartCause::powerButton);
2056         }
2057         else
2058         {
2059             lg2::info("power button press masked");
2060         }
2061     }
2062 }
2063 
2064 static void resetButtonHandler(bool state)
2065 {
2066     bool asserted = state == resetButtonConfig.polarity;
2067     resetButtonIface->set_property("ButtonPressed", asserted);
2068     if (asserted)
2069     {
2070         resetButtonPressLog();
2071         if (!resetButtonMask)
2072         {
2073             sendPowerControlEvent(Event::resetButtonPressed);
2074             addRestartCause(RestartCause::resetButton);
2075         }
2076         else
2077         {
2078             lg2::info("reset button press masked");
2079         }
2080     }
2081 }
2082 
2083 #ifdef CHASSIS_SYSTEM_RESET
2084 static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2085 static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2086 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2087 static constexpr auto systemTargetName = "chassis-system-reset.target";
2088 
2089 void systemReset()
2090 {
2091     conn->async_method_call(
2092         [](boost::system::error_code ec) {
2093         if (ec)
2094         {
2095             lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2096                        ec.message());
2097         }
2098     },
2099         systemdBusname, systemdPath, systemdInterface, "StartUnit",
2100         systemTargetName, "replace");
2101 }
2102 #endif
2103 
2104 static void nmiSetEnableProperty(bool value)
2105 {
2106     conn->async_method_call(
2107         [](boost::system::error_code ec) {
2108         if (ec)
2109         {
2110             lg2::error("failed to set NMI source");
2111         }
2112     },
2113         "xyz.openbmc_project.Settings",
2114         "/xyz/openbmc_project/Chassis/Control/NMISource",
2115         "org.freedesktop.DBus.Properties", "Set",
2116         "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2117         std::variant<bool>{value});
2118 }
2119 
2120 static void nmiReset(void)
2121 {
2122     const static constexpr int nmiOutPulseTimeMs = 200;
2123 
2124     lg2::info("NMI out action");
2125     nmiOutLine.set_value(nmiOutConfig.polarity);
2126     lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
2127               nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
2128     gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2129     gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2130         // restore the NMI_OUT GPIO line back to the opposite value
2131         nmiOutLine.set_value(!nmiOutConfig.polarity);
2132         lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
2133         if (ec)
2134         {
2135             // operation_aborted is expected if timer is canceled before
2136             // completion.
2137             if (ec != boost::asio::error::operation_aborted)
2138             {
2139                 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2140                            "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2141                            ec.message());
2142             }
2143         }
2144     });
2145     // log to redfish
2146     nmiDiagIntLog();
2147     lg2::info("NMI out action completed");
2148     // reset Enable Property
2149     nmiSetEnableProperty(false);
2150 }
2151 
2152 static void nmiSourcePropertyMonitor(void)
2153 {
2154     lg2::info("NMI Source Property Monitor");
2155 
2156     static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2157         std::make_unique<sdbusplus::bus::match_t>(
2158             *conn,
2159             "type='signal',interface='org.freedesktop.DBus.Properties',"
2160             "member='PropertiesChanged',"
2161             "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
2162             [](sdbusplus::message_t& msg) {
2163         std::string interfaceName;
2164         boost::container::flat_map<std::string, std::variant<bool, std::string>>
2165             propertiesChanged;
2166         std::string state;
2167         bool value = true;
2168         try
2169         {
2170             msg.read(interfaceName, propertiesChanged);
2171             if (propertiesChanged.begin()->first == "Enabled")
2172             {
2173                 value = std::get<bool>(propertiesChanged.begin()->second);
2174                 lg2::info("NMI Enabled propertiesChanged value: {VALUE}",
2175                           "VALUE", value);
2176                 nmiEnabled = value;
2177                 if (nmiEnabled)
2178                 {
2179                     nmiReset();
2180                 }
2181             }
2182         }
2183         catch (const std::exception& e)
2184         {
2185             lg2::error("Unable to read NMI source: {ERROR}", "ERROR", e);
2186             return;
2187         }
2188     });
2189 }
2190 
2191 static void setNmiSource()
2192 {
2193     conn->async_method_call(
2194         [](boost::system::error_code ec) {
2195         if (ec)
2196         {
2197             lg2::error("failed to set NMI source");
2198         }
2199     },
2200         "xyz.openbmc_project.Settings",
2201         "/xyz/openbmc_project/Chassis/Control/NMISource",
2202         "org.freedesktop.DBus.Properties", "Set",
2203         "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
2204         std::variant<std::string>{
2205             "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
2206     // set Enable Property
2207     nmiSetEnableProperty(true);
2208 }
2209 
2210 static void nmiButtonHandler(bool state)
2211 {
2212     // Don't handle event if host not running and config doesn't force it
2213     if (!nmiWhenPoweredOff &&
2214         getHostState(powerState) !=
2215             "xyz.openbmc_project.State.Host.HostState.Running")
2216     {
2217         return;
2218     }
2219 
2220     bool asserted = state == nmiButtonConfig.polarity;
2221     nmiButtonIface->set_property("ButtonPressed", asserted);
2222     if (asserted)
2223     {
2224         nmiButtonPressLog();
2225         if (nmiButtonMasked)
2226         {
2227             lg2::info("NMI button press masked");
2228         }
2229         else
2230         {
2231             setNmiSource();
2232         }
2233     }
2234 }
2235 
2236 static void idButtonHandler(bool state)
2237 {
2238     bool asserted = state == idButtonConfig.polarity;
2239     idButtonIface->set_property("ButtonPressed", asserted);
2240 }
2241 
2242 static void pltRstHandler(bool pltRst)
2243 {
2244     if (pltRst)
2245     {
2246         sendPowerControlEvent(Event::pltRstDeAssert);
2247     }
2248     else
2249     {
2250         sendPowerControlEvent(Event::pltRstAssert);
2251     }
2252 }
2253 
2254 [[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
2255 {
2256     std::string interfaceName;
2257     boost::container::flat_map<std::string, std::variant<bool>>
2258         propertiesChanged;
2259     try
2260     {
2261         msg.read(interfaceName, propertiesChanged);
2262     }
2263     catch (const std::exception& e)
2264     {
2265         lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
2266         return;
2267     }
2268     if (propertiesChanged.empty())
2269     {
2270         lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
2271         return;
2272     }
2273 
2274     for (auto& [property, value] : propertiesChanged)
2275     {
2276         if (property == "ESpiPlatformReset")
2277         {
2278             bool* pltRst = std::get_if<bool>(&value);
2279             if (pltRst == nullptr)
2280             {
2281                 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
2282                 return;
2283             }
2284             pltRstHandler(*pltRst);
2285         }
2286     }
2287 }
2288 
2289 static void postCompleteHandler(bool state)
2290 {
2291     bool asserted = state == postCompleteConfig.polarity;
2292     if (asserted)
2293     {
2294         sendPowerControlEvent(Event::postCompleteAssert);
2295         setOperatingSystemState(OperatingSystemStateStage::Standby);
2296     }
2297     else
2298     {
2299         sendPowerControlEvent(Event::postCompleteDeAssert);
2300         setOperatingSystemState(OperatingSystemStateStage::Inactive);
2301     }
2302 }
2303 
2304 static int loadConfigValues()
2305 {
2306     const std::string configFilePath =
2307         "/usr/share/x86-power-control/power-config-host" + power_control::node +
2308         ".json";
2309     std::ifstream configFile(configFilePath.c_str());
2310     if (!configFile.is_open())
2311     {
2312         lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2313                    "PATH", configFilePath);
2314         return -1;
2315     }
2316     auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
2317 
2318     if (jsonData.is_discarded())
2319     {
2320         lg2::error("Power config readings JSON parser failure");
2321         return -1;
2322     }
2323     auto gpios = jsonData["gpio_configs"];
2324     auto timers = jsonData["timing_configs"];
2325 
2326     ConfigData* tempGpioData;
2327 
2328     for (nlohmann::json& gpioConfig : gpios)
2329     {
2330         if (!gpioConfig.contains("Name"))
2331         {
2332             lg2::error("The 'Name' field must be defined in Json file");
2333             return -1;
2334         }
2335 
2336         // Iterate through the powersignal map to check if the gpio json config
2337         // entry is valid
2338         std::string gpioName = gpioConfig["Name"];
2339         auto signalMapIter = powerSignalMap.find(gpioName);
2340         if (signalMapIter == powerSignalMap.end())
2341         {
2342             lg2::error(
2343                 "{GPIO_NAME} is not a recognized power-control signal name",
2344                 "GPIO_NAME", gpioName);
2345             return -1;
2346         }
2347 
2348         // assign the power signal name to the corresponding structure reference
2349         // from map then fillup the structure with coressponding json config
2350         // value
2351         tempGpioData = signalMapIter->second;
2352         tempGpioData->name = gpioName;
2353 
2354         if (!gpioConfig.contains("Type"))
2355         {
2356             lg2::error("The \'Type\' field must be defined in Json file");
2357             return -1;
2358         }
2359 
2360         std::string signalType = gpioConfig["Type"];
2361         if (signalType == "GPIO")
2362         {
2363             tempGpioData->type = ConfigType::GPIO;
2364         }
2365         else if (signalType == "DBUS")
2366         {
2367             tempGpioData->type = ConfigType::DBUS;
2368         }
2369         else
2370         {
2371             lg2::error("{TYPE} is not a recognized power-control signal type",
2372                        "TYPE", signalType);
2373             return -1;
2374         }
2375 
2376         if (tempGpioData->type == ConfigType::GPIO)
2377         {
2378             if (gpioConfig.contains("LineName"))
2379             {
2380                 tempGpioData->lineName = gpioConfig["LineName"];
2381             }
2382             else
2383             {
2384                 lg2::error(
2385                     "The \'LineName\' field must be defined for GPIO configuration");
2386                 return -1;
2387             }
2388             if (gpioConfig.contains("Polarity"))
2389             {
2390                 std::string polarity = gpioConfig["Polarity"];
2391                 if (polarity == "ActiveLow")
2392                 {
2393                     tempGpioData->polarity = false;
2394                 }
2395                 else if (polarity == "ActiveHigh")
2396                 {
2397                     tempGpioData->polarity = true;
2398                 }
2399                 else
2400                 {
2401                     lg2::error(
2402                         "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2403                         "POLARITY", polarity);
2404                     return -1;
2405                 }
2406             }
2407             else
2408             {
2409                 lg2::error("Polarity field not found for {GPIO_NAME}",
2410                            "GPIO_NAME", tempGpioData->lineName);
2411                 return -1;
2412             }
2413         }
2414         else
2415         {
2416             // if dbus based gpio config is defined read and update the dbus
2417             // params corresponding to the gpio config instance
2418             for (auto& [key, dbusParamName] : dbusParams)
2419             {
2420                 if (!gpioConfig.contains(dbusParamName))
2421                 {
2422                     lg2::error(
2423                         "The {DBUS_NAME} field must be defined for Dbus configuration ",
2424                         "DBUS_NAME", dbusParamName);
2425                     return -1;
2426                 }
2427             }
2428             tempGpioData->dbusName =
2429                 gpioConfig[dbusParams[DbusConfigType::name]];
2430             tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
2431             tempGpioData->interface =
2432                 gpioConfig[dbusParams[DbusConfigType::interface]];
2433             tempGpioData->lineName =
2434                 gpioConfig[dbusParams[DbusConfigType::property]];
2435         }
2436     }
2437 
2438     // read and store the timer values from json config to Timer Map
2439     for (auto& [key, timerValue] : TimerMap)
2440     {
2441         if (timers.contains(key.c_str()))
2442         {
2443             timerValue = timers[key.c_str()];
2444         }
2445     }
2446 
2447     // If "events_configs" key is not in json config, fallback to null
2448     auto events = jsonData.value("event_configs",
2449                                  nlohmann::json(nlohmann::json::value_t::null));
2450     if (events.is_object())
2451     {
2452         nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2453     }
2454 
2455     return 0;
2456 }
2457 
2458 static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2459                                 const std::string& lineName, bool& value)
2460 {
2461     std::string thresholdInterface;
2462     std::string event;
2463     boost::container::flat_map<std::string, std::variant<bool>>
2464         propertiesChanged;
2465     try
2466     {
2467         msg.read(thresholdInterface, propertiesChanged);
2468         if (propertiesChanged.empty())
2469         {
2470             return false;
2471         }
2472 
2473         event = propertiesChanged.begin()->first;
2474         if (event.empty() || event != lineName)
2475         {
2476             return false;
2477         }
2478 
2479         value = std::get<bool>(propertiesChanged.begin()->second);
2480         return true;
2481     }
2482     catch (const std::exception& e)
2483     {
2484         lg2::error(
2485             "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
2486             "DBUS_NAME", lineName, "ERROR", e);
2487         return false;
2488     }
2489 }
2490 
2491 static sdbusplus::bus::match_t
2492     dbusGPIOMatcher(const ConfigData& cfg, std::function<void(bool)> onMatch)
2493 {
2494     auto pulseEventMatcherCallback = [&cfg,
2495                                       onMatch](sdbusplus::message_t& msg) {
2496         bool value = false;
2497         if (!getDbusMsgGPIOState(msg, cfg.lineName, value))
2498         {
2499             return;
2500         }
2501         onMatch(value);
2502     };
2503 
2504     return sdbusplus::bus::match_t(
2505         static_cast<sdbusplus::bus_t&>(*conn),
2506         "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2507         "PropertiesChanged',arg0='" +
2508             cfg.interface + "'",
2509         std::move(pulseEventMatcherCallback));
2510 }
2511 
2512 // D-Bus property read functions
2513 void reschedulePropertyRead(const ConfigData& configData);
2514 
2515 int getProperty(const ConfigData& configData)
2516 {
2517     std::variant<bool> resp;
2518 
2519     try
2520     {
2521         auto method = conn->new_method_call(
2522             configData.dbusName.c_str(), configData.path.c_str(),
2523             "org.freedesktop.DBus.Properties", "Get");
2524         method.append(configData.interface.c_str(),
2525                       configData.lineName.c_str());
2526 
2527         auto reply = conn->call(method);
2528         if (reply.is_method_error())
2529         {
2530             lg2::error(
2531                 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
2532                 "PROPERTY", configData.lineName, "INTERFACE",
2533                 configData.interface, "PATH", configData.path);
2534             return -1;
2535         }
2536 
2537         reply.read(resp);
2538     }
2539     catch (const sdbusplus::exception_t& e)
2540     {
2541         lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
2542                    configData.lineName, "WHAT", e.what());
2543         reschedulePropertyRead(configData);
2544         return -1;
2545     }
2546 
2547     auto respValue = std::get_if<bool>(&resp);
2548     if (!respValue)
2549     {
2550         lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2551                    "PROPERTY", configData.lineName);
2552         return -1;
2553     }
2554     return (*respValue);
2555 }
2556 
2557 void setInitialValue(const ConfigData& configData, bool initialValue)
2558 {
2559     if (configData.name == "PowerOk")
2560     {
2561         powerState = (initialValue ? PowerState::on : PowerState::off);
2562         hostIface->set_property("CurrentHostState",
2563                                 std::string(getHostState(powerState)));
2564     }
2565     else if (configData.name == "PowerButton")
2566     {
2567         powerButtonIface->set_property("ButtonPressed", !initialValue);
2568     }
2569     else if (configData.name == "ResetButton")
2570     {
2571         resetButtonIface->set_property("ButtonPressed", !initialValue);
2572     }
2573     else if (configData.name == "NMIButton")
2574     {
2575         nmiButtonIface->set_property("ButtonPressed", !initialValue);
2576     }
2577     else if (configData.name == "IdButton")
2578     {
2579         idButtonIface->set_property("ButtonPressed", !initialValue);
2580     }
2581     else if (configData.name == "PostComplete")
2582     {
2583         OperatingSystemStateStage osState =
2584             (initialValue ? OperatingSystemStateStage::Inactive
2585                           : OperatingSystemStateStage::Standby);
2586         setOperatingSystemState(osState);
2587     }
2588     else
2589     {
2590         lg2::error("Unknown name {NAME}", "NAME", configData.name);
2591     }
2592 }
2593 
2594 void reschedulePropertyRead(const ConfigData& configData)
2595 {
2596     auto item = dBusRetryTimers.find(configData.name);
2597 
2598     if (item == dBusRetryTimers.end())
2599     {
2600         auto newItem = dBusRetryTimers.insert(
2601             {configData.name, boost::asio::steady_timer(io)});
2602 
2603         if (!newItem.second)
2604         {
2605             lg2::error("Failed to add new timer for {NAME}", "NAME",
2606                        configData.name);
2607             return;
2608         }
2609 
2610         item = newItem.first;
2611     }
2612 
2613     auto& timer = item->second;
2614     timer.expires_after(
2615         std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2616     timer.async_wait([&configData](const boost::system::error_code ec) {
2617         if (ec)
2618         {
2619             lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2620                        configData.name, "MSG", ec.message());
2621             dBusRetryTimers.erase(configData.name);
2622             return;
2623         }
2624 
2625         int property = getProperty(configData);
2626 
2627         if (property >= 0)
2628         {
2629             setInitialValue(configData, (property > 0));
2630             dBusRetryTimers.erase(configData.name);
2631         }
2632     });
2633 }
2634 } // namespace power_control
2635 
2636 int main(int argc, char* argv[])
2637 {
2638     using namespace power_control;
2639 
2640     if (argc > 1)
2641     {
2642         node = argv[1];
2643     }
2644     lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2645               node);
2646 
2647     conn = std::make_shared<sdbusplus::asio::connection>(io);
2648 
2649     // Load GPIO's through json config file
2650     if (loadConfigValues() == -1)
2651     {
2652         lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
2653     }
2654     /* Currently for single host based systems additional busname is added
2655     with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2656     Going forward for single hosts the old bus name without zero numbering
2657     will be removed when all other applications adapted to the
2658     bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2659 
2660     if (node == "0")
2661     {
2662         // Request all the dbus names
2663         conn->request_name(hostDbusName.c_str());
2664         conn->request_name(chassisDbusName.c_str());
2665         conn->request_name(osDbusName.c_str());
2666         conn->request_name(buttonDbusName.c_str());
2667         conn->request_name(nmiDbusName.c_str());
2668         conn->request_name(rstCauseDbusName.c_str());
2669     }
2670 
2671     hostDbusName += node;
2672     chassisDbusName += node;
2673     osDbusName += node;
2674     buttonDbusName += node;
2675     nmiDbusName += node;
2676     rstCauseDbusName += node;
2677 
2678     // Request all the dbus names
2679     conn->request_name(hostDbusName.c_str());
2680     conn->request_name(chassisDbusName.c_str());
2681     conn->request_name(osDbusName.c_str());
2682     conn->request_name(buttonDbusName.c_str());
2683     conn->request_name(nmiDbusName.c_str());
2684     conn->request_name(rstCauseDbusName.c_str());
2685 
2686     if (sioPwrGoodConfig.lineName.empty() ||
2687         sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
2688     {
2689         sioEnabled = false;
2690         lg2::info("SIO control GPIOs not defined, disable SIO support.");
2691     }
2692 
2693     // Request PS_PWROK GPIO events
2694     if (powerOkConfig.type == ConfigType::GPIO)
2695     {
2696         if (!requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler,
2697                                psPowerOKLine, psPowerOKEvent))
2698         {
2699             return -1;
2700         }
2701     }
2702     else if (powerOkConfig.type == ConfigType::DBUS)
2703     {
2704         static sdbusplus::bus::match_t powerOkEventMonitor =
2705             power_control::dbusGPIOMatcher(powerOkConfig, psPowerOKHandler);
2706     }
2707     else
2708     {
2709         lg2::error("PowerOk name should be configured from json config file");
2710         return -1;
2711     }
2712 
2713     if (sioEnabled == true)
2714     {
2715         // Request SIO_POWER_GOOD GPIO events
2716         if (sioPwrGoodConfig.type == ConfigType::GPIO)
2717         {
2718             if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
2719                                    sioPowerGoodHandler, sioPowerGoodLine,
2720                                    sioPowerGoodEvent))
2721             {
2722                 return -1;
2723             }
2724         }
2725         else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2726         {
2727             static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
2728                 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2729                                                sioPowerGoodHandler);
2730         }
2731         else
2732         {
2733             lg2::error(
2734                 "sioPwrGood name should be configured from json config file");
2735             return -1;
2736         }
2737 
2738         // Request SIO_ONCONTROL GPIO events
2739         if (sioOnControlConfig.type == ConfigType::GPIO)
2740         {
2741             if (!requestGPIOEvents(sioOnControlConfig.lineName,
2742                                    sioOnControlHandler, sioOnControlLine,
2743                                    sioOnControlEvent))
2744             {
2745                 return -1;
2746             }
2747         }
2748         else if (sioOnControlConfig.type == ConfigType::DBUS)
2749         {
2750             static sdbusplus::bus::match_t sioOnControlEventMonitor =
2751                 power_control::dbusGPIOMatcher(sioOnControlConfig,
2752                                                sioOnControlHandler);
2753         }
2754         else
2755         {
2756             lg2::error(
2757                 "sioOnControl name should be configured from jsonconfig file\n");
2758             return -1;
2759         }
2760 
2761         // Request SIO_S5 GPIO events
2762         if (sioS5Config.type == ConfigType::GPIO)
2763         {
2764             if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
2765                                    sioS5Line, sioS5Event))
2766             {
2767                 return -1;
2768             }
2769         }
2770         else if (sioS5Config.type == ConfigType::DBUS)
2771         {
2772             static sdbusplus::bus::match_t sioS5EventMonitor =
2773                 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
2774         }
2775         else
2776         {
2777             lg2::error("sioS5 name should be configured from json config file");
2778             return -1;
2779         }
2780     }
2781 
2782     // Request POWER_BUTTON GPIO events
2783     if (powerButtonConfig.type == ConfigType::GPIO)
2784     {
2785         if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2786                                powerButtonLine, powerButtonEvent))
2787         {
2788             return -1;
2789         }
2790     }
2791     else if (powerButtonConfig.type == ConfigType::DBUS)
2792     {
2793         static sdbusplus::bus::match_t powerButtonEventMonitor =
2794             power_control::dbusGPIOMatcher(powerButtonConfig,
2795                                            powerButtonHandler);
2796     }
2797 
2798     // Request RESET_BUTTON GPIO events
2799     if (resetButtonConfig.type == ConfigType::GPIO)
2800     {
2801         if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2802                                resetButtonLine, resetButtonEvent))
2803         {
2804             return -1;
2805         }
2806     }
2807     else if (resetButtonConfig.type == ConfigType::DBUS)
2808     {
2809         static sdbusplus::bus::match_t resetButtonEventMonitor =
2810             power_control::dbusGPIOMatcher(resetButtonConfig,
2811                                            resetButtonHandler);
2812     }
2813 
2814     // Request NMI_BUTTON GPIO events
2815     if (nmiButtonConfig.type == ConfigType::GPIO)
2816     {
2817         if (!nmiButtonConfig.lineName.empty())
2818         {
2819             requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
2820                               nmiButtonLine, nmiButtonEvent);
2821         }
2822     }
2823     else if (nmiButtonConfig.type == ConfigType::DBUS)
2824     {
2825         static sdbusplus::bus::match_t nmiButtonEventMonitor =
2826             power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
2827     }
2828 
2829     // Request ID_BUTTON GPIO events
2830     if (idButtonConfig.type == ConfigType::GPIO)
2831     {
2832         if (!idButtonConfig.lineName.empty())
2833         {
2834             requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
2835                               idButtonLine, idButtonEvent);
2836         }
2837     }
2838     else if (idButtonConfig.type == ConfigType::DBUS)
2839     {
2840         static sdbusplus::bus::match_t idButtonEventMonitor =
2841             power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
2842     }
2843 
2844 #ifdef USE_PLT_RST
2845     sdbusplus::bus::match_t pltRstMatch(
2846         *conn,
2847         "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2848         "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
2849         hostMiscHandler);
2850 #endif
2851 
2852     // Request POST_COMPLETE GPIO events
2853     if (postCompleteConfig.type == ConfigType::GPIO)
2854     {
2855         if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2856                                postCompleteLine, postCompleteEvent))
2857         {
2858             return -1;
2859         }
2860     }
2861     else if (postCompleteConfig.type == ConfigType::DBUS)
2862     {
2863         static sdbusplus::bus::match_t postCompleteEventMonitor =
2864             power_control::dbusGPIOMatcher(postCompleteConfig,
2865                                            postCompleteHandler);
2866     }
2867     else
2868     {
2869         lg2::error(
2870             "postComplete name should be configured from json config file");
2871         return -1;
2872     }
2873 
2874     // initialize NMI_OUT GPIO.
2875     if (!nmiOutConfig.lineName.empty())
2876     {
2877         setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2878                       nmiOutLine);
2879     }
2880 
2881     // Initialize POWER_OUT and RESET_OUT GPIO.
2882     gpiod::line line;
2883     if (!powerOutConfig.lineName.empty())
2884     {
2885         if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2886                            line))
2887         {
2888             return -1;
2889         }
2890     }
2891     else
2892     {
2893         lg2::error("powerOut name should be configured from json config file");
2894         return -1;
2895     }
2896 
2897     if (!resetOutConfig.lineName.empty())
2898     {
2899         if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2900                            line))
2901         {
2902             return -1;
2903         }
2904     }
2905     else
2906     {
2907         lg2::error("ResetOut name should be configured from json config file");
2908         return -1;
2909     }
2910     // Release line
2911     line.reset();
2912 
2913     // Initialize the power state and operating system state
2914     powerState = PowerState::off;
2915     operatingSystemState = OperatingSystemStateStage::Inactive;
2916     // Check power good
2917 
2918     if (powerOkConfig.type == ConfigType::GPIO)
2919     {
2920         if (psPowerOKLine.get_value() > 0 ||
2921             (sioEnabled &&
2922              (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
2923         {
2924             powerState = PowerState::on;
2925         }
2926     }
2927     else
2928     {
2929         if (getProperty(powerOkConfig))
2930         {
2931             powerState = PowerState::on;
2932         }
2933     }
2934     // Check if we need to start the Power Restore policy
2935     if (powerState != PowerState::on)
2936     {
2937         powerRestore.run();
2938     }
2939 
2940     if (nmiOutLine)
2941         nmiSourcePropertyMonitor();
2942 
2943     lg2::info("Initializing power state.");
2944     logStateTransition(powerState);
2945 
2946     // Power Control Service
2947     sdbusplus::asio::object_server hostServer =
2948         sdbusplus::asio::object_server(conn);
2949 
2950     // Power Control Interface
2951     hostIface =
2952         hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
2953                                  "xyz.openbmc_project.State.Host");
2954     // Interface for IPMI/Redfish initiated host state transitions
2955     hostIface->register_property(
2956         "RequestedHostTransition",
2957         std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2958         [](const std::string& requested, std::string& resp) {
2959         if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2960         {
2961             // if power button is masked, ignore this
2962             if (!powerButtonMask)
2963             {
2964                 sendPowerControlEvent(Event::gracefulPowerOffRequest);
2965                 addRestartCause(RestartCause::command);
2966             }
2967             else
2968             {
2969                 lg2::info("Power Button Masked.");
2970                 throw std::invalid_argument("Transition Request Masked");
2971                 return 0;
2972             }
2973         }
2974         else if (requested == "xyz.openbmc_project.State.Host.Transition.On")
2975         {
2976             // if power button is masked, ignore this
2977             if (!powerButtonMask)
2978             {
2979                 sendPowerControlEvent(Event::powerOnRequest);
2980                 addRestartCause(RestartCause::command);
2981             }
2982             else
2983             {
2984                 lg2::info("Power Button Masked.");
2985                 throw std::invalid_argument("Transition Request Masked");
2986                 return 0;
2987             }
2988         }
2989         else if (requested ==
2990                  "xyz.openbmc_project.State.Host.Transition.Reboot")
2991         {
2992             // if power button is masked, ignore this
2993             if (!powerButtonMask)
2994             {
2995                 sendPowerControlEvent(Event::powerCycleRequest);
2996                 addRestartCause(RestartCause::command);
2997             }
2998             else
2999             {
3000                 lg2::info("Power Button Masked.");
3001                 throw std::invalid_argument("Transition Request Masked");
3002                 return 0;
3003             }
3004         }
3005         else if (requested ==
3006                  "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3007         {
3008             // if reset button is masked, ignore this
3009             if (!resetButtonMask)
3010             {
3011                 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3012                 addRestartCause(RestartCause::command);
3013             }
3014             else
3015             {
3016                 lg2::info("Reset Button Masked.");
3017                 throw std::invalid_argument("Transition Request Masked");
3018                 return 0;
3019             }
3020         }
3021         else if (requested ==
3022                  "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3023         {
3024             // if reset button is masked, ignore this
3025             if (!resetButtonMask)
3026             {
3027                 sendPowerControlEvent(Event::resetRequest);
3028                 addRestartCause(RestartCause::command);
3029             }
3030             else
3031             {
3032                 lg2::info("Reset Button Masked.");
3033                 throw std::invalid_argument("Transition Request Masked");
3034                 return 0;
3035             }
3036         }
3037         else
3038         {
3039             lg2::error("Unrecognized host state transition request.");
3040             throw std::invalid_argument("Unrecognized Transition Request");
3041             return 0;
3042         }
3043         resp = requested;
3044         return 1;
3045     });
3046     hostIface->register_property("CurrentHostState",
3047                                  std::string(getHostState(powerState)));
3048 
3049     hostIface->initialize();
3050 
3051     // Chassis Control Service
3052     sdbusplus::asio::object_server chassisServer =
3053         sdbusplus::asio::object_server(conn);
3054 
3055     // Chassis Control Interface
3056     chassisIface =
3057         chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
3058                                     "xyz.openbmc_project.State.Chassis");
3059 
3060     chassisIface->register_property(
3061         "RequestedPowerTransition",
3062         std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3063         [](const std::string& requested, std::string& resp) {
3064         if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
3065         {
3066             // if power button is masked, ignore this
3067             if (!powerButtonMask)
3068             {
3069                 sendPowerControlEvent(Event::powerOffRequest);
3070                 addRestartCause(RestartCause::command);
3071             }
3072             else
3073             {
3074                 lg2::info("Power Button Masked.");
3075                 throw std::invalid_argument("Transition Request Masked");
3076                 return 0;
3077             }
3078         }
3079         else if (requested == "xyz.openbmc_project.State.Chassis.Transition.On")
3080         {
3081             // if power button is masked, ignore this
3082             if (!powerButtonMask)
3083             {
3084                 sendPowerControlEvent(Event::powerOnRequest);
3085                 addRestartCause(RestartCause::command);
3086             }
3087             else
3088             {
3089                 lg2::info("Power Button Masked.");
3090                 throw std::invalid_argument("Transition Request Masked");
3091                 return 0;
3092             }
3093         }
3094         else if (requested ==
3095                  "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3096         {
3097             // if power button is masked, ignore this
3098             if (!powerButtonMask)
3099             {
3100                 sendPowerControlEvent(Event::powerCycleRequest);
3101                 addRestartCause(RestartCause::command);
3102             }
3103             else
3104             {
3105                 lg2::info("Power Button Masked.");
3106                 throw std::invalid_argument("Transition Request Masked");
3107                 return 0;
3108             }
3109         }
3110         else
3111         {
3112             lg2::error("Unrecognized chassis state transition request.");
3113             throw std::invalid_argument("Unrecognized Transition Request");
3114             return 0;
3115         }
3116         resp = requested;
3117         return 1;
3118     });
3119     chassisIface->register_property("CurrentPowerState",
3120                                     std::string(getChassisState(powerState)));
3121     chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
3122 
3123     chassisIface->initialize();
3124 
3125 #ifdef CHASSIS_SYSTEM_RESET
3126     // Chassis System Service
3127     sdbusplus::asio::object_server chassisSysServer =
3128         sdbusplus::asio::object_server(conn);
3129 
3130     // Chassis System Interface
3131     chassisSysIface = chassisSysServer.add_interface(
3132         "/xyz/openbmc_project/state/chassis_system0",
3133         "xyz.openbmc_project.State.Chassis");
3134 
3135     chassisSysIface->register_property(
3136         "RequestedPowerTransition",
3137         std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3138         [](const std::string& requested, std::string& resp) {
3139         if (requested ==
3140             "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3141         {
3142             systemReset();
3143             addRestartCause(RestartCause::command);
3144         }
3145         else
3146         {
3147             lg2::error("Unrecognized chassis system state transition request.");
3148             throw std::invalid_argument("Unrecognized Transition Request");
3149             return 0;
3150         }
3151         resp = requested;
3152         return 1;
3153     });
3154     chassisSysIface->register_property(
3155         "CurrentPowerState", std::string(getChassisState(powerState)));
3156     chassisSysIface->register_property("LastStateChangeTime",
3157                                        getCurrentTimeMs());
3158 
3159     chassisSysIface->initialize();
3160 
3161     if (!slotPowerConfig.lineName.empty())
3162     {
3163         if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3164         {
3165             return -1;
3166         }
3167 
3168         slotPowerState = SlotPowerState::off;
3169         if (slotPowerLine.get_value() > 0)
3170         {
3171             slotPowerState = SlotPowerState::on;
3172         }
3173 
3174         chassisSlotIface = chassisSysServer.add_interface(
3175             "/xyz/openbmc_project/state/chassis_system" + node,
3176             "xyz.openbmc_project.State.Chassis");
3177         chassisSlotIface->register_property(
3178             "RequestedPowerTransition",
3179             std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3180             [](const std::string& requested, std::string& resp) {
3181             if (requested == "xyz.openbmc_project.State.Chassis.Transition.On")
3182             {
3183                 slotPowerOn();
3184             }
3185             else if (requested ==
3186                      "xyz.openbmc_project.State.Chassis.Transition.Off")
3187             {
3188                 slotPowerOff();
3189             }
3190             else if (requested ==
3191                      "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3192             {
3193                 slotPowerCycle();
3194             }
3195             else
3196             {
3197                 lg2::error(
3198                     "Unrecognized chassis system state transition request.\n");
3199                 throw std::invalid_argument("Unrecognized Transition Request");
3200                 return 0;
3201             }
3202             resp = requested;
3203             return 1;
3204         });
3205         chassisSlotIface->register_property(
3206             "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3207         chassisSlotIface->register_property("LastStateChangeTime",
3208                                             getCurrentTimeMs());
3209         chassisSlotIface->initialize();
3210     }
3211 #endif
3212     // Buttons Service
3213     sdbusplus::asio::object_server buttonsServer =
3214         sdbusplus::asio::object_server(conn);
3215 
3216     if (!powerButtonConfig.lineName.empty())
3217     {
3218         // Power Button Interface
3219         power_control::powerButtonIface = buttonsServer.add_interface(
3220             "/xyz/openbmc_project/chassis/buttons/power",
3221             "xyz.openbmc_project.Chassis.Buttons");
3222 
3223         powerButtonIface->register_property(
3224             "ButtonMasked", false, [](const bool requested, bool& current) {
3225             if (requested)
3226             {
3227                 if (powerButtonMask)
3228                 {
3229                     return 1;
3230                 }
3231                 if (!setGPIOOutput(powerOutConfig.lineName,
3232                                    !powerOutConfig.polarity, powerButtonMask))
3233                 {
3234                     throw std::runtime_error("Failed to request GPIO");
3235                     return 0;
3236                 }
3237                 lg2::info("Power Button Masked.");
3238             }
3239             else
3240             {
3241                 if (!powerButtonMask)
3242                 {
3243                     return 1;
3244                 }
3245                 lg2::info("Power Button Un-masked");
3246                 powerButtonMask.reset();
3247             }
3248             // Update the mask setting
3249             current = requested;
3250             return 1;
3251         });
3252 
3253         // Check power button state
3254         bool powerButtonPressed;
3255         if (powerButtonConfig.type == ConfigType::GPIO)
3256         {
3257             powerButtonPressed = powerButtonLine.get_value() == 0;
3258         }
3259         else
3260         {
3261             powerButtonPressed = getProperty(powerButtonConfig) == 0;
3262         }
3263 
3264         powerButtonIface->register_property("ButtonPressed",
3265                                             powerButtonPressed);
3266 
3267         powerButtonIface->initialize();
3268     }
3269 
3270     if (!resetButtonConfig.lineName.empty())
3271     {
3272         // Reset Button Interface
3273 
3274         resetButtonIface = buttonsServer.add_interface(
3275             "/xyz/openbmc_project/chassis/buttons/reset",
3276             "xyz.openbmc_project.Chassis.Buttons");
3277 
3278         resetButtonIface->register_property(
3279             "ButtonMasked", false, [](const bool requested, bool& current) {
3280             if (requested)
3281             {
3282                 if (resetButtonMask)
3283                 {
3284                     return 1;
3285                 }
3286                 if (!setGPIOOutput(resetOutConfig.lineName,
3287                                    !resetOutConfig.polarity, resetButtonMask))
3288                 {
3289                     throw std::runtime_error("Failed to request GPIO");
3290                     return 0;
3291                 }
3292                 lg2::info("Reset Button Masked.");
3293             }
3294             else
3295             {
3296                 if (!resetButtonMask)
3297                 {
3298                     return 1;
3299                 }
3300                 lg2::info("Reset Button Un-masked");
3301                 resetButtonMask.reset();
3302             }
3303             // Update the mask setting
3304             current = requested;
3305             return 1;
3306         });
3307 
3308         // Check reset button state
3309         bool resetButtonPressed;
3310         if (resetButtonConfig.type == ConfigType::GPIO)
3311         {
3312             resetButtonPressed = resetButtonLine.get_value() == 0;
3313         }
3314         else
3315         {
3316             resetButtonPressed = getProperty(resetButtonConfig) == 0;
3317         }
3318 
3319         resetButtonIface->register_property("ButtonPressed",
3320                                             resetButtonPressed);
3321 
3322         resetButtonIface->initialize();
3323     }
3324 
3325     if (nmiButtonLine)
3326     {
3327         // NMI Button Interface
3328         nmiButtonIface = buttonsServer.add_interface(
3329             "/xyz/openbmc_project/chassis/buttons/nmi",
3330             "xyz.openbmc_project.Chassis.Buttons");
3331 
3332         nmiButtonIface->register_property(
3333             "ButtonMasked", false, [](const bool requested, bool& current) {
3334             if (nmiButtonMasked == requested)
3335             {
3336                 // NMI button mask is already set as requested, so no change
3337                 return 1;
3338             }
3339             if (requested)
3340             {
3341                 lg2::info("NMI Button Masked.");
3342                 nmiButtonMasked = true;
3343             }
3344             else
3345             {
3346                 lg2::info("NMI Button Un-masked.");
3347                 nmiButtonMasked = false;
3348             }
3349             // Update the mask setting
3350             current = nmiButtonMasked;
3351             return 1;
3352         });
3353 
3354         // Check NMI button state
3355         bool nmiButtonPressed;
3356         if (nmiButtonConfig.type == ConfigType::GPIO)
3357         {
3358             nmiButtonPressed = nmiButtonLine.get_value() == 0;
3359         }
3360         else
3361         {
3362             nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3363         }
3364 
3365         nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
3366 
3367         nmiButtonIface->initialize();
3368     }
3369 
3370     if (nmiOutLine)
3371     {
3372         // NMI out Service
3373         sdbusplus::asio::object_server nmiOutServer =
3374             sdbusplus::asio::object_server(conn);
3375 
3376         // NMI out Interface
3377         nmiOutIface = nmiOutServer.add_interface(
3378             "/xyz/openbmc_project/control/host" + node + "/nmi",
3379             "xyz.openbmc_project.Control.Host.NMI");
3380         nmiOutIface->register_method("NMI", nmiReset);
3381         nmiOutIface->initialize();
3382     }
3383 
3384     if (idButtonLine)
3385     {
3386         // ID Button Interface
3387         idButtonIface = buttonsServer.add_interface(
3388             "/xyz/openbmc_project/chassis/buttons/id",
3389             "xyz.openbmc_project.Chassis.Buttons");
3390 
3391         // Check ID button state
3392         bool idButtonPressed;
3393         if (idButtonConfig.type == ConfigType::GPIO)
3394         {
3395             idButtonPressed = idButtonLine.get_value() == 0;
3396         }
3397         else
3398         {
3399             idButtonPressed = getProperty(idButtonConfig) == 0;
3400         }
3401 
3402         idButtonIface->register_property("ButtonPressed", idButtonPressed);
3403 
3404         idButtonIface->initialize();
3405     }
3406 
3407     // OS State Service
3408     sdbusplus::asio::object_server osServer =
3409         sdbusplus::asio::object_server(conn);
3410 
3411     // OS State Interface
3412     osIface = osServer.add_interface(
3413         "/xyz/openbmc_project/state/host" + node,
3414         "xyz.openbmc_project.State.OperatingSystem.Status");
3415 
3416     // Get the initial OS state based on POST complete
3417     //      0: Asserted, OS state is "Standby" (ready to boot)
3418     //      1: De-Asserted, OS state is "Inactive"
3419     OperatingSystemStateStage osState;
3420     if (postCompleteConfig.type == ConfigType::GPIO)
3421     {
3422         osState = postCompleteLine.get_value() > 0
3423                       ? OperatingSystemStateStage::Inactive
3424                       : OperatingSystemStateStage::Standby;
3425     }
3426     else
3427     {
3428         osState = getProperty(postCompleteConfig) > 0
3429                       ? OperatingSystemStateStage::Inactive
3430                       : OperatingSystemStateStage::Standby;
3431     }
3432 
3433     osIface->register_property(
3434         "OperatingSystemState",
3435         std::string(getOperatingSystemStateStage(osState)));
3436 
3437     osIface->initialize();
3438 
3439     // Restart Cause Service
3440     sdbusplus::asio::object_server restartCauseServer =
3441         sdbusplus::asio::object_server(conn);
3442 
3443     // Restart Cause Interface
3444     restartCauseIface = restartCauseServer.add_interface(
3445         "/xyz/openbmc_project/control/host" + node + "/restart_cause",
3446         "xyz.openbmc_project.Control.Host.RestartCause");
3447 
3448     restartCauseIface->register_property(
3449         "RestartCause",
3450         std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3451 
3452     restartCauseIface->register_property(
3453         "RequestedRestartCause",
3454         std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3455         [](const std::string& requested, std::string& resp) {
3456         if (requested ==
3457             "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3458         {
3459             addRestartCause(RestartCause::watchdog);
3460         }
3461         else
3462         {
3463             throw std::invalid_argument("Unrecognized RestartCause Request");
3464             return 0;
3465         }
3466 
3467         lg2::info("RestartCause requested: {RESTART_CAUSE}", "RESTART_CAUSE",
3468                   requested);
3469         resp = requested;
3470         return 1;
3471     });
3472 
3473     restartCauseIface->initialize();
3474 
3475     currentHostStateMonitor();
3476 
3477     if (!hpmStbyEnConfig.lineName.empty())
3478     {
3479         // Set to indicate BMC's power control module is ready to take
3480         // the inputs [PWR_GOOD] from the HPM FPGA
3481         gpiod::line hpmLine;
3482         if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3483                            hpmLine))
3484         {
3485             return -1;
3486         }
3487     }
3488 
3489     io.run();
3490 
3491     return 0;
3492 }
3493