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