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