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