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