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 = "/xyz/openbmc_project/control/host" +
950                                            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(
1077                 [this](const boost::system::error_code 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(const std::string& name,
1149                              const std::function<void(bool)>& eventHandler,
1150                              gpiod::line& line,
1151                              boost::asio::posix::stream_descriptor& event)
1152 {
1153     event.async_wait(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}", "GPIO_NAME",
1159                        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 == gpiod::line_event::RISING_EDGE);
1166         waitForGPIOEvent(name, eventHandler, line, event);
1167     });
1168 }
1169 
requestGPIOEvents(const std::string & name,const std::function<void (bool)> & handler,gpiod::line & gpioLine,boost::asio::posix::stream_descriptor & gpioEventDescriptor)1170 static bool requestGPIOEvents(
1171     const std::string& name, const std::function<void(bool)>& handler,
1172     gpiod::line& gpioLine,
1173     boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1174 {
1175     // Find the GPIO line
1176     gpioLine = gpiod::find_line(name);
1177     if (!gpioLine)
1178     {
1179         lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
1180         return false;
1181     }
1182 
1183     try
1184     {
1185         gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});
1186     }
1187     catch (const std::exception& e)
1188     {
1189         lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}",
1190                    "GPIO_NAME", name, "ERROR", e);
1191         return false;
1192     }
1193 
1194     int gpioLineFd = gpioLine.event_get_fd();
1195     if (gpioLineFd < 0)
1196     {
1197         lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name);
1198         return false;
1199     }
1200 
1201     gpioEventDescriptor.assign(gpioLineFd);
1202 
1203     waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor);
1204     return true;
1205 }
1206 
setGPIOOutput(const std::string & name,const int value,gpiod::line & gpioLine)1207 static bool setGPIOOutput(const std::string& name, const int value,
1208                           gpiod::line& gpioLine)
1209 {
1210     // Find the GPIO line
1211     gpioLine = gpiod::find_line(name);
1212     if (!gpioLine)
1213     {
1214         lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
1215         return false;
1216     }
1217 
1218     // Request GPIO output to specified value
1219     try
1220     {
1221         gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}},
1222                          value);
1223     }
1224     catch (const std::exception& e)
1225     {
1226         lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
1227                    name, "ERROR", e);
1228         return false;
1229     }
1230 
1231     lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1232               "GPIO_VALUE", value);
1233     return true;
1234 }
1235 
setMaskedGPIOOutputForMs(gpiod::line & maskedGPIOLine,const std::string & name,const int value,const int durationMs)1236 static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1237                                     const std::string& name, const int value,
1238                                     const int durationMs)
1239 {
1240     // Set the masked GPIO line to the specified value
1241     maskedGPIOLine.set_value(value);
1242     lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1243               "GPIO_VALUE", value);
1244     gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1245     gpioAssertTimer.async_wait(
1246         [maskedGPIOLine, value, name](const boost::system::error_code ec) {
1247         // Set the masked GPIO line back to the opposite value
1248         maskedGPIOLine.set_value(!value);
1249         lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1250         if (ec)
1251         {
1252             // operation_aborted is expected if timer is canceled before
1253             // completion.
1254             if (ec != boost::asio::error::operation_aborted)
1255             {
1256                 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1257                            "GPIO_NAME", name, "ERROR_MSG", ec.message());
1258             }
1259         }
1260     });
1261     return 0;
1262 }
1263 
setGPIOOutputForMs(const ConfigData & config,const int value,const int durationMs)1264 static int setGPIOOutputForMs(const ConfigData& config, const int value,
1265                               const int durationMs)
1266 {
1267     // If the requested GPIO is masked, use the mask line to set the output
1268     if (powerButtonMask && config.lineName == powerOutConfig.lineName)
1269     {
1270         return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value,
1271                                         durationMs);
1272     }
1273     if (resetButtonMask && config.lineName == resetOutConfig.lineName)
1274     {
1275         return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value,
1276                                         durationMs);
1277     }
1278 
1279     // No mask set, so request and set the GPIO normally
1280     gpiod::line gpioLine;
1281     if (!setGPIOOutput(config.lineName, value, gpioLine))
1282     {
1283         return -1;
1284     }
1285     const std::string name = config.lineName;
1286 
1287     gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1288     gpioAssertTimer.async_wait(
1289         [gpioLine, value, name](const boost::system::error_code ec) {
1290         // Set the GPIO line back to the opposite value
1291         gpioLine.set_value(!value);
1292         lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1293         if (ec)
1294         {
1295             // operation_aborted is expected if timer is canceled before
1296             // completion.
1297             if (ec != boost::asio::error::operation_aborted)
1298             {
1299                 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1300                            "GPIO_NAME", name, "ERROR_MSG", ec.message());
1301             }
1302         }
1303     });
1304     return 0;
1305 }
1306 
assertGPIOForMs(const ConfigData & config,const int durationMs)1307 static int assertGPIOForMs(const ConfigData& config, const int durationMs)
1308 {
1309     return setGPIOOutputForMs(config, config.polarity, durationMs);
1310 }
1311 
powerOn()1312 static void powerOn()
1313 {
1314     assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
1315 }
1316 #ifdef CHASSIS_SYSTEM_RESET
slotPowerOn()1317 static int slotPowerOn()
1318 {
1319     if (power_control::slotPowerState != power_control::SlotPowerState::on)
1320     {
1321         slotPowerLine.set_value(1);
1322 
1323         if (slotPowerLine.get_value() > 0)
1324         {
1325             setSlotPowerState(SlotPowerState::on);
1326             lg2::info("Slot Power is switched On\n");
1327         }
1328         else
1329         {
1330             return -1;
1331         }
1332     }
1333     else
1334     {
1335         lg2::info("Slot Power is already in 'On' state\n");
1336         return -1;
1337     }
1338     return 0;
1339 }
slotPowerOff()1340 static int slotPowerOff()
1341 {
1342     if (power_control::slotPowerState != power_control::SlotPowerState::off)
1343     {
1344         slotPowerLine.set_value(0);
1345 
1346         if (!(slotPowerLine.get_value() > 0))
1347         {
1348             setSlotPowerState(SlotPowerState::off);
1349             setPowerState(PowerState::off);
1350             lg2::info("Slot Power is switched Off\n");
1351         }
1352         else
1353         {
1354             return -1;
1355         }
1356     }
1357     else
1358     {
1359         lg2::info("Slot Power is already in 'Off' state\n");
1360         return -1;
1361     }
1362     return 0;
1363 }
slotPowerCycle()1364 static void slotPowerCycle()
1365 {
1366     lg2::info("Slot Power Cycle started\n");
1367     slotPowerOff();
1368     slotPowerCycleTimer.expires_after(
1369         std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"]));
1370     slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) {
1371         if (ec)
1372         {
1373             if (ec != boost::asio::error::operation_aborted)
1374             {
1375                 lg2::error(
1376                     "Slot Power cycle timer async_wait failed: {ERROR_MSG}",
1377                     "ERROR_MSG", ec.message());
1378             }
1379             lg2::info("Slot Power cycle timer canceled\n");
1380             return;
1381         }
1382         lg2::info("Slot Power cycle timer completed\n");
1383         slotPowerOn();
1384         lg2::info("Slot Power Cycle Completed\n");
1385     });
1386 }
1387 #endif
gracefulPowerOff()1388 static void gracefulPowerOff()
1389 {
1390     assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
1391 }
1392 
forcePowerOff()1393 static void forcePowerOff()
1394 {
1395     if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0)
1396     {
1397         return;
1398     }
1399 
1400     // If the force off timer expires, then the power-button override failed
1401     gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1402         if (ec)
1403         {
1404             // operation_aborted is expected if timer is canceled before
1405             // completion.
1406             if (ec != boost::asio::error::operation_aborted)
1407             {
1408                 lg2::error("Force power off async_wait failed: {ERROR_MSG}",
1409                            "ERROR_MSG", ec.message());
1410             }
1411             return;
1412         }
1413 
1414         lg2::error("Power-button override failed. Not sure what to do now.");
1415     });
1416 }
1417 
reset()1418 static void reset()
1419 {
1420     assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]);
1421 }
1422 
gracefulPowerOffTimerStart()1423 static void gracefulPowerOffTimerStart()
1424 {
1425     lg2::info("Graceful power-off timer started");
1426     gracefulPowerOffTimer.expires_after(
1427         std::chrono::seconds(TimerMap["GracefulPowerOffS"]));
1428     gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1429         if (ec)
1430         {
1431             // operation_aborted is expected if timer is canceled before
1432             // completion.
1433             if (ec != boost::asio::error::operation_aborted)
1434             {
1435                 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}",
1436                            "ERROR_MSG", ec.message());
1437             }
1438             lg2::info("Graceful power-off timer canceled");
1439             return;
1440         }
1441         lg2::info("Graceful power-off timer completed");
1442         sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1443     });
1444 }
1445 
powerCycleTimerStart()1446 static void powerCycleTimerStart()
1447 {
1448     lg2::info("Power-cycle timer started");
1449     powerCycleTimer.expires_after(
1450         std::chrono::milliseconds(TimerMap["PowerCycleMs"]));
1451     powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1452         if (ec)
1453         {
1454             // operation_aborted is expected if timer is canceled before
1455             // completion.
1456             if (ec != boost::asio::error::operation_aborted)
1457             {
1458                 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}",
1459                            "ERROR_MSG", ec.message());
1460             }
1461             lg2::info("Power-cycle timer canceled");
1462             return;
1463         }
1464         lg2::info("Power-cycle timer completed");
1465         sendPowerControlEvent(Event::powerCycleTimerExpired);
1466     });
1467 }
1468 
psPowerOKWatchdogTimerStart()1469 static void psPowerOKWatchdogTimerStart()
1470 {
1471     lg2::info("power supply power OK watchdog timer started");
1472     psPowerOKWatchdogTimer.expires_after(
1473         std::chrono::milliseconds(TimerMap["PsPowerOKWatchdogMs"]));
1474     psPowerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) {
1475         if (ec)
1476         {
1477             // operation_aborted is expected if timer is canceled before
1478             // completion.
1479             if (ec != boost::asio::error::operation_aborted)
1480             {
1481                 lg2::error(
1482                     "power supply power OK watchdog async_wait failed: {ERROR_MSG}",
1483                     "ERROR_MSG", ec.message());
1484             }
1485             lg2::info("power supply power OK watchdog timer canceled");
1486             return;
1487         }
1488         lg2::info("power supply power OK watchdog timer expired");
1489         sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1490     });
1491 }
1492 
warmResetCheckTimerStart()1493 static void warmResetCheckTimerStart()
1494 {
1495     lg2::info("Warm reset check timer started");
1496     warmResetCheckTimer.expires_after(
1497         std::chrono::milliseconds(TimerMap["WarmResetCheckMs"]));
1498     warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1499         if (ec)
1500         {
1501             // operation_aborted is expected if timer is canceled before
1502             // completion.
1503             if (ec != boost::asio::error::operation_aborted)
1504             {
1505                 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}",
1506                            "ERROR_MSG", ec.message());
1507             }
1508             lg2::info("Warm reset check timer canceled");
1509             return;
1510         }
1511         lg2::info("Warm reset check timer completed");
1512         sendPowerControlEvent(Event::warmResetDetected);
1513     });
1514 }
1515 
pohCounterTimerStart()1516 static void pohCounterTimerStart()
1517 {
1518     lg2::info("POH timer started");
1519     // Set the time-out as 1 hour, to align with POH command in ipmid
1520     pohCounterTimer.expires_after(std::chrono::hours(1));
1521     pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1522         if (ec)
1523         {
1524             // operation_aborted is expected if timer is canceled before
1525             // completion.
1526             if (ec != boost::asio::error::operation_aborted)
1527             {
1528                 lg2::error("POH timer async_wait failed: {ERROR_MSG}",
1529                            "ERROR_MSG", ec.message());
1530             }
1531             lg2::info("POH timer canceled");
1532             return;
1533         }
1534 
1535         if (getHostState(powerState) !=
1536             "xyz.openbmc_project.State.Host.HostState.Running")
1537         {
1538             return;
1539         }
1540 
1541         conn->async_method_call(
1542             [](boost::system::error_code ec,
1543                const std::variant<uint32_t>& pohCounterProperty) {
1544             if (ec)
1545             {
1546                 lg2::error("error getting poh counter");
1547                 return;
1548             }
1549             const uint32_t* pohCounter =
1550                 std::get_if<uint32_t>(&pohCounterProperty);
1551             if (pohCounter == nullptr)
1552             {
1553                 lg2::error("unable to read poh counter");
1554                 return;
1555             }
1556 
1557             conn->async_method_call(
1558                 [](boost::system::error_code ec) {
1559                 if (ec)
1560                 {
1561                     lg2::error("failed to set poh counter");
1562                 }
1563             },
1564                 "xyz.openbmc_project.Settings",
1565                 "/xyz/openbmc_project/state/chassis0",
1566                 "org.freedesktop.DBus.Properties", "Set",
1567                 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1568                 std::variant<uint32_t>(*pohCounter + 1));
1569         },
1570             "xyz.openbmc_project.Settings",
1571             "/xyz/openbmc_project/state/chassis0",
1572             "org.freedesktop.DBus.Properties", "Get",
1573             "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1574 
1575         pohCounterTimerStart();
1576     });
1577 }
1578 
currentHostStateMonitor()1579 static void currentHostStateMonitor()
1580 {
1581     if (getHostState(powerState) ==
1582         "xyz.openbmc_project.State.Host.HostState.Running")
1583     {
1584         pohCounterTimerStart();
1585         // Clear the restart cause set for the next restart
1586         clearRestartCause();
1587     }
1588     else
1589     {
1590         pohCounterTimer.cancel();
1591         // Set the restart cause set for this restart
1592         setRestartCause();
1593     }
1594 
1595     static auto match =
1596         sdbusplus::bus::match_t(*conn,
1597                                 "type='signal',member='PropertiesChanged', "
1598                                 "interface='org.freedesktop.DBus.Properties', "
1599                                 "arg0='xyz.openbmc_project.State.Host'",
1600                                 [](sdbusplus::message_t& message) {
1601         std::string intfName;
1602         std::map<std::string, std::variant<std::string>> properties;
1603 
1604         try
1605         {
1606             message.read(intfName, properties);
1607         }
1608         catch (const std::exception& e)
1609         {
1610             lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1611             return;
1612         }
1613         if (properties.empty())
1614         {
1615             lg2::error("ERROR: Empty PropertiesChanged signal received");
1616             return;
1617         }
1618 
1619         // We only want to check for CurrentHostState
1620         if (properties.begin()->first != "CurrentHostState")
1621         {
1622             return;
1623         }
1624         std::string* currentHostState =
1625             std::get_if<std::string>(&(properties.begin()->second));
1626         if (currentHostState == nullptr)
1627         {
1628             lg2::error("{PROPERTY} property invalid", "PROPERTY",
1629                        properties.begin()->first);
1630             return;
1631         }
1632 
1633         if (*currentHostState ==
1634             "xyz.openbmc_project.State.Host.HostState.Running")
1635         {
1636             pohCounterTimerStart();
1637             // Clear the restart cause set for the next restart
1638             clearRestartCause();
1639             sd_journal_send("MESSAGE=Host system DC power is on", "PRIORITY=%i",
1640                             LOG_INFO, "REDFISH_MESSAGE_ID=%s",
1641                             "OpenBMC.0.1.DCPowerOn", NULL);
1642         }
1643         else
1644         {
1645             pohCounterTimer.cancel();
1646             // POST_COMPLETE GPIO event is not working in some platforms
1647             // when power state is changed to OFF. This resulted in
1648             // 'OperatingSystemState' to stay at 'Standby', even though
1649             // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1650             // if HostState is trurned to OFF.
1651             setOperatingSystemState(OperatingSystemStateStage::Inactive);
1652 
1653             // Set the restart cause set for this restart
1654             setRestartCause();
1655 #ifdef USE_ACBOOT
1656             resetACBootProperty();
1657 #endif // USE_ACBOOT
1658             sd_journal_send("MESSAGE=Host system DC power is off",
1659                             "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
1660                             "OpenBMC.0.1.DCPowerOff", NULL);
1661         }
1662     });
1663 }
1664 
sioPowerGoodWatchdogTimerStart()1665 static void sioPowerGoodWatchdogTimerStart()
1666 {
1667     lg2::info("SIO power good watchdog timer started");
1668     sioPowerGoodWatchdogTimer.expires_after(
1669         std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
1670     sioPowerGoodWatchdogTimer.async_wait(
1671         [](const boost::system::error_code ec) {
1672         if (ec)
1673         {
1674             // operation_aborted is expected if timer is canceled before
1675             // completion.
1676             if (ec != boost::asio::error::operation_aborted)
1677             {
1678                 lg2::error(
1679                     "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1680                     "ERROR_MSG", ec.message());
1681             }
1682             lg2::info("SIO power good watchdog timer canceled");
1683             return;
1684         }
1685         lg2::info("SIO power good watchdog timer completed");
1686         sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1687     });
1688 }
1689 
powerStateOn(const Event event)1690 static void powerStateOn(const Event event)
1691 {
1692     logEvent(__FUNCTION__, event);
1693     switch (event)
1694     {
1695         case Event::psPowerOKDeAssert:
1696             setPowerState(PowerState::off);
1697             // DC power is unexpectedly lost, beep
1698             beep(beepPowerFail);
1699             break;
1700         case Event::sioS5Assert:
1701             setPowerState(PowerState::transitionToOff);
1702 #if IGNORE_SOFT_RESETS_DURING_POST
1703             // Only recognize soft resets once host gets past POST COMPLETE
1704             if (operatingSystemState != OperatingSystemStateStage::Standby)
1705             {
1706                 ignoreNextSoftReset = true;
1707             }
1708 #endif
1709             addRestartCause(RestartCause::softReset);
1710             break;
1711 #if USE_PLT_RST
1712         case Event::pltRstAssert:
1713 #else
1714         case Event::postCompleteDeAssert:
1715 #endif
1716             setPowerState(PowerState::checkForWarmReset);
1717 #if IGNORE_SOFT_RESETS_DURING_POST
1718             // Only recognize soft resets once host gets past POST COMPLETE
1719             if (operatingSystemState != OperatingSystemStateStage::Standby)
1720             {
1721                 ignoreNextSoftReset = true;
1722             }
1723 #endif
1724             addRestartCause(RestartCause::softReset);
1725             warmResetCheckTimerStart();
1726             break;
1727         case Event::powerButtonPressed:
1728             setPowerState(PowerState::gracefulTransitionToOff);
1729             gracefulPowerOffTimerStart();
1730             break;
1731         case Event::powerOffRequest:
1732             setPowerState(PowerState::transitionToOff);
1733             forcePowerOff();
1734             break;
1735         case Event::gracefulPowerOffRequest:
1736             setPowerState(PowerState::gracefulTransitionToOff);
1737             gracefulPowerOffTimerStart();
1738             gracefulPowerOff();
1739             break;
1740         case Event::powerCycleRequest:
1741             setPowerState(PowerState::transitionToCycleOff);
1742             forcePowerOff();
1743             break;
1744         case Event::gracefulPowerCycleRequest:
1745             setPowerState(PowerState::gracefulTransitionToCycleOff);
1746             gracefulPowerOffTimerStart();
1747             gracefulPowerOff();
1748             break;
1749         case Event::resetButtonPressed:
1750             setPowerState(PowerState::checkForWarmReset);
1751             warmResetCheckTimerStart();
1752             break;
1753         case Event::resetRequest:
1754             reset();
1755             break;
1756         default:
1757             lg2::info("No action taken.");
1758             break;
1759     }
1760 }
1761 
powerStateWaitForPSPowerOK(const Event event)1762 static void powerStateWaitForPSPowerOK(const Event event)
1763 {
1764     logEvent(__FUNCTION__, event);
1765     switch (event)
1766     {
1767         case Event::psPowerOKAssert:
1768         {
1769             // Cancel any GPIO assertions held during the transition
1770             gpioAssertTimer.cancel();
1771             psPowerOKWatchdogTimer.cancel();
1772             if (sioEnabled == true)
1773             {
1774                 sioPowerGoodWatchdogTimerStart();
1775                 setPowerState(PowerState::waitForSIOPowerGood);
1776             }
1777             else
1778             {
1779                 setPowerState(PowerState::on);
1780             }
1781             break;
1782         }
1783         case Event::psPowerOKWatchdogTimerExpired:
1784             setPowerState(PowerState::off);
1785             psPowerOKFailedLog();
1786             break;
1787         case Event::sioPowerGoodAssert:
1788             psPowerOKWatchdogTimer.cancel();
1789             setPowerState(PowerState::on);
1790             break;
1791         default:
1792             lg2::info("No action taken.");
1793             break;
1794     }
1795 }
1796 
powerStateWaitForSIOPowerGood(const Event event)1797 static void powerStateWaitForSIOPowerGood(const Event event)
1798 {
1799     logEvent(__FUNCTION__, event);
1800     switch (event)
1801     {
1802         case Event::sioPowerGoodAssert:
1803             sioPowerGoodWatchdogTimer.cancel();
1804             setPowerState(PowerState::on);
1805             break;
1806         case Event::sioPowerGoodWatchdogTimerExpired:
1807             setPowerState(PowerState::off);
1808             systemPowerGoodFailedLog();
1809             break;
1810         default:
1811             lg2::info("No action taken.");
1812             break;
1813     }
1814 }
1815 
powerStateOff(const Event event)1816 static void powerStateOff(const Event event)
1817 {
1818     logEvent(__FUNCTION__, event);
1819     switch (event)
1820     {
1821         case Event::psPowerOKAssert:
1822         {
1823             if (sioEnabled == true)
1824             {
1825                 sioPowerGoodWatchdogTimerStart();
1826                 setPowerState(PowerState::waitForSIOPowerGood);
1827             }
1828             else
1829             {
1830                 setPowerState(PowerState::on);
1831             }
1832             break;
1833         }
1834         case Event::sioS5DeAssert:
1835             psPowerOKWatchdogTimerStart();
1836             setPowerState(PowerState::waitForPSPowerOK);
1837             break;
1838         case Event::sioPowerGoodAssert:
1839             setPowerState(PowerState::on);
1840             break;
1841         case Event::powerButtonPressed:
1842             psPowerOKWatchdogTimerStart();
1843             setPowerState(PowerState::waitForPSPowerOK);
1844             break;
1845         case Event::powerOnRequest:
1846             psPowerOKWatchdogTimerStart();
1847             setPowerState(PowerState::waitForPSPowerOK);
1848             powerOn();
1849             break;
1850         default:
1851             lg2::info("No action taken.");
1852             break;
1853     }
1854 }
1855 
powerStateTransitionToOff(const Event event)1856 static void powerStateTransitionToOff(const Event event)
1857 {
1858     logEvent(__FUNCTION__, event);
1859     switch (event)
1860     {
1861         case Event::psPowerOKDeAssert:
1862             // Cancel any GPIO assertions held during the transition
1863             gpioAssertTimer.cancel();
1864             setPowerState(PowerState::off);
1865             break;
1866         default:
1867             lg2::info("No action taken.");
1868             break;
1869     }
1870 }
1871 
powerStateGracefulTransitionToOff(const Event event)1872 static void powerStateGracefulTransitionToOff(const Event event)
1873 {
1874     logEvent(__FUNCTION__, event);
1875     switch (event)
1876     {
1877         case Event::psPowerOKDeAssert:
1878             gracefulPowerOffTimer.cancel();
1879             setPowerState(PowerState::off);
1880             break;
1881         case Event::gracefulPowerOffTimerExpired:
1882             setPowerState(PowerState::on);
1883             break;
1884         case Event::powerOffRequest:
1885             gracefulPowerOffTimer.cancel();
1886             setPowerState(PowerState::transitionToOff);
1887             forcePowerOff();
1888             break;
1889         case Event::powerCycleRequest:
1890             gracefulPowerOffTimer.cancel();
1891             setPowerState(PowerState::transitionToCycleOff);
1892             forcePowerOff();
1893             break;
1894         case Event::resetRequest:
1895             gracefulPowerOffTimer.cancel();
1896             setPowerState(PowerState::on);
1897             reset();
1898             break;
1899         default:
1900             lg2::info("No action taken.");
1901             break;
1902     }
1903 }
1904 
powerStateCycleOff(const Event event)1905 static void powerStateCycleOff(const Event event)
1906 {
1907     logEvent(__FUNCTION__, event);
1908     switch (event)
1909     {
1910         case Event::psPowerOKAssert:
1911         {
1912             powerCycleTimer.cancel();
1913             if (sioEnabled == true)
1914             {
1915                 sioPowerGoodWatchdogTimerStart();
1916                 setPowerState(PowerState::waitForSIOPowerGood);
1917             }
1918             else
1919             {
1920                 setPowerState(PowerState::on);
1921             }
1922             break;
1923         }
1924         case Event::sioS5DeAssert:
1925             powerCycleTimer.cancel();
1926             psPowerOKWatchdogTimerStart();
1927             setPowerState(PowerState::waitForPSPowerOK);
1928             break;
1929         case Event::powerButtonPressed:
1930             powerCycleTimer.cancel();
1931             psPowerOKWatchdogTimerStart();
1932             setPowerState(PowerState::waitForPSPowerOK);
1933             break;
1934         case Event::powerCycleTimerExpired:
1935             psPowerOKWatchdogTimerStart();
1936             setPowerState(PowerState::waitForPSPowerOK);
1937             powerOn();
1938             break;
1939         default:
1940             lg2::info("No action taken.");
1941             break;
1942     }
1943 }
1944 
powerStateTransitionToCycleOff(const Event event)1945 static void powerStateTransitionToCycleOff(const Event event)
1946 {
1947     logEvent(__FUNCTION__, event);
1948     switch (event)
1949     {
1950         case Event::psPowerOKDeAssert:
1951             // Cancel any GPIO assertions held during the transition
1952             gpioAssertTimer.cancel();
1953             setPowerState(PowerState::cycleOff);
1954             powerCycleTimerStart();
1955             break;
1956         default:
1957             lg2::info("No action taken.");
1958             break;
1959     }
1960 }
1961 
powerStateGracefulTransitionToCycleOff(const Event event)1962 static void powerStateGracefulTransitionToCycleOff(const Event event)
1963 {
1964     logEvent(__FUNCTION__, event);
1965     switch (event)
1966     {
1967         case Event::psPowerOKDeAssert:
1968             gracefulPowerOffTimer.cancel();
1969             setPowerState(PowerState::cycleOff);
1970             powerCycleTimerStart();
1971             break;
1972         case Event::gracefulPowerOffTimerExpired:
1973             setPowerState(PowerState::on);
1974             break;
1975         case Event::powerOffRequest:
1976             gracefulPowerOffTimer.cancel();
1977             setPowerState(PowerState::transitionToOff);
1978             forcePowerOff();
1979             break;
1980         case Event::powerCycleRequest:
1981             gracefulPowerOffTimer.cancel();
1982             setPowerState(PowerState::transitionToCycleOff);
1983             forcePowerOff();
1984             break;
1985         case Event::resetRequest:
1986             gracefulPowerOffTimer.cancel();
1987             setPowerState(PowerState::on);
1988             reset();
1989             break;
1990         default:
1991             lg2::info("No action taken.");
1992             break;
1993     }
1994 }
1995 
powerStateCheckForWarmReset(const Event event)1996 static void powerStateCheckForWarmReset(const Event event)
1997 {
1998     logEvent(__FUNCTION__, event);
1999     switch (event)
2000     {
2001         case Event::sioS5Assert:
2002             warmResetCheckTimer.cancel();
2003             setPowerState(PowerState::transitionToOff);
2004             break;
2005         case Event::warmResetDetected:
2006             setPowerState(PowerState::on);
2007             break;
2008         case Event::psPowerOKDeAssert:
2009             warmResetCheckTimer.cancel();
2010             setPowerState(PowerState::off);
2011             // DC power is unexpectedly lost, beep
2012             beep(beepPowerFail);
2013             break;
2014         default:
2015             lg2::info("No action taken.");
2016             break;
2017     }
2018 }
2019 
psPowerOKHandler(bool state)2020 static void psPowerOKHandler(bool state)
2021 {
2022     Event powerControlEvent = (state == powerOkConfig.polarity)
2023                                   ? Event::psPowerOKAssert
2024                                   : Event::psPowerOKDeAssert;
2025     sendPowerControlEvent(powerControlEvent);
2026 }
2027 
sioPowerGoodHandler(bool state)2028 static void sioPowerGoodHandler(bool state)
2029 {
2030     Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2031                                   ? Event::sioPowerGoodAssert
2032                                   : Event::sioPowerGoodDeAssert;
2033     sendPowerControlEvent(powerControlEvent);
2034 }
2035 
sioOnControlHandler(bool state)2036 static void sioOnControlHandler(bool state)
2037 {
2038     lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2039               static_cast<int>(state));
2040 }
2041 
sioS5Handler(bool state)2042 static void sioS5Handler(bool state)
2043 {
2044     Event powerControlEvent = (state == sioS5Config.polarity)
2045                                   ? Event::sioS5Assert
2046                                   : Event::sioS5DeAssert;
2047     sendPowerControlEvent(powerControlEvent);
2048 }
2049 
powerButtonHandler(bool state)2050 static void powerButtonHandler(bool state)
2051 {
2052     bool asserted = state == powerButtonConfig.polarity;
2053     powerButtonIface->set_property("ButtonPressed", asserted);
2054     if (asserted)
2055     {
2056         powerButtonPressLog();
2057         if (!powerButtonMask)
2058         {
2059             sendPowerControlEvent(Event::powerButtonPressed);
2060             addRestartCause(RestartCause::powerButton);
2061         }
2062         else
2063         {
2064             lg2::info("power button press masked");
2065         }
2066     }
2067 }
2068 
resetButtonHandler(bool state)2069 static void resetButtonHandler(bool state)
2070 {
2071     bool asserted = state == resetButtonConfig.polarity;
2072     resetButtonIface->set_property("ButtonPressed", asserted);
2073     if (asserted)
2074     {
2075         resetButtonPressLog();
2076         if (!resetButtonMask)
2077         {
2078             sendPowerControlEvent(Event::resetButtonPressed);
2079             addRestartCause(RestartCause::resetButton);
2080         }
2081         else
2082         {
2083             lg2::info("reset button press masked");
2084         }
2085     }
2086 }
2087 
2088 #ifdef CHASSIS_SYSTEM_RESET
2089 static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2090 static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2091 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2092 static constexpr auto systemTargetName = "chassis-system-reset.target";
2093 
systemReset()2094 void systemReset()
2095 {
2096     conn->async_method_call(
2097         [](boost::system::error_code ec) {
2098         if (ec)
2099         {
2100             lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2101                        ec.message());
2102         }
2103     },
2104         systemdBusname, systemdPath, systemdInterface, "StartUnit",
2105         systemTargetName, "replace");
2106 }
2107 #endif
2108 
nmiSetEnableProperty(bool value)2109 static void nmiSetEnableProperty(bool value)
2110 {
2111     conn->async_method_call(
2112         [](boost::system::error_code ec) {
2113         if (ec)
2114         {
2115             lg2::error("failed to set NMI source");
2116         }
2117     },
2118         "xyz.openbmc_project.Settings",
2119         "/xyz/openbmc_project/Chassis/Control/NMISource",
2120         "org.freedesktop.DBus.Properties", "Set",
2121         "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2122         std::variant<bool>{value});
2123 }
2124 
nmiReset(void)2125 static void nmiReset(void)
2126 {
2127     const static constexpr int nmiOutPulseTimeMs = 200;
2128 
2129     lg2::info("NMI out action");
2130     nmiOutLine.set_value(nmiOutConfig.polarity);
2131     lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
2132               nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
2133     gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2134     gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2135         // restore the NMI_OUT GPIO line back to the opposite value
2136         nmiOutLine.set_value(!nmiOutConfig.polarity);
2137         lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
2138         if (ec)
2139         {
2140             // operation_aborted is expected if timer is canceled before
2141             // completion.
2142             if (ec != boost::asio::error::operation_aborted)
2143             {
2144                 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2145                            "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2146                            ec.message());
2147             }
2148         }
2149     });
2150     // log to redfish
2151     nmiDiagIntLog();
2152     lg2::info("NMI out action completed");
2153     // reset Enable Property
2154     nmiSetEnableProperty(false);
2155 }
2156 
nmiSourcePropertyMonitor(void)2157 static void nmiSourcePropertyMonitor(void)
2158 {
2159     lg2::info("NMI Source Property Monitor");
2160 
2161     static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2162         std::make_unique<sdbusplus::bus::match_t>(
2163             *conn,
2164             "type='signal',interface='org.freedesktop.DBus.Properties',"
2165             "member='PropertiesChanged',"
2166             "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
2167             [](sdbusplus::message_t& msg) {
2168         std::string interfaceName;
2169         boost::container::flat_map<std::string, std::variant<bool, std::string>>
2170             propertiesChanged;
2171         std::string state;
2172         bool value = true;
2173         try
2174         {
2175             msg.read(interfaceName, propertiesChanged);
2176             if (propertiesChanged.begin()->first == "Enabled")
2177             {
2178                 value = std::get<bool>(propertiesChanged.begin()->second);
2179                 lg2::info("NMI Enabled propertiesChanged value: {VALUE}",
2180                           "VALUE", value);
2181                 nmiEnabled = value;
2182                 if (nmiEnabled)
2183                 {
2184                     nmiReset();
2185                 }
2186             }
2187         }
2188         catch (const std::exception& e)
2189         {
2190             lg2::error("Unable to read NMI source: {ERROR}", "ERROR", e);
2191             return;
2192         }
2193     });
2194 }
2195 
setNmiSource()2196 static void setNmiSource()
2197 {
2198     conn->async_method_call(
2199         [](boost::system::error_code ec) {
2200         if (ec)
2201         {
2202             lg2::error("failed to set NMI source");
2203         }
2204     },
2205         "xyz.openbmc_project.Settings",
2206         "/xyz/openbmc_project/Chassis/Control/NMISource",
2207         "org.freedesktop.DBus.Properties", "Set",
2208         "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
2209         std::variant<std::string>{
2210             "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
2211     // set Enable Property
2212     nmiSetEnableProperty(true);
2213 }
2214 
nmiButtonHandler(bool state)2215 static void nmiButtonHandler(bool state)
2216 {
2217     // Don't handle event if host not running and config doesn't force it
2218     if (!nmiWhenPoweredOff &&
2219         getHostState(powerState) !=
2220             "xyz.openbmc_project.State.Host.HostState.Running")
2221     {
2222         return;
2223     }
2224 
2225     bool asserted = state == nmiButtonConfig.polarity;
2226     nmiButtonIface->set_property("ButtonPressed", asserted);
2227     if (asserted)
2228     {
2229         nmiButtonPressLog();
2230         if (nmiButtonMasked)
2231         {
2232             lg2::info("NMI button press masked");
2233         }
2234         else
2235         {
2236             setNmiSource();
2237         }
2238     }
2239 }
2240 
idButtonHandler(bool state)2241 static void idButtonHandler(bool state)
2242 {
2243     bool asserted = state == idButtonConfig.polarity;
2244     idButtonIface->set_property("ButtonPressed", asserted);
2245 }
2246 
pltRstHandler(bool pltRst)2247 static void pltRstHandler(bool pltRst)
2248 {
2249     if (pltRst)
2250     {
2251         sendPowerControlEvent(Event::pltRstDeAssert);
2252     }
2253     else
2254     {
2255         sendPowerControlEvent(Event::pltRstAssert);
2256     }
2257 }
2258 
hostMiscHandler(sdbusplus::message_t & msg)2259 [[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
2260 {
2261     std::string interfaceName;
2262     boost::container::flat_map<std::string, std::variant<bool>>
2263         propertiesChanged;
2264     try
2265     {
2266         msg.read(interfaceName, propertiesChanged);
2267     }
2268     catch (const std::exception& e)
2269     {
2270         lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
2271         return;
2272     }
2273     if (propertiesChanged.empty())
2274     {
2275         lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
2276         return;
2277     }
2278 
2279     for (auto& [property, value] : propertiesChanged)
2280     {
2281         if (property == "ESpiPlatformReset")
2282         {
2283             bool* pltRst = std::get_if<bool>(&value);
2284             if (pltRst == nullptr)
2285             {
2286                 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
2287                 return;
2288             }
2289             pltRstHandler(*pltRst);
2290         }
2291     }
2292 }
2293 
postCompleteHandler(bool state)2294 static void postCompleteHandler(bool state)
2295 {
2296     bool asserted = state == postCompleteConfig.polarity;
2297     if (asserted)
2298     {
2299         sendPowerControlEvent(Event::postCompleteAssert);
2300         setOperatingSystemState(OperatingSystemStateStage::Standby);
2301     }
2302     else
2303     {
2304         sendPowerControlEvent(Event::postCompleteDeAssert);
2305         setOperatingSystemState(OperatingSystemStateStage::Inactive);
2306     }
2307 }
2308 
loadConfigValues()2309 static int loadConfigValues()
2310 {
2311     const std::string configFilePath =
2312         "/usr/share/x86-power-control/power-config-host" + power_control::node +
2313         ".json";
2314     std::ifstream configFile(configFilePath.c_str());
2315     if (!configFile.is_open())
2316     {
2317         lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2318                    "PATH", configFilePath);
2319         return -1;
2320     }
2321     auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
2322 
2323     if (jsonData.is_discarded())
2324     {
2325         lg2::error("Power config readings JSON parser failure");
2326         return -1;
2327     }
2328     auto gpios = jsonData["gpio_configs"];
2329     auto timers = jsonData["timing_configs"];
2330 
2331     ConfigData* tempGpioData;
2332 
2333     for (nlohmann::json& gpioConfig : gpios)
2334     {
2335         if (!gpioConfig.contains("Name"))
2336         {
2337             lg2::error("The 'Name' field must be defined in Json file");
2338             return -1;
2339         }
2340 
2341         // Iterate through the powersignal map to check if the gpio json config
2342         // entry is valid
2343         std::string gpioName = gpioConfig["Name"];
2344         auto signalMapIter = powerSignalMap.find(gpioName);
2345         if (signalMapIter == powerSignalMap.end())
2346         {
2347             lg2::error(
2348                 "{GPIO_NAME} is not a recognized power-control signal name",
2349                 "GPIO_NAME", gpioName);
2350             return -1;
2351         }
2352 
2353         // assign the power signal name to the corresponding structure reference
2354         // from map then fillup the structure with coressponding json config
2355         // value
2356         tempGpioData = signalMapIter->second;
2357         tempGpioData->name = gpioName;
2358 
2359         if (!gpioConfig.contains("Type"))
2360         {
2361             lg2::error("The \'Type\' field must be defined in Json file");
2362             return -1;
2363         }
2364 
2365         std::string signalType = gpioConfig["Type"];
2366         if (signalType == "GPIO")
2367         {
2368             tempGpioData->type = ConfigType::GPIO;
2369         }
2370         else if (signalType == "DBUS")
2371         {
2372             tempGpioData->type = ConfigType::DBUS;
2373         }
2374         else
2375         {
2376             lg2::error("{TYPE} is not a recognized power-control signal type",
2377                        "TYPE", signalType);
2378             return -1;
2379         }
2380 
2381         if (tempGpioData->type == ConfigType::GPIO)
2382         {
2383             if (gpioConfig.contains("LineName"))
2384             {
2385                 tempGpioData->lineName = gpioConfig["LineName"];
2386             }
2387             else
2388             {
2389                 lg2::error(
2390                     "The \'LineName\' field must be defined for GPIO configuration");
2391                 return -1;
2392             }
2393             if (gpioConfig.contains("Polarity"))
2394             {
2395                 std::string polarity = gpioConfig["Polarity"];
2396                 if (polarity == "ActiveLow")
2397                 {
2398                     tempGpioData->polarity = false;
2399                 }
2400                 else if (polarity == "ActiveHigh")
2401                 {
2402                     tempGpioData->polarity = true;
2403                 }
2404                 else
2405                 {
2406                     lg2::error(
2407                         "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2408                         "POLARITY", polarity);
2409                     return -1;
2410                 }
2411             }
2412             else
2413             {
2414                 lg2::error("Polarity field not found for {GPIO_NAME}",
2415                            "GPIO_NAME", tempGpioData->lineName);
2416                 return -1;
2417             }
2418         }
2419         else
2420         {
2421             // if dbus based gpio config is defined read and update the dbus
2422             // params corresponding to the gpio config instance
2423             for (auto& [key, dbusParamName] : dbusParams)
2424             {
2425                 if (!gpioConfig.contains(dbusParamName))
2426                 {
2427                     lg2::error(
2428                         "The {DBUS_NAME} field must be defined for Dbus configuration ",
2429                         "DBUS_NAME", dbusParamName);
2430                     return -1;
2431                 }
2432             }
2433             tempGpioData->dbusName =
2434                 gpioConfig[dbusParams[DbusConfigType::name]];
2435             tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
2436             tempGpioData->interface =
2437                 gpioConfig[dbusParams[DbusConfigType::interface]];
2438             tempGpioData->lineName =
2439                 gpioConfig[dbusParams[DbusConfigType::property]];
2440 
2441             // dbus-based inputs must be active-high.
2442             tempGpioData->polarity = true;
2443 
2444             // MatchRegex is optional
2445             auto item = gpioConfig.find("MatchRegex");
2446             if (item != gpioConfig.end())
2447             {
2448                 try
2449                 {
2450                     tempGpioData->matchRegex = std::regex(*item);
2451                 }
2452                 catch (const std::regex_error& e)
2453                 {
2454                     lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2455                                gpioName, "ERR", e.what());
2456                     return -1;
2457                 }
2458             }
2459         }
2460     }
2461 
2462     // read and store the timer values from json config to Timer Map
2463     for (auto& [key, timerValue] : TimerMap)
2464     {
2465         if (timers.contains(key.c_str()))
2466         {
2467             timerValue = timers[key.c_str()];
2468         }
2469     }
2470 
2471     // If "events_configs" key is not in json config, fallback to null
2472     auto events = jsonData.value("event_configs",
2473                                  nlohmann::json(nlohmann::json::value_t::null));
2474     if (events.is_object())
2475     {
2476         nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2477     }
2478 
2479     return 0;
2480 }
2481 
2482 template <typename T>
getMessageValue(sdbusplus::message_t & msg,const std::string & name)2483 static std::optional<T> getMessageValue(sdbusplus::message_t& msg,
2484                                         const std::string& name)
2485 {
2486     std::string event;
2487     std::string thresholdInterface;
2488     boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2489 
2490     msg.read(thresholdInterface, propertiesChanged);
2491     if (propertiesChanged.empty())
2492     {
2493         return std::nullopt;
2494     }
2495 
2496     event = propertiesChanged.begin()->first;
2497     if (event.empty() || event != name)
2498     {
2499         return std::nullopt;
2500     }
2501 
2502     return std::get<T>(propertiesChanged.begin()->second);
2503 }
2504 
getDbusMsgGPIOState(sdbusplus::message_t & msg,const ConfigData & config,bool & value)2505 static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2506                                 const ConfigData& config, bool& value)
2507 {
2508     try
2509     {
2510         if (config.matchRegex.has_value())
2511         {
2512             std::optional<std::string> s =
2513                 getMessageValue<std::string>(msg, config.lineName);
2514             if (!s.has_value())
2515             {
2516                 return false;
2517             }
2518 
2519             std::smatch m;
2520             value = std::regex_match(s.value(), m, config.matchRegex.value());
2521         }
2522         else
2523         {
2524             std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2525             if (!v.has_value())
2526             {
2527                 return false;
2528             }
2529             value = v.value();
2530         }
2531         return true;
2532     }
2533     catch (const std::exception& e)
2534     {
2535         lg2::error(
2536             "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
2537             "DBUS_NAME", config.lineName, "ERROR", e);
2538         return false;
2539     }
2540 }
2541 
2542 static sdbusplus::bus::match_t
dbusGPIOMatcher(const ConfigData & cfg,std::function<void (bool)> onMatch)2543     dbusGPIOMatcher(const ConfigData& cfg, std::function<void(bool)> onMatch)
2544 {
2545     auto pulseEventMatcherCallback = [&cfg,
2546                                       onMatch](sdbusplus::message_t& msg) {
2547         bool value = false;
2548         if (!getDbusMsgGPIOState(msg, cfg, value))
2549         {
2550             return;
2551         }
2552         onMatch(value);
2553     };
2554 
2555     return sdbusplus::bus::match_t(
2556         static_cast<sdbusplus::bus_t&>(*conn),
2557         "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2558         "PropertiesChanged',arg0='" +
2559             cfg.interface + "',path='" + cfg.path + "',sender='" +
2560             cfg.dbusName + "'",
2561         std::move(pulseEventMatcherCallback));
2562 }
2563 
2564 // D-Bus property read functions
2565 void reschedulePropertyRead(const ConfigData& configData);
2566 
getProperty(const ConfigData & configData)2567 int getProperty(const ConfigData& configData)
2568 {
2569     std::variant<bool> resp;
2570 
2571     try
2572     {
2573         auto method = conn->new_method_call(
2574             configData.dbusName.c_str(), configData.path.c_str(),
2575             "org.freedesktop.DBus.Properties", "Get");
2576         method.append(configData.interface.c_str(),
2577                       configData.lineName.c_str());
2578 
2579         auto reply = conn->call(method);
2580         if (reply.is_method_error())
2581         {
2582             lg2::error(
2583                 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
2584                 "PROPERTY", configData.lineName, "INTERFACE",
2585                 configData.interface, "PATH", configData.path);
2586             return -1;
2587         }
2588 
2589         reply.read(resp);
2590     }
2591     catch (const sdbusplus::exception_t& e)
2592     {
2593         lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
2594                    configData.lineName, "WHAT", e.what());
2595         reschedulePropertyRead(configData);
2596         return -1;
2597     }
2598 
2599     auto respValue = std::get_if<bool>(&resp);
2600     if (!respValue)
2601     {
2602         lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2603                    "PROPERTY", configData.lineName);
2604         return -1;
2605     }
2606     return (*respValue);
2607 }
2608 
setInitialValue(const ConfigData & configData,bool initialValue)2609 void setInitialValue(const ConfigData& configData, bool initialValue)
2610 {
2611     if (configData.name == "PowerOk")
2612     {
2613         powerState = (initialValue ? PowerState::on : PowerState::off);
2614         hostIface->set_property("CurrentHostState",
2615                                 std::string(getHostState(powerState)));
2616     }
2617     else if (configData.name == "PowerButton")
2618     {
2619         powerButtonIface->set_property("ButtonPressed", !initialValue);
2620     }
2621     else if (configData.name == "ResetButton")
2622     {
2623         resetButtonIface->set_property("ButtonPressed", !initialValue);
2624     }
2625     else if (configData.name == "NMIButton")
2626     {
2627         nmiButtonIface->set_property("ButtonPressed", !initialValue);
2628     }
2629     else if (configData.name == "IdButton")
2630     {
2631         idButtonIface->set_property("ButtonPressed", !initialValue);
2632     }
2633     else if (configData.name == "PostComplete")
2634     {
2635         OperatingSystemStateStage osState =
2636             (initialValue ? OperatingSystemStateStage::Inactive
2637                           : OperatingSystemStateStage::Standby);
2638         setOperatingSystemState(osState);
2639     }
2640     else
2641     {
2642         lg2::error("Unknown name {NAME}", "NAME", configData.name);
2643     }
2644 }
2645 
reschedulePropertyRead(const ConfigData & configData)2646 void reschedulePropertyRead(const ConfigData& configData)
2647 {
2648     auto item = dBusRetryTimers.find(configData.name);
2649 
2650     if (item == dBusRetryTimers.end())
2651     {
2652         auto newItem = dBusRetryTimers.insert(
2653             {configData.name, boost::asio::steady_timer(io)});
2654 
2655         if (!newItem.second)
2656         {
2657             lg2::error("Failed to add new timer for {NAME}", "NAME",
2658                        configData.name);
2659             return;
2660         }
2661 
2662         item = newItem.first;
2663     }
2664 
2665     auto& timer = item->second;
2666     timer.expires_after(
2667         std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2668     timer.async_wait([&configData](const boost::system::error_code ec) {
2669         if (ec)
2670         {
2671             lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2672                        configData.name, "MSG", ec.message());
2673             dBusRetryTimers.erase(configData.name);
2674             return;
2675         }
2676 
2677         int property = getProperty(configData);
2678 
2679         if (property >= 0)
2680         {
2681             setInitialValue(configData, (property > 0));
2682             dBusRetryTimers.erase(configData.name);
2683         }
2684     });
2685 }
2686 } // namespace power_control
2687 
main(int argc,char * argv[])2688 int main(int argc, char* argv[])
2689 {
2690     using namespace power_control;
2691 
2692     if (argc > 1)
2693     {
2694         node = argv[1];
2695     }
2696     lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2697               node);
2698 
2699     conn = std::make_shared<sdbusplus::asio::connection>(io);
2700 
2701     // Load GPIO's through json config file
2702     if (loadConfigValues() == -1)
2703     {
2704         lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
2705     }
2706     /* Currently for single host based systems additional busname is added
2707     with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2708     Going forward for single hosts the old bus name without zero numbering
2709     will be removed when all other applications adapted to the
2710     bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2711 
2712     if (node == "0")
2713     {
2714         // Request all the dbus names
2715         conn->request_name(hostDbusName.c_str());
2716         conn->request_name(chassisDbusName.c_str());
2717         conn->request_name(osDbusName.c_str());
2718         conn->request_name(buttonDbusName.c_str());
2719         conn->request_name(nmiDbusName.c_str());
2720         conn->request_name(rstCauseDbusName.c_str());
2721     }
2722 
2723     hostDbusName += node;
2724     chassisDbusName += node;
2725     osDbusName += node;
2726     buttonDbusName += node;
2727     nmiDbusName += node;
2728     rstCauseDbusName += node;
2729 
2730     // Request all the dbus names
2731     conn->request_name(hostDbusName.c_str());
2732     conn->request_name(chassisDbusName.c_str());
2733     conn->request_name(osDbusName.c_str());
2734     conn->request_name(buttonDbusName.c_str());
2735     conn->request_name(nmiDbusName.c_str());
2736     conn->request_name(rstCauseDbusName.c_str());
2737 
2738     if (sioPwrGoodConfig.lineName.empty() ||
2739         sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
2740     {
2741         sioEnabled = false;
2742         lg2::info("SIO control GPIOs not defined, disable SIO support.");
2743     }
2744 
2745     // Request PS_PWROK GPIO events
2746     if (powerOkConfig.type == ConfigType::GPIO)
2747     {
2748         if (!requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler,
2749                                psPowerOKLine, psPowerOKEvent))
2750         {
2751             return -1;
2752         }
2753     }
2754     else if (powerOkConfig.type == ConfigType::DBUS)
2755     {
2756         static sdbusplus::bus::match_t powerOkEventMonitor =
2757             power_control::dbusGPIOMatcher(powerOkConfig, psPowerOKHandler);
2758     }
2759     else
2760     {
2761         lg2::error("PowerOk name should be configured from json config file");
2762         return -1;
2763     }
2764 
2765     if (sioEnabled == true)
2766     {
2767         // Request SIO_POWER_GOOD GPIO events
2768         if (sioPwrGoodConfig.type == ConfigType::GPIO)
2769         {
2770             if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
2771                                    sioPowerGoodHandler, sioPowerGoodLine,
2772                                    sioPowerGoodEvent))
2773             {
2774                 return -1;
2775             }
2776         }
2777         else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2778         {
2779             static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
2780                 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2781                                                sioPowerGoodHandler);
2782         }
2783         else
2784         {
2785             lg2::error(
2786                 "sioPwrGood name should be configured from json config file");
2787             return -1;
2788         }
2789 
2790         // Request SIO_ONCONTROL GPIO events
2791         if (sioOnControlConfig.type == ConfigType::GPIO)
2792         {
2793             if (!requestGPIOEvents(sioOnControlConfig.lineName,
2794                                    sioOnControlHandler, sioOnControlLine,
2795                                    sioOnControlEvent))
2796             {
2797                 return -1;
2798             }
2799         }
2800         else if (sioOnControlConfig.type == ConfigType::DBUS)
2801         {
2802             static sdbusplus::bus::match_t sioOnControlEventMonitor =
2803                 power_control::dbusGPIOMatcher(sioOnControlConfig,
2804                                                sioOnControlHandler);
2805         }
2806         else
2807         {
2808             lg2::error(
2809                 "sioOnControl name should be configured from jsonconfig file\n");
2810             return -1;
2811         }
2812 
2813         // Request SIO_S5 GPIO events
2814         if (sioS5Config.type == ConfigType::GPIO)
2815         {
2816             if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
2817                                    sioS5Line, sioS5Event))
2818             {
2819                 return -1;
2820             }
2821         }
2822         else if (sioS5Config.type == ConfigType::DBUS)
2823         {
2824             static sdbusplus::bus::match_t sioS5EventMonitor =
2825                 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
2826         }
2827         else
2828         {
2829             lg2::error("sioS5 name should be configured from json config file");
2830             return -1;
2831         }
2832     }
2833 
2834     // Request POWER_BUTTON GPIO events
2835     if (powerButtonConfig.type == ConfigType::GPIO)
2836     {
2837         if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2838                                powerButtonLine, powerButtonEvent))
2839         {
2840             return -1;
2841         }
2842     }
2843     else if (powerButtonConfig.type == ConfigType::DBUS)
2844     {
2845         static sdbusplus::bus::match_t powerButtonEventMonitor =
2846             power_control::dbusGPIOMatcher(powerButtonConfig,
2847                                            powerButtonHandler);
2848     }
2849 
2850     // Request RESET_BUTTON GPIO events
2851     if (resetButtonConfig.type == ConfigType::GPIO)
2852     {
2853         if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2854                                resetButtonLine, resetButtonEvent))
2855         {
2856             return -1;
2857         }
2858     }
2859     else if (resetButtonConfig.type == ConfigType::DBUS)
2860     {
2861         static sdbusplus::bus::match_t resetButtonEventMonitor =
2862             power_control::dbusGPIOMatcher(resetButtonConfig,
2863                                            resetButtonHandler);
2864     }
2865 
2866     // Request NMI_BUTTON GPIO events
2867     if (nmiButtonConfig.type == ConfigType::GPIO)
2868     {
2869         if (!nmiButtonConfig.lineName.empty())
2870         {
2871             requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
2872                               nmiButtonLine, nmiButtonEvent);
2873         }
2874     }
2875     else if (nmiButtonConfig.type == ConfigType::DBUS)
2876     {
2877         static sdbusplus::bus::match_t nmiButtonEventMonitor =
2878             power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
2879     }
2880 
2881     // Request ID_BUTTON GPIO events
2882     if (idButtonConfig.type == ConfigType::GPIO)
2883     {
2884         if (!idButtonConfig.lineName.empty())
2885         {
2886             requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
2887                               idButtonLine, idButtonEvent);
2888         }
2889     }
2890     else if (idButtonConfig.type == ConfigType::DBUS)
2891     {
2892         static sdbusplus::bus::match_t idButtonEventMonitor =
2893             power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
2894     }
2895 
2896 #ifdef USE_PLT_RST
2897     sdbusplus::bus::match_t pltRstMatch(
2898         *conn,
2899         "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2900         "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
2901         hostMiscHandler);
2902 #endif
2903 
2904     // Request POST_COMPLETE GPIO events
2905     if (postCompleteConfig.type == ConfigType::GPIO)
2906     {
2907         if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2908                                postCompleteLine, postCompleteEvent))
2909         {
2910             return -1;
2911         }
2912     }
2913     else if (postCompleteConfig.type == ConfigType::DBUS)
2914     {
2915         static sdbusplus::bus::match_t postCompleteEventMonitor =
2916             power_control::dbusGPIOMatcher(postCompleteConfig,
2917                                            postCompleteHandler);
2918     }
2919     else
2920     {
2921         lg2::error(
2922             "postComplete name should be configured from json config file");
2923         return -1;
2924     }
2925 
2926     // initialize NMI_OUT GPIO.
2927     if (!nmiOutConfig.lineName.empty())
2928     {
2929         setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2930                       nmiOutLine);
2931     }
2932 
2933     // Initialize POWER_OUT and RESET_OUT GPIO.
2934     gpiod::line line;
2935     if (!powerOutConfig.lineName.empty())
2936     {
2937         if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2938                            line))
2939         {
2940             return -1;
2941         }
2942     }
2943     else
2944     {
2945         lg2::error("powerOut name should be configured from json config file");
2946         return -1;
2947     }
2948 
2949     if (!resetOutConfig.lineName.empty())
2950     {
2951         if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2952                            line))
2953         {
2954             return -1;
2955         }
2956     }
2957     else
2958     {
2959         lg2::error("ResetOut name should be configured from json config file");
2960         return -1;
2961     }
2962     // Release line
2963     line.reset();
2964 
2965     // Initialize the power state and operating system state
2966     powerState = PowerState::off;
2967     operatingSystemState = OperatingSystemStateStage::Inactive;
2968     // Check power good
2969 
2970     if (powerOkConfig.type == ConfigType::GPIO)
2971     {
2972         if (psPowerOKLine.get_value() > 0 ||
2973             (sioEnabled &&
2974              (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
2975         {
2976             powerState = PowerState::on;
2977         }
2978     }
2979     else
2980     {
2981         if (getProperty(powerOkConfig))
2982         {
2983             powerState = PowerState::on;
2984         }
2985     }
2986     // Check if we need to start the Power Restore policy
2987     if (powerState != PowerState::on)
2988     {
2989         powerRestore.run();
2990     }
2991 
2992     if (nmiOutLine)
2993         nmiSourcePropertyMonitor();
2994 
2995     lg2::info("Initializing power state.");
2996     logStateTransition(powerState);
2997 
2998     // Power Control Service
2999     sdbusplus::asio::object_server hostServer =
3000         sdbusplus::asio::object_server(conn);
3001 
3002     // Power Control Interface
3003     hostIface =
3004         hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
3005                                  "xyz.openbmc_project.State.Host");
3006     // Interface for IPMI/Redfish initiated host state transitions
3007     hostIface->register_property(
3008         "RequestedHostTransition",
3009         std::string("xyz.openbmc_project.State.Host.Transition.Off"),
3010         [](const std::string& requested, std::string& resp) {
3011         if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
3012         {
3013             // if power button is masked, ignore this
3014             if (!powerButtonMask)
3015             {
3016                 sendPowerControlEvent(Event::gracefulPowerOffRequest);
3017                 addRestartCause(RestartCause::command);
3018             }
3019             else
3020             {
3021                 lg2::info("Power Button Masked.");
3022                 throw std::invalid_argument("Transition Request Masked");
3023                 return 0;
3024             }
3025         }
3026         else if (requested == "xyz.openbmc_project.State.Host.Transition.On")
3027         {
3028             // if power button is masked, ignore this
3029             if (!powerButtonMask)
3030             {
3031                 sendPowerControlEvent(Event::powerOnRequest);
3032                 addRestartCause(RestartCause::command);
3033             }
3034             else
3035             {
3036                 lg2::info("Power Button Masked.");
3037                 throw std::invalid_argument("Transition Request Masked");
3038                 return 0;
3039             }
3040         }
3041         else if (requested ==
3042                  "xyz.openbmc_project.State.Host.Transition.Reboot")
3043         {
3044             // if power button is masked, ignore this
3045             if (!powerButtonMask)
3046             {
3047                 sendPowerControlEvent(Event::powerCycleRequest);
3048                 addRestartCause(RestartCause::command);
3049             }
3050             else
3051             {
3052                 lg2::info("Power Button Masked.");
3053                 throw std::invalid_argument("Transition Request Masked");
3054                 return 0;
3055             }
3056         }
3057         else if (requested ==
3058                  "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3059         {
3060             // if reset button is masked, ignore this
3061             if (!resetButtonMask)
3062             {
3063                 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3064                 addRestartCause(RestartCause::command);
3065             }
3066             else
3067             {
3068                 lg2::info("Reset Button Masked.");
3069                 throw std::invalid_argument("Transition Request Masked");
3070                 return 0;
3071             }
3072         }
3073         else if (requested ==
3074                  "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3075         {
3076             // if reset button is masked, ignore this
3077             if (!resetButtonMask)
3078             {
3079                 sendPowerControlEvent(Event::resetRequest);
3080                 addRestartCause(RestartCause::command);
3081             }
3082             else
3083             {
3084                 lg2::info("Reset Button Masked.");
3085                 throw std::invalid_argument("Transition Request Masked");
3086                 return 0;
3087             }
3088         }
3089         else
3090         {
3091             lg2::error("Unrecognized host state transition request.");
3092             throw std::invalid_argument("Unrecognized Transition Request");
3093             return 0;
3094         }
3095         resp = requested;
3096         return 1;
3097     });
3098     hostIface->register_property("CurrentHostState",
3099                                  std::string(getHostState(powerState)));
3100 
3101     hostIface->initialize();
3102 
3103     // Chassis Control Service
3104     sdbusplus::asio::object_server chassisServer =
3105         sdbusplus::asio::object_server(conn);
3106 
3107     // Chassis Control Interface
3108     chassisIface =
3109         chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
3110                                     "xyz.openbmc_project.State.Chassis");
3111 
3112     chassisIface->register_property(
3113         "RequestedPowerTransition",
3114         std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3115         [](const std::string& requested, std::string& resp) {
3116         if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
3117         {
3118             // if power button is masked, ignore this
3119             if (!powerButtonMask)
3120             {
3121                 sendPowerControlEvent(Event::powerOffRequest);
3122                 addRestartCause(RestartCause::command);
3123             }
3124             else
3125             {
3126                 lg2::info("Power Button Masked.");
3127                 throw std::invalid_argument("Transition Request Masked");
3128                 return 0;
3129             }
3130         }
3131         else if (requested == "xyz.openbmc_project.State.Chassis.Transition.On")
3132         {
3133             // if power button is masked, ignore this
3134             if (!powerButtonMask)
3135             {
3136                 sendPowerControlEvent(Event::powerOnRequest);
3137                 addRestartCause(RestartCause::command);
3138             }
3139             else
3140             {
3141                 lg2::info("Power Button Masked.");
3142                 throw std::invalid_argument("Transition Request Masked");
3143                 return 0;
3144             }
3145         }
3146         else if (requested ==
3147                  "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3148         {
3149             // if power button is masked, ignore this
3150             if (!powerButtonMask)
3151             {
3152                 sendPowerControlEvent(Event::powerCycleRequest);
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
3163         {
3164             lg2::error("Unrecognized chassis state transition request.");
3165             throw std::invalid_argument("Unrecognized Transition Request");
3166             return 0;
3167         }
3168         resp = requested;
3169         return 1;
3170     });
3171     chassisIface->register_property("CurrentPowerState",
3172                                     std::string(getChassisState(powerState)));
3173     chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
3174 
3175     chassisIface->initialize();
3176 
3177 #ifdef CHASSIS_SYSTEM_RESET
3178     // Chassis System Service
3179     sdbusplus::asio::object_server chassisSysServer =
3180         sdbusplus::asio::object_server(conn);
3181 
3182     // Chassis System Interface
3183     chassisSysIface = chassisSysServer.add_interface(
3184         "/xyz/openbmc_project/state/chassis_system0",
3185         "xyz.openbmc_project.State.Chassis");
3186 
3187     chassisSysIface->register_property(
3188         "RequestedPowerTransition",
3189         std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3190         [](const std::string& requested, std::string& resp) {
3191         if (requested ==
3192             "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3193         {
3194             systemReset();
3195             addRestartCause(RestartCause::command);
3196         }
3197         else
3198         {
3199             lg2::error("Unrecognized chassis system state transition request.");
3200             throw std::invalid_argument("Unrecognized Transition Request");
3201             return 0;
3202         }
3203         resp = requested;
3204         return 1;
3205     });
3206     chassisSysIface->register_property(
3207         "CurrentPowerState", std::string(getChassisState(powerState)));
3208     chassisSysIface->register_property("LastStateChangeTime",
3209                                        getCurrentTimeMs());
3210 
3211     chassisSysIface->initialize();
3212 
3213     if (!slotPowerConfig.lineName.empty())
3214     {
3215         if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3216         {
3217             return -1;
3218         }
3219 
3220         slotPowerState = SlotPowerState::off;
3221         if (slotPowerLine.get_value() > 0)
3222         {
3223             slotPowerState = SlotPowerState::on;
3224         }
3225 
3226         chassisSlotIface = chassisSysServer.add_interface(
3227             "/xyz/openbmc_project/state/chassis_system" + node,
3228             "xyz.openbmc_project.State.Chassis");
3229         chassisSlotIface->register_property(
3230             "RequestedPowerTransition",
3231             std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3232             [](const std::string& requested, std::string& resp) {
3233             if (requested == "xyz.openbmc_project.State.Chassis.Transition.On")
3234             {
3235                 slotPowerOn();
3236             }
3237             else if (requested ==
3238                      "xyz.openbmc_project.State.Chassis.Transition.Off")
3239             {
3240                 slotPowerOff();
3241             }
3242             else if (requested ==
3243                      "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3244             {
3245                 slotPowerCycle();
3246             }
3247             else
3248             {
3249                 lg2::error(
3250                     "Unrecognized chassis system state transition request.\n");
3251                 throw std::invalid_argument("Unrecognized Transition Request");
3252                 return 0;
3253             }
3254             resp = requested;
3255             return 1;
3256         });
3257         chassisSlotIface->register_property(
3258             "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3259         chassisSlotIface->register_property("LastStateChangeTime",
3260                                             getCurrentTimeMs());
3261         chassisSlotIface->initialize();
3262     }
3263 #endif
3264     // Buttons Service
3265     sdbusplus::asio::object_server buttonsServer =
3266         sdbusplus::asio::object_server(conn);
3267 
3268     if (!powerButtonConfig.lineName.empty())
3269     {
3270         // Power Button Interface
3271         power_control::powerButtonIface = buttonsServer.add_interface(
3272             "/xyz/openbmc_project/chassis/buttons/power",
3273             "xyz.openbmc_project.Chassis.Buttons");
3274 
3275         powerButtonIface->register_property(
3276             "ButtonMasked", false, [](const bool requested, bool& current) {
3277             if (requested)
3278             {
3279                 if (powerButtonMask)
3280                 {
3281                     return 1;
3282                 }
3283                 if (!setGPIOOutput(powerOutConfig.lineName,
3284                                    !powerOutConfig.polarity, powerButtonMask))
3285                 {
3286                     throw std::runtime_error("Failed to request GPIO");
3287                     return 0;
3288                 }
3289                 lg2::info("Power Button Masked.");
3290             }
3291             else
3292             {
3293                 if (!powerButtonMask)
3294                 {
3295                     return 1;
3296                 }
3297                 lg2::info("Power Button Un-masked");
3298                 powerButtonMask.reset();
3299             }
3300             // Update the mask setting
3301             current = requested;
3302             return 1;
3303         });
3304 
3305         // Check power button state
3306         bool powerButtonPressed;
3307         if (powerButtonConfig.type == ConfigType::GPIO)
3308         {
3309             powerButtonPressed = powerButtonLine.get_value() == 0;
3310         }
3311         else
3312         {
3313             powerButtonPressed = getProperty(powerButtonConfig) == 0;
3314         }
3315 
3316         powerButtonIface->register_property("ButtonPressed",
3317                                             powerButtonPressed);
3318 
3319         powerButtonIface->initialize();
3320     }
3321 
3322     if (!resetButtonConfig.lineName.empty())
3323     {
3324         // Reset Button Interface
3325 
3326         resetButtonIface = buttonsServer.add_interface(
3327             "/xyz/openbmc_project/chassis/buttons/reset",
3328             "xyz.openbmc_project.Chassis.Buttons");
3329 
3330         resetButtonIface->register_property(
3331             "ButtonMasked", false, [](const bool requested, bool& current) {
3332             if (requested)
3333             {
3334                 if (resetButtonMask)
3335                 {
3336                     return 1;
3337                 }
3338                 if (!setGPIOOutput(resetOutConfig.lineName,
3339                                    !resetOutConfig.polarity, resetButtonMask))
3340                 {
3341                     throw std::runtime_error("Failed to request GPIO");
3342                     return 0;
3343                 }
3344                 lg2::info("Reset Button Masked.");
3345             }
3346             else
3347             {
3348                 if (!resetButtonMask)
3349                 {
3350                     return 1;
3351                 }
3352                 lg2::info("Reset Button Un-masked");
3353                 resetButtonMask.reset();
3354             }
3355             // Update the mask setting
3356             current = requested;
3357             return 1;
3358         });
3359 
3360         // Check reset button state
3361         bool resetButtonPressed;
3362         if (resetButtonConfig.type == ConfigType::GPIO)
3363         {
3364             resetButtonPressed = resetButtonLine.get_value() == 0;
3365         }
3366         else
3367         {
3368             resetButtonPressed = getProperty(resetButtonConfig) == 0;
3369         }
3370 
3371         resetButtonIface->register_property("ButtonPressed",
3372                                             resetButtonPressed);
3373 
3374         resetButtonIface->initialize();
3375     }
3376 
3377     if (nmiButtonLine)
3378     {
3379         // NMI Button Interface
3380         nmiButtonIface = buttonsServer.add_interface(
3381             "/xyz/openbmc_project/chassis/buttons/nmi",
3382             "xyz.openbmc_project.Chassis.Buttons");
3383 
3384         nmiButtonIface->register_property(
3385             "ButtonMasked", false, [](const bool requested, bool& current) {
3386             if (nmiButtonMasked == requested)
3387             {
3388                 // NMI button mask is already set as requested, so no change
3389                 return 1;
3390             }
3391             if (requested)
3392             {
3393                 lg2::info("NMI Button Masked.");
3394                 nmiButtonMasked = true;
3395             }
3396             else
3397             {
3398                 lg2::info("NMI Button Un-masked.");
3399                 nmiButtonMasked = false;
3400             }
3401             // Update the mask setting
3402             current = nmiButtonMasked;
3403             return 1;
3404         });
3405 
3406         // Check NMI button state
3407         bool nmiButtonPressed;
3408         if (nmiButtonConfig.type == ConfigType::GPIO)
3409         {
3410             nmiButtonPressed = nmiButtonLine.get_value() == 0;
3411         }
3412         else
3413         {
3414             nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3415         }
3416 
3417         nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
3418 
3419         nmiButtonIface->initialize();
3420     }
3421 
3422     if (nmiOutLine)
3423     {
3424         // NMI out Service
3425         sdbusplus::asio::object_server nmiOutServer =
3426             sdbusplus::asio::object_server(conn);
3427 
3428         // NMI out Interface
3429         nmiOutIface = nmiOutServer.add_interface(
3430             "/xyz/openbmc_project/control/host" + node + "/nmi",
3431             "xyz.openbmc_project.Control.Host.NMI");
3432         nmiOutIface->register_method("NMI", nmiReset);
3433         nmiOutIface->initialize();
3434     }
3435 
3436     if (idButtonLine)
3437     {
3438         // ID Button Interface
3439         idButtonIface = buttonsServer.add_interface(
3440             "/xyz/openbmc_project/chassis/buttons/id",
3441             "xyz.openbmc_project.Chassis.Buttons");
3442 
3443         // Check ID button state
3444         bool idButtonPressed;
3445         if (idButtonConfig.type == ConfigType::GPIO)
3446         {
3447             idButtonPressed = idButtonLine.get_value() == 0;
3448         }
3449         else
3450         {
3451             idButtonPressed = getProperty(idButtonConfig) == 0;
3452         }
3453 
3454         idButtonIface->register_property("ButtonPressed", idButtonPressed);
3455 
3456         idButtonIface->initialize();
3457     }
3458 
3459     // OS State Service
3460     sdbusplus::asio::object_server osServer =
3461         sdbusplus::asio::object_server(conn);
3462 
3463     // OS State Interface
3464     osIface = osServer.add_interface(
3465         "/xyz/openbmc_project/state/host" + node,
3466         "xyz.openbmc_project.State.OperatingSystem.Status");
3467 
3468     // Get the initial OS state based on POST complete
3469     //      0: Asserted, OS state is "Standby" (ready to boot)
3470     //      1: De-Asserted, OS state is "Inactive"
3471     OperatingSystemStateStage osState;
3472     if (postCompleteConfig.type == ConfigType::GPIO)
3473     {
3474         osState = postCompleteLine.get_value() > 0
3475                       ? OperatingSystemStateStage::Inactive
3476                       : OperatingSystemStateStage::Standby;
3477     }
3478     else
3479     {
3480         osState = getProperty(postCompleteConfig) > 0
3481                       ? OperatingSystemStateStage::Inactive
3482                       : OperatingSystemStateStage::Standby;
3483     }
3484 
3485     osIface->register_property(
3486         "OperatingSystemState",
3487         std::string(getOperatingSystemStateStage(osState)));
3488 
3489     osIface->initialize();
3490 
3491     // Restart Cause Service
3492     sdbusplus::asio::object_server restartCauseServer =
3493         sdbusplus::asio::object_server(conn);
3494 
3495     // Restart Cause Interface
3496     restartCauseIface = restartCauseServer.add_interface(
3497         "/xyz/openbmc_project/control/host" + node + "/restart_cause",
3498         "xyz.openbmc_project.Control.Host.RestartCause");
3499 
3500     restartCauseIface->register_property(
3501         "RestartCause",
3502         std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3503 
3504     restartCauseIface->register_property(
3505         "RequestedRestartCause",
3506         std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3507         [](const std::string& requested, std::string& resp) {
3508         if (requested ==
3509             "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3510         {
3511             addRestartCause(RestartCause::watchdog);
3512         }
3513         else
3514         {
3515             throw std::invalid_argument("Unrecognized RestartCause Request");
3516             return 0;
3517         }
3518 
3519         lg2::info("RestartCause requested: {RESTART_CAUSE}", "RESTART_CAUSE",
3520                   requested);
3521         resp = requested;
3522         return 1;
3523     });
3524 
3525     restartCauseIface->initialize();
3526 
3527     currentHostStateMonitor();
3528 
3529     if (!hpmStbyEnConfig.lineName.empty())
3530     {
3531         // Set to indicate BMC's power control module is ready to take
3532         // the inputs [PWR_GOOD] from the HPM FPGA
3533         gpiod::line hpmLine;
3534         if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3535                            hpmLine))
3536         {
3537             return -1;
3538         }
3539     }
3540 
3541     io.run();
3542 
3543     return 0;
3544 }
3545