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