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