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