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