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