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