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