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