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