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,
2722                       nmiOutLine);
2723     }
2724 
2725     // Initialize POWER_OUT and RESET_OUT GPIO.
2726     gpiod::line line;
2727     if (!powerOutConfig.lineName.empty())
2728     {
2729         if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2730                            line))
2731         {
2732             return -1;
2733         }
2734     }
2735     else
2736     {
2737         lg2::error("powerOut name should be configured from json config file");
2738         return -1;
2739     }
2740 
2741     if (!resetOutConfig.lineName.empty())
2742     {
2743         if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2744                            line))
2745         {
2746             return -1;
2747         }
2748     }
2749     else
2750     {
2751         lg2::error("ResetOut name should be configured from json config file");
2752         return -1;
2753     }
2754     // Release line
2755     line.reset();
2756 
2757     // Initialize the power state
2758     powerState = PowerState::off;
2759     // Check power good
2760 
2761     if (powerOkConfig.type == ConfigType::GPIO)
2762     {
2763         if (psPowerOKLine.get_value() > 0 ||
2764             (sioEnabled &&
2765              (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
2766         {
2767             powerState = PowerState::on;
2768         }
2769     }
2770     else
2771     {
2772         if (getProperty(powerOkConfig))
2773         {
2774             powerState = PowerState::on;
2775         }
2776     }
2777     // Check if we need to start the Power Restore policy
2778     if (powerState != PowerState::on)
2779     {
2780         powerRestore.run();
2781     }
2782 
2783     if (nmiOutLine)
2784         nmiSourcePropertyMonitor();
2785 
2786     lg2::info("Initializing power state.");
2787     logStateTransition(powerState);
2788 
2789     // Power Control Service
2790     sdbusplus::asio::object_server hostServer =
2791         sdbusplus::asio::object_server(conn);
2792 
2793     // Power Control Interface
2794     hostIface =
2795         hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
2796                                  "xyz.openbmc_project.State.Host");
2797     // Interface for IPMI/Redfish initiated host state transitions
2798     hostIface->register_property(
2799         "RequestedHostTransition",
2800         std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2801         [](const std::string& requested, std::string& resp) {
2802             if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2803             {
2804                 // if power button is masked, ignore this
2805                 if (!powerButtonMask)
2806                 {
2807                     sendPowerControlEvent(Event::gracefulPowerOffRequest);
2808                     addRestartCause(RestartCause::command);
2809                 }
2810                 else
2811                 {
2812                     lg2::info("Power Button Masked.");
2813                     throw std::invalid_argument("Transition Request Masked");
2814                     return 0;
2815                 }
2816             }
2817             else if (requested ==
2818                      "xyz.openbmc_project.State.Host.Transition.On")
2819             {
2820                 // if power button is masked, ignore this
2821                 if (!powerButtonMask)
2822                 {
2823                     sendPowerControlEvent(Event::powerOnRequest);
2824                     addRestartCause(RestartCause::command);
2825                 }
2826                 else
2827                 {
2828                     lg2::info("Power Button Masked.");
2829                     throw std::invalid_argument("Transition Request Masked");
2830                     return 0;
2831                 }
2832             }
2833             else if (requested ==
2834                      "xyz.openbmc_project.State.Host.Transition.Reboot")
2835             {
2836                 // if power button is masked, ignore this
2837                 if (!powerButtonMask)
2838                 {
2839                     sendPowerControlEvent(Event::powerCycleRequest);
2840                     addRestartCause(RestartCause::command);
2841                 }
2842                 else
2843                 {
2844                     lg2::info("Power Button Masked.");
2845                     throw std::invalid_argument("Transition Request Masked");
2846                     return 0;
2847                 }
2848             }
2849             else if (
2850                 requested ==
2851                 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
2852             {
2853                 // if reset button is masked, ignore this
2854                 if (!resetButtonMask)
2855                 {
2856                     sendPowerControlEvent(Event::gracefulPowerCycleRequest);
2857                     addRestartCause(RestartCause::command);
2858                 }
2859                 else
2860                 {
2861                     lg2::info("Reset Button Masked.");
2862                     throw std::invalid_argument("Transition Request Masked");
2863                     return 0;
2864                 }
2865             }
2866             else if (
2867                 requested ==
2868                 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
2869             {
2870                 // if reset button is masked, ignore this
2871                 if (!resetButtonMask)
2872                 {
2873                     sendPowerControlEvent(Event::resetRequest);
2874                     addRestartCause(RestartCause::command);
2875                 }
2876                 else
2877                 {
2878                     lg2::info("Reset Button Masked.");
2879                     throw std::invalid_argument("Transition Request Masked");
2880                     return 0;
2881                 }
2882             }
2883             else
2884             {
2885                 lg2::error("Unrecognized host state transition request.");
2886                 throw std::invalid_argument("Unrecognized Transition Request");
2887                 return 0;
2888             }
2889             resp = requested;
2890             return 1;
2891         });
2892     hostIface->register_property("CurrentHostState",
2893                                  std::string(getHostState(powerState)));
2894 
2895     hostIface->initialize();
2896 
2897     // Chassis Control Service
2898     sdbusplus::asio::object_server chassisServer =
2899         sdbusplus::asio::object_server(conn);
2900 
2901     // Chassis Control Interface
2902     chassisIface =
2903         chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
2904                                     "xyz.openbmc_project.State.Chassis");
2905 
2906     chassisIface->register_property(
2907         "RequestedPowerTransition",
2908         std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2909         [](const std::string& requested, std::string& resp) {
2910             if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2911             {
2912                 // if power button is masked, ignore this
2913                 if (!powerButtonMask)
2914                 {
2915                     sendPowerControlEvent(Event::powerOffRequest);
2916                     addRestartCause(RestartCause::command);
2917                 }
2918                 else
2919                 {
2920                     lg2::info("Power Button Masked.");
2921                     throw std::invalid_argument("Transition Request Masked");
2922                     return 0;
2923                 }
2924             }
2925             else if (requested ==
2926                      "xyz.openbmc_project.State.Chassis.Transition.On")
2927             {
2928                 // if power button is masked, ignore this
2929                 if (!powerButtonMask)
2930                 {
2931                     sendPowerControlEvent(Event::powerOnRequest);
2932                     addRestartCause(RestartCause::command);
2933                 }
2934                 else
2935                 {
2936                     lg2::info("Power Button Masked.");
2937                     throw std::invalid_argument("Transition Request Masked");
2938                     return 0;
2939                 }
2940             }
2941             else if (requested ==
2942                      "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2943             {
2944                 // if power button is masked, ignore this
2945                 if (!powerButtonMask)
2946                 {
2947                     sendPowerControlEvent(Event::powerCycleRequest);
2948                     addRestartCause(RestartCause::command);
2949                 }
2950                 else
2951                 {
2952                     lg2::info("Power Button Masked.");
2953                     throw std::invalid_argument("Transition Request Masked");
2954                     return 0;
2955                 }
2956             }
2957             else
2958             {
2959                 lg2::error("Unrecognized chassis state transition request.");
2960                 throw std::invalid_argument("Unrecognized Transition Request");
2961                 return 0;
2962             }
2963             resp = requested;
2964             return 1;
2965         });
2966     chassisIface->register_property("CurrentPowerState",
2967                                     std::string(getChassisState(powerState)));
2968     chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
2969 
2970     chassisIface->initialize();
2971 
2972 #ifdef CHASSIS_SYSTEM_RESET
2973     // Chassis System Service
2974     sdbusplus::asio::object_server chassisSysServer =
2975         sdbusplus::asio::object_server(conn);
2976 
2977     // Chassis System Interface
2978     chassisSysIface = chassisSysServer.add_interface(
2979         "/xyz/openbmc_project/state/chassis_system0",
2980         "xyz.openbmc_project.State.Chassis");
2981 
2982     chassisSysIface->register_property(
2983         "RequestedPowerTransition",
2984         std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
2985         [](const std::string& requested, std::string& resp) {
2986             if (requested ==
2987                 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2988             {
2989                 systemReset();
2990                 addRestartCause(RestartCause::command);
2991             }
2992             else
2993             {
2994                 lg2::error(
2995                     "Unrecognized chassis system state transition request.");
2996                 throw std::invalid_argument("Unrecognized Transition Request");
2997                 return 0;
2998             }
2999             resp = requested;
3000             return 1;
3001         });
3002     chassisSysIface->register_property(
3003         "CurrentPowerState", std::string(getChassisState(powerState)));
3004     chassisSysIface->register_property("LastStateChangeTime",
3005                                        getCurrentTimeMs());
3006 
3007     chassisSysIface->initialize();
3008 
3009     if (!slotPowerConfig.lineName.empty())
3010     {
3011         if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3012         {
3013             return -1;
3014         }
3015 
3016         slotPowerState = SlotPowerState::off;
3017         if (slotPowerLine.get_value() > 0)
3018         {
3019             slotPowerState = SlotPowerState::on;
3020         }
3021 
3022         chassisSlotIface = chassisSysServer.add_interface(
3023             "/xyz/openbmc_project/state/chassis_system" + node,
3024             "xyz.openbmc_project.State.Chassis");
3025         chassisSlotIface->register_property(
3026             "RequestedPowerTransition",
3027             std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3028             [](const std::string& requested, std::string& resp) {
3029                 if (requested ==
3030                     "xyz.openbmc_project.State.Chassis.Transition.On")
3031                 {
3032                     slotPowerOn();
3033                 }
3034                 else if (requested ==
3035                          "xyz.openbmc_project.State.Chassis.Transition.Off")
3036                 {
3037                     slotPowerOff();
3038                 }
3039                 else if (
3040                     requested ==
3041                     "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3042                 {
3043                     slotPowerCycle();
3044                 }
3045                 else
3046                 {
3047                     lg2::error(
3048                         "Unrecognized chassis system state transition request.\n");
3049                     throw std::invalid_argument(
3050                         "Unrecognized Transition Request");
3051                     return 0;
3052                 }
3053                 resp = requested;
3054                 return 1;
3055             });
3056         chassisSlotIface->register_property(
3057             "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3058         chassisSlotIface->register_property("LastStateChangeTime",
3059                                             getCurrentTimeMs());
3060         chassisSlotIface->initialize();
3061     }
3062 #endif
3063     // Buttons Service
3064     sdbusplus::asio::object_server buttonsServer =
3065         sdbusplus::asio::object_server(conn);
3066 
3067     if (!powerButtonConfig.lineName.empty())
3068     {
3069         // Power Button Interface
3070         power_control::powerButtonIface = buttonsServer.add_interface(
3071             "/xyz/openbmc_project/chassis/buttons/power",
3072             "xyz.openbmc_project.Chassis.Buttons");
3073 
3074         powerButtonIface->register_property(
3075             "ButtonMasked", false, [](const bool requested, bool& current) {
3076                 if (requested)
3077                 {
3078                     if (powerButtonMask)
3079                     {
3080                         return 1;
3081                     }
3082                     if (!setGPIOOutput(powerOutConfig.lineName,
3083                                        !powerOutConfig.polarity,
3084                                        powerButtonMask))
3085                     {
3086                         throw std::runtime_error("Failed to request GPIO");
3087                         return 0;
3088                     }
3089                     lg2::info("Power Button Masked.");
3090                 }
3091                 else
3092                 {
3093                     if (!powerButtonMask)
3094                     {
3095                         return 1;
3096                     }
3097                     lg2::info("Power Button Un-masked");
3098                     powerButtonMask.reset();
3099                 }
3100                 // Update the mask setting
3101                 current = requested;
3102                 return 1;
3103             });
3104 
3105         // Check power button state
3106         bool powerButtonPressed;
3107         if (powerButtonConfig.type == ConfigType::GPIO)
3108         {
3109             powerButtonPressed = powerButtonLine.get_value() == 0;
3110         }
3111         else
3112         {
3113             powerButtonPressed = getProperty(powerButtonConfig) == 0;
3114         }
3115 
3116         powerButtonIface->register_property("ButtonPressed",
3117                                             powerButtonPressed);
3118 
3119         powerButtonIface->initialize();
3120     }
3121 
3122     if (!resetButtonConfig.lineName.empty())
3123     {
3124         // Reset Button Interface
3125 
3126         resetButtonIface = buttonsServer.add_interface(
3127             "/xyz/openbmc_project/chassis/buttons/reset",
3128             "xyz.openbmc_project.Chassis.Buttons");
3129 
3130         resetButtonIface->register_property(
3131             "ButtonMasked", false, [](const bool requested, bool& current) {
3132                 if (requested)
3133                 {
3134                     if (resetButtonMask)
3135                     {
3136                         return 1;
3137                     }
3138                     if (!setGPIOOutput(resetOutConfig.lineName,
3139                                        !resetOutConfig.polarity,
3140                                        resetButtonMask))
3141                     {
3142                         throw std::runtime_error("Failed to request GPIO");
3143                         return 0;
3144                     }
3145                     lg2::info("Reset Button Masked.");
3146                 }
3147                 else
3148                 {
3149                     if (!resetButtonMask)
3150                     {
3151                         return 1;
3152                     }
3153                     lg2::info("Reset Button Un-masked");
3154                     resetButtonMask.reset();
3155                 }
3156                 // Update the mask setting
3157                 current = requested;
3158                 return 1;
3159             });
3160 
3161         // Check reset button state
3162         bool resetButtonPressed;
3163         if (resetButtonConfig.type == ConfigType::GPIO)
3164         {
3165             resetButtonPressed = resetButtonLine.get_value() == 0;
3166         }
3167         else
3168         {
3169             resetButtonPressed = getProperty(resetButtonConfig) == 0;
3170         }
3171 
3172         resetButtonIface->register_property("ButtonPressed",
3173                                             resetButtonPressed);
3174 
3175         resetButtonIface->initialize();
3176     }
3177 
3178     if (nmiButtonLine)
3179     {
3180         // NMI Button Interface
3181         nmiButtonIface = buttonsServer.add_interface(
3182             "/xyz/openbmc_project/chassis/buttons/nmi",
3183             "xyz.openbmc_project.Chassis.Buttons");
3184 
3185         nmiButtonIface->register_property(
3186             "ButtonMasked", false, [](const bool requested, bool& current) {
3187                 if (nmiButtonMasked == requested)
3188                 {
3189                     // NMI button mask is already set as requested, so no change
3190                     return 1;
3191                 }
3192                 if (requested)
3193                 {
3194                     lg2::info("NMI Button Masked.");
3195                     nmiButtonMasked = true;
3196                 }
3197                 else
3198                 {
3199                     lg2::info("NMI Button Un-masked.");
3200                     nmiButtonMasked = false;
3201                 }
3202                 // Update the mask setting
3203                 current = nmiButtonMasked;
3204                 return 1;
3205             });
3206 
3207         // Check NMI button state
3208         bool nmiButtonPressed;
3209         if (nmiButtonConfig.type == ConfigType::GPIO)
3210         {
3211             nmiButtonPressed = nmiButtonLine.get_value() == 0;
3212         }
3213         else
3214         {
3215             nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3216         }
3217 
3218         nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
3219 
3220         nmiButtonIface->initialize();
3221     }
3222 
3223     if (nmiOutLine)
3224     {
3225         // NMI out Service
3226         sdbusplus::asio::object_server nmiOutServer =
3227             sdbusplus::asio::object_server(conn);
3228 
3229         // NMI out Interface
3230         nmiOutIface = nmiOutServer.add_interface(
3231             "/xyz/openbmc_project/control/host" + node + "/nmi",
3232             "xyz.openbmc_project.Control.Host.NMI");
3233         nmiOutIface->register_method("NMI", nmiReset);
3234         nmiOutIface->initialize();
3235     }
3236 
3237     if (idButtonLine)
3238     {
3239         // ID Button Interface
3240         idButtonIface = buttonsServer.add_interface(
3241             "/xyz/openbmc_project/chassis/buttons/id",
3242             "xyz.openbmc_project.Chassis.Buttons");
3243 
3244         // Check ID button state
3245         bool idButtonPressed;
3246         if (idButtonConfig.type == ConfigType::GPIO)
3247         {
3248             idButtonPressed = idButtonLine.get_value() == 0;
3249         }
3250         else
3251         {
3252             idButtonPressed = getProperty(idButtonConfig) == 0;
3253         }
3254 
3255         idButtonIface->register_property("ButtonPressed", idButtonPressed);
3256 
3257         idButtonIface->initialize();
3258     }
3259 
3260     // OS State Service
3261     sdbusplus::asio::object_server osServer =
3262         sdbusplus::asio::object_server(conn);
3263 
3264     // OS State Interface
3265     osIface = osServer.add_interface(
3266         "/xyz/openbmc_project/state/os",
3267         "xyz.openbmc_project.State.OperatingSystem.Status");
3268 
3269     // Get the initial OS state based on POST complete
3270     //      0: Asserted, OS state is "Standby" (ready to boot)
3271     //      1: De-Asserted, OS state is "Inactive"
3272     OperatingSystemStateStage osState;
3273     if (postCompleteConfig.type == ConfigType::GPIO)
3274     {
3275         osState = postCompleteLine.get_value() > 0
3276                       ? OperatingSystemStateStage::Inactive
3277                       : OperatingSystemStateStage::Standby;
3278     }
3279     else
3280     {
3281         osState = getProperty(postCompleteConfig) > 0
3282                       ? OperatingSystemStateStage::Inactive
3283                       : OperatingSystemStateStage::Standby;
3284     }
3285 
3286     osIface->register_property(
3287         "OperatingSystemState",
3288         std::string(getOperatingSystemStateStage(osState)));
3289 
3290     osIface->initialize();
3291 
3292     // Restart Cause Service
3293     sdbusplus::asio::object_server restartCauseServer =
3294         sdbusplus::asio::object_server(conn);
3295 
3296     // Restart Cause Interface
3297     restartCauseIface = restartCauseServer.add_interface(
3298         "/xyz/openbmc_project/control/host" + node + "/restart_cause",
3299         "xyz.openbmc_project.Control.Host.RestartCause");
3300 
3301     restartCauseIface->register_property(
3302         "RestartCause",
3303         std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3304 
3305     restartCauseIface->register_property(
3306         "RequestedRestartCause",
3307         std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3308         [](const std::string& requested, std::string& resp) {
3309             if (requested ==
3310                 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3311             {
3312                 addRestartCause(RestartCause::watchdog);
3313             }
3314             else
3315             {
3316                 throw std::invalid_argument(
3317                     "Unrecognized RestartCause Request");
3318                 return 0;
3319             }
3320 
3321             lg2::info("RestartCause requested: {RESTART_CAUSE}",
3322                       "RESTART_CAUSE", requested);
3323             resp = requested;
3324             return 1;
3325         });
3326 
3327     restartCauseIface->initialize();
3328 
3329     currentHostStateMonitor();
3330 
3331     io.run();
3332 
3333     return 0;
3334 }
3335