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