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