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