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