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