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