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 = 950 "/xyz/openbmc_project/control/host" + node + "/power_restore_policy"; 951 powerRestorePolicyLog(); 952 // this list only needs to be created once 953 if (matches.empty()) 954 { 955 matches.emplace_back( 956 *conn, 957 match_rules::interfacesAdded() + 958 match_rules::argNpath(0, powerRestorePolicyObject) + 959 match_rules::sender(setingsService), 960 powerRestoreConfigHandler, this); 961 #ifdef USE_ACBOOT 962 matches.emplace_back(*conn, 963 match_rules::interfacesAdded() + 964 match_rules::argNpath(0, powerACBootObject) + 965 match_rules::sender(setingsService), 966 powerRestoreConfigHandler, this); 967 matches.emplace_back(*conn, 968 match_rules::propertiesChanged(powerACBootObject, 969 powerACBootIface) + 970 match_rules::sender(setingsService), 971 powerRestoreConfigHandler, this); 972 #endif // USE_ACBOOT 973 } 974 975 // Check if it's already on DBus 976 conn->async_method_call( 977 [this](boost::system::error_code ec, 978 const dbusPropertiesList properties) { 979 if (ec) 980 { 981 return; 982 } 983 setProperties(properties); 984 }, 985 setingsService, powerRestorePolicyObject, 986 "org.freedesktop.DBus.Properties", "GetAll", powerRestorePolicyIface); 987 988 #ifdef USE_ACBOOT 989 // Check if it's already on DBus 990 conn->async_method_call( 991 [this](boost::system::error_code ec, 992 const dbusPropertiesList properties) { 993 if (ec) 994 { 995 return; 996 } 997 setProperties(properties); 998 }, 999 setingsService, powerACBootObject, "org.freedesktop.DBus.Properties", 1000 "GetAll", powerACBootIface); 1001 #endif 1002 } 1003 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([this](const boost::system::error_code 1077 ec) { 1078 if (ec) 1079 { 1080 // operation_aborted is expected if timer is canceled before 1081 // completion. 1082 if (ec == boost::asio::error::operation_aborted) 1083 { 1084 return; 1085 } 1086 lg2::error( 1087 "power restore policy async_wait failed: {ERROR_MSG}", 1088 "ERROR_MSG", ec.message()); 1089 } 1090 else 1091 { 1092 lg2::info("Power Restore delay timer expired"); 1093 } 1094 invoke(); 1095 }); 1096 timerFired = true; 1097 } 1098 else 1099 { 1100 invoke(); 1101 } 1102 } 1103 } 1104 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( 1149 const std::string& name, const std::function<void(bool)>& eventHandler, 1150 gpiod::line& line, boost::asio::posix::stream_descriptor& event) 1151 { 1152 event.async_wait( 1153 boost::asio::posix::stream_descriptor::wait_read, 1154 [&name, eventHandler, &line, 1155 &event](const boost::system::error_code ec) { 1156 if (ec) 1157 { 1158 lg2::error("{GPIO_NAME} fd handler error: {ERROR_MSG}", 1159 "GPIO_NAME", name, "ERROR_MSG", ec.message()); 1160 // TODO: throw here to force power-control to 1161 // restart? 1162 return; 1163 } 1164 gpiod::line_event line_event = line.event_read(); 1165 eventHandler(line_event.event_type == 1166 gpiod::line_event::RISING_EDGE); 1167 waitForGPIOEvent(name, eventHandler, line, event); 1168 }); 1169 } 1170 1171 static bool requestGPIOEvents( 1172 const std::string& name, const std::function<void(bool)>& handler, 1173 gpiod::line& gpioLine, 1174 boost::asio::posix::stream_descriptor& gpioEventDescriptor) 1175 { 1176 // Find the GPIO line 1177 gpioLine = gpiod::find_line(name); 1178 if (!gpioLine) 1179 { 1180 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name); 1181 return false; 1182 } 1183 1184 try 1185 { 1186 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}}); 1187 } 1188 catch (const std::exception& e) 1189 { 1190 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}", 1191 "GPIO_NAME", name, "ERROR", e); 1192 return false; 1193 } 1194 1195 int gpioLineFd = gpioLine.event_get_fd(); 1196 if (gpioLineFd < 0) 1197 { 1198 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name); 1199 return false; 1200 } 1201 1202 gpioEventDescriptor.assign(gpioLineFd); 1203 1204 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor); 1205 return true; 1206 } 1207 1208 static bool setGPIOOutput(const std::string& name, const int value, 1209 gpiod::line& gpioLine) 1210 { 1211 // Find the GPIO line 1212 gpioLine = gpiod::find_line(name); 1213 if (!gpioLine) 1214 { 1215 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name); 1216 return false; 1217 } 1218 1219 // Request GPIO output to specified value 1220 try 1221 { 1222 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}}, 1223 value); 1224 } 1225 catch (const std::exception& e) 1226 { 1227 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME", 1228 name, "ERROR", e); 1229 return false; 1230 } 1231 1232 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name, 1233 "GPIO_VALUE", value); 1234 return true; 1235 } 1236 1237 static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine, 1238 const std::string& name, const int value, 1239 const int durationMs) 1240 { 1241 // Set the masked GPIO line to the specified value 1242 maskedGPIOLine.set_value(value); 1243 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name, 1244 "GPIO_VALUE", value); 1245 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs)); 1246 gpioAssertTimer.async_wait( 1247 [maskedGPIOLine, value, name](const boost::system::error_code ec) { 1248 // Set the masked GPIO line back to the opposite value 1249 maskedGPIOLine.set_value(!value); 1250 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name); 1251 if (ec) 1252 { 1253 // operation_aborted is expected if timer is canceled before 1254 // completion. 1255 if (ec != boost::asio::error::operation_aborted) 1256 { 1257 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 1258 "GPIO_NAME", name, "ERROR_MSG", ec.message()); 1259 } 1260 } 1261 }); 1262 return 0; 1263 } 1264 1265 static int setGPIOOutputForMs(const ConfigData& config, const int value, 1266 const int durationMs) 1267 { 1268 // If the requested GPIO is masked, use the mask line to set the output 1269 if (powerButtonMask && config.lineName == powerOutConfig.lineName) 1270 { 1271 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value, 1272 durationMs); 1273 } 1274 if (resetButtonMask && config.lineName == resetOutConfig.lineName) 1275 { 1276 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value, 1277 durationMs); 1278 } 1279 1280 // No mask set, so request and set the GPIO normally 1281 gpiod::line gpioLine; 1282 if (!setGPIOOutput(config.lineName, value, gpioLine)) 1283 { 1284 return -1; 1285 } 1286 const std::string name = config.lineName; 1287 1288 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs)); 1289 gpioAssertTimer.async_wait( 1290 [gpioLine, value, name](const boost::system::error_code ec) { 1291 // Set the GPIO line back to the opposite value 1292 gpioLine.set_value(!value); 1293 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name); 1294 if (ec) 1295 { 1296 // operation_aborted is expected if timer is canceled before 1297 // completion. 1298 if (ec != boost::asio::error::operation_aborted) 1299 { 1300 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 1301 "GPIO_NAME", name, "ERROR_MSG", ec.message()); 1302 } 1303 } 1304 }); 1305 return 0; 1306 } 1307 1308 static int assertGPIOForMs(const ConfigData& config, const int durationMs) 1309 { 1310 return setGPIOOutputForMs(config, config.polarity, durationMs); 1311 } 1312 1313 static void powerOn() 1314 { 1315 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]); 1316 } 1317 #ifdef CHASSIS_SYSTEM_RESET 1318 static int slotPowerOn() 1319 { 1320 if (power_control::slotPowerState != power_control::SlotPowerState::on) 1321 { 1322 slotPowerLine.set_value(1); 1323 1324 if (slotPowerLine.get_value() > 0) 1325 { 1326 setSlotPowerState(SlotPowerState::on); 1327 lg2::info("Slot Power is switched On\n"); 1328 } 1329 else 1330 { 1331 return -1; 1332 } 1333 } 1334 else 1335 { 1336 lg2::info("Slot Power is already in 'On' state\n"); 1337 return -1; 1338 } 1339 return 0; 1340 } 1341 static int slotPowerOff() 1342 { 1343 if (power_control::slotPowerState != power_control::SlotPowerState::off) 1344 { 1345 slotPowerLine.set_value(0); 1346 1347 if (!(slotPowerLine.get_value() > 0)) 1348 { 1349 setSlotPowerState(SlotPowerState::off); 1350 setPowerState(PowerState::off); 1351 lg2::info("Slot Power is switched Off\n"); 1352 } 1353 else 1354 { 1355 return -1; 1356 } 1357 } 1358 else 1359 { 1360 lg2::info("Slot Power is already in 'Off' state\n"); 1361 return -1; 1362 } 1363 return 0; 1364 } 1365 static void slotPowerCycle() 1366 { 1367 lg2::info("Slot Power Cycle started\n"); 1368 slotPowerOff(); 1369 slotPowerCycleTimer.expires_after( 1370 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"])); 1371 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) { 1372 if (ec) 1373 { 1374 if (ec != boost::asio::error::operation_aborted) 1375 { 1376 lg2::error( 1377 "Slot Power cycle timer async_wait failed: {ERROR_MSG}", 1378 "ERROR_MSG", ec.message()); 1379 } 1380 lg2::info("Slot Power cycle timer canceled\n"); 1381 return; 1382 } 1383 lg2::info("Slot Power cycle timer completed\n"); 1384 slotPowerOn(); 1385 lg2::info("Slot Power Cycle Completed\n"); 1386 }); 1387 } 1388 #endif 1389 static void gracefulPowerOff() 1390 { 1391 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]); 1392 } 1393 1394 static void forcePowerOff() 1395 { 1396 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0) 1397 { 1398 return; 1399 } 1400 1401 // If the force off timer expires, then the power-button override failed 1402 gpioAssertTimer.async_wait([](const boost::system::error_code ec) { 1403 if (ec) 1404 { 1405 // operation_aborted is expected if timer is canceled before 1406 // completion. 1407 if (ec != boost::asio::error::operation_aborted) 1408 { 1409 lg2::error("Force power off async_wait failed: {ERROR_MSG}", 1410 "ERROR_MSG", ec.message()); 1411 } 1412 return; 1413 } 1414 1415 lg2::error("Power-button override failed. Not sure what to do now."); 1416 }); 1417 } 1418 1419 static void reset() 1420 { 1421 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]); 1422 } 1423 1424 static void gracefulPowerOffTimerStart() 1425 { 1426 lg2::info("Graceful power-off timer started"); 1427 gracefulPowerOffTimer.expires_after( 1428 std::chrono::seconds(TimerMap["GracefulPowerOffS"])); 1429 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) { 1430 if (ec) 1431 { 1432 // operation_aborted is expected if timer is canceled before 1433 // completion. 1434 if (ec != boost::asio::error::operation_aborted) 1435 { 1436 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}", 1437 "ERROR_MSG", ec.message()); 1438 } 1439 lg2::info("Graceful power-off timer canceled"); 1440 return; 1441 } 1442 lg2::info("Graceful power-off timer completed"); 1443 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired); 1444 }); 1445 } 1446 1447 static void powerCycleTimerStart() 1448 { 1449 lg2::info("Power-cycle timer started"); 1450 powerCycleTimer.expires_after( 1451 std::chrono::milliseconds(TimerMap["PowerCycleMs"])); 1452 powerCycleTimer.async_wait([](const boost::system::error_code ec) { 1453 if (ec) 1454 { 1455 // operation_aborted is expected if timer is canceled before 1456 // completion. 1457 if (ec != boost::asio::error::operation_aborted) 1458 { 1459 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}", 1460 "ERROR_MSG", ec.message()); 1461 } 1462 lg2::info("Power-cycle timer canceled"); 1463 return; 1464 } 1465 lg2::info("Power-cycle timer completed"); 1466 sendPowerControlEvent(Event::powerCycleTimerExpired); 1467 }); 1468 } 1469 1470 static void psPowerOKWatchdogTimerStart() 1471 { 1472 lg2::info("power supply power OK watchdog timer started"); 1473 psPowerOKWatchdogTimer.expires_after( 1474 std::chrono::milliseconds(TimerMap["PsPowerOKWatchdogMs"])); 1475 psPowerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) { 1476 if (ec) 1477 { 1478 // operation_aborted is expected if timer is canceled before 1479 // completion. 1480 if (ec != boost::asio::error::operation_aborted) 1481 { 1482 lg2::error( 1483 "power supply power OK watchdog async_wait failed: {ERROR_MSG}", 1484 "ERROR_MSG", ec.message()); 1485 } 1486 lg2::info("power supply power OK watchdog timer canceled"); 1487 return; 1488 } 1489 lg2::info("power supply power OK watchdog timer expired"); 1490 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired); 1491 }); 1492 } 1493 1494 static void warmResetCheckTimerStart() 1495 { 1496 lg2::info("Warm reset check timer started"); 1497 warmResetCheckTimer.expires_after( 1498 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"])); 1499 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) { 1500 if (ec) 1501 { 1502 // operation_aborted is expected if timer is canceled before 1503 // completion. 1504 if (ec != boost::asio::error::operation_aborted) 1505 { 1506 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}", 1507 "ERROR_MSG", ec.message()); 1508 } 1509 lg2::info("Warm reset check timer canceled"); 1510 return; 1511 } 1512 lg2::info("Warm reset check timer completed"); 1513 sendPowerControlEvent(Event::warmResetDetected); 1514 }); 1515 } 1516 1517 static void pohCounterTimerStart() 1518 { 1519 lg2::info("POH timer started"); 1520 // Set the time-out as 1 hour, to align with POH command in ipmid 1521 pohCounterTimer.expires_after(std::chrono::hours(1)); 1522 pohCounterTimer.async_wait([](const boost::system::error_code& ec) { 1523 if (ec) 1524 { 1525 // operation_aborted is expected if timer is canceled before 1526 // completion. 1527 if (ec != boost::asio::error::operation_aborted) 1528 { 1529 lg2::error("POH timer async_wait failed: {ERROR_MSG}", 1530 "ERROR_MSG", ec.message()); 1531 } 1532 lg2::info("POH timer canceled"); 1533 return; 1534 } 1535 1536 if (getHostState(powerState) != 1537 "xyz.openbmc_project.State.Host.HostState.Running") 1538 { 1539 return; 1540 } 1541 1542 conn->async_method_call( 1543 [](boost::system::error_code ec, 1544 const std::variant<uint32_t>& pohCounterProperty) { 1545 if (ec) 1546 { 1547 lg2::error("error getting poh counter"); 1548 return; 1549 } 1550 const uint32_t* pohCounter = 1551 std::get_if<uint32_t>(&pohCounterProperty); 1552 if (pohCounter == nullptr) 1553 { 1554 lg2::error("unable to read poh counter"); 1555 return; 1556 } 1557 1558 conn->async_method_call( 1559 [](boost::system::error_code ec) { 1560 if (ec) 1561 { 1562 lg2::error("failed to set poh counter"); 1563 } 1564 }, 1565 "xyz.openbmc_project.Settings", 1566 "/xyz/openbmc_project/state/chassis0", 1567 "org.freedesktop.DBus.Properties", "Set", 1568 "xyz.openbmc_project.State.PowerOnHours", "POHCounter", 1569 std::variant<uint32_t>(*pohCounter + 1)); 1570 }, 1571 "xyz.openbmc_project.Settings", 1572 "/xyz/openbmc_project/state/chassis0", 1573 "org.freedesktop.DBus.Properties", "Get", 1574 "xyz.openbmc_project.State.PowerOnHours", "POHCounter"); 1575 1576 pohCounterTimerStart(); 1577 }); 1578 } 1579 1580 static void currentHostStateMonitor() 1581 { 1582 if (getHostState(powerState) == 1583 "xyz.openbmc_project.State.Host.HostState.Running") 1584 { 1585 pohCounterTimerStart(); 1586 // Clear the restart cause set for the next restart 1587 clearRestartCause(); 1588 } 1589 else 1590 { 1591 pohCounterTimer.cancel(); 1592 // Set the restart cause set for this restart 1593 setRestartCause(); 1594 } 1595 1596 static auto match = sdbusplus::bus::match_t( 1597 *conn, 1598 "type='signal',member='PropertiesChanged', " 1599 "interface='org.freedesktop.DBus.Properties', " 1600 "arg0='xyz.openbmc_project.State.Host'", 1601 [](sdbusplus::message_t& message) { 1602 std::string intfName; 1603 std::map<std::string, std::variant<std::string>> properties; 1604 1605 try 1606 { 1607 message.read(intfName, properties); 1608 } 1609 catch (const std::exception& e) 1610 { 1611 lg2::error("Unable to read host state: {ERROR}", "ERROR", e); 1612 return; 1613 } 1614 if (properties.empty()) 1615 { 1616 lg2::error("ERROR: Empty PropertiesChanged signal received"); 1617 return; 1618 } 1619 1620 // We only want to check for CurrentHostState 1621 if (properties.begin()->first != "CurrentHostState") 1622 { 1623 return; 1624 } 1625 std::string* currentHostState = 1626 std::get_if<std::string>(&(properties.begin()->second)); 1627 if (currentHostState == nullptr) 1628 { 1629 lg2::error("{PROPERTY} property invalid", "PROPERTY", 1630 properties.begin()->first); 1631 return; 1632 } 1633 1634 if (*currentHostState == 1635 "xyz.openbmc_project.State.Host.HostState.Running") 1636 { 1637 pohCounterTimerStart(); 1638 // Clear the restart cause set for the next restart 1639 clearRestartCause(); 1640 sd_journal_send("MESSAGE=Host system DC power is on", 1641 "PRIORITY=%i", LOG_INFO, 1642 "REDFISH_MESSAGE_ID=%s", 1643 "OpenBMC.0.1.DCPowerOn", NULL); 1644 } 1645 else 1646 { 1647 pohCounterTimer.cancel(); 1648 // POST_COMPLETE GPIO event is not working in some platforms 1649 // when power state is changed to OFF. This resulted in 1650 // 'OperatingSystemState' to stay at 'Standby', even though 1651 // system is OFF. Set 'OperatingSystemState' to 'Inactive' 1652 // if HostState is trurned to OFF. 1653 setOperatingSystemState(OperatingSystemStateStage::Inactive); 1654 1655 // Set the restart cause set for this restart 1656 setRestartCause(); 1657 #ifdef USE_ACBOOT 1658 resetACBootProperty(); 1659 #endif // USE_ACBOOT 1660 sd_journal_send("MESSAGE=Host system DC power is off", 1661 "PRIORITY=%i", LOG_INFO, 1662 "REDFISH_MESSAGE_ID=%s", 1663 "OpenBMC.0.1.DCPowerOff", NULL); 1664 } 1665 }); 1666 } 1667 1668 static void sioPowerGoodWatchdogTimerStart() 1669 { 1670 lg2::info("SIO power good watchdog timer started"); 1671 sioPowerGoodWatchdogTimer.expires_after( 1672 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"])); 1673 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code 1674 ec) { 1675 if (ec) 1676 { 1677 // operation_aborted is expected if timer is canceled before 1678 // completion. 1679 if (ec != boost::asio::error::operation_aborted) 1680 { 1681 lg2::error( 1682 "SIO power good watchdog async_wait failed: {ERROR_MSG}", 1683 "ERROR_MSG", ec.message()); 1684 } 1685 lg2::info("SIO power good watchdog timer canceled"); 1686 return; 1687 } 1688 lg2::info("SIO power good watchdog timer completed"); 1689 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired); 1690 }); 1691 } 1692 1693 static void powerStateOn(const Event event) 1694 { 1695 logEvent(__FUNCTION__, event); 1696 switch (event) 1697 { 1698 case Event::psPowerOKDeAssert: 1699 setPowerState(PowerState::off); 1700 // DC power is unexpectedly lost, beep 1701 beep(beepPowerFail); 1702 break; 1703 case Event::sioS5Assert: 1704 setPowerState(PowerState::transitionToOff); 1705 #if IGNORE_SOFT_RESETS_DURING_POST 1706 // Only recognize soft resets once host gets past POST COMPLETE 1707 if (operatingSystemState != OperatingSystemStateStage::Standby) 1708 { 1709 ignoreNextSoftReset = true; 1710 } 1711 #endif 1712 addRestartCause(RestartCause::softReset); 1713 break; 1714 #if USE_PLT_RST 1715 case Event::pltRstAssert: 1716 #else 1717 case Event::postCompleteDeAssert: 1718 #endif 1719 setPowerState(PowerState::checkForWarmReset); 1720 #if IGNORE_SOFT_RESETS_DURING_POST 1721 // Only recognize soft resets once host gets past POST COMPLETE 1722 if (operatingSystemState != OperatingSystemStateStage::Standby) 1723 { 1724 ignoreNextSoftReset = true; 1725 } 1726 #endif 1727 addRestartCause(RestartCause::softReset); 1728 warmResetCheckTimerStart(); 1729 break; 1730 case Event::powerButtonPressed: 1731 setPowerState(PowerState::gracefulTransitionToOff); 1732 gracefulPowerOffTimerStart(); 1733 break; 1734 case Event::powerOffRequest: 1735 setPowerState(PowerState::transitionToOff); 1736 forcePowerOff(); 1737 break; 1738 case Event::gracefulPowerOffRequest: 1739 setPowerState(PowerState::gracefulTransitionToOff); 1740 gracefulPowerOffTimerStart(); 1741 gracefulPowerOff(); 1742 break; 1743 case Event::powerCycleRequest: 1744 setPowerState(PowerState::transitionToCycleOff); 1745 forcePowerOff(); 1746 break; 1747 case Event::gracefulPowerCycleRequest: 1748 setPowerState(PowerState::gracefulTransitionToCycleOff); 1749 gracefulPowerOffTimerStart(); 1750 gracefulPowerOff(); 1751 break; 1752 case Event::resetButtonPressed: 1753 setPowerState(PowerState::checkForWarmReset); 1754 warmResetCheckTimerStart(); 1755 break; 1756 case Event::resetRequest: 1757 reset(); 1758 break; 1759 default: 1760 lg2::info("No action taken."); 1761 break; 1762 } 1763 } 1764 1765 static void powerStateWaitForPSPowerOK(const Event event) 1766 { 1767 logEvent(__FUNCTION__, event); 1768 switch (event) 1769 { 1770 case Event::psPowerOKAssert: 1771 { 1772 // Cancel any GPIO assertions held during the transition 1773 gpioAssertTimer.cancel(); 1774 psPowerOKWatchdogTimer.cancel(); 1775 if (sioEnabled == true) 1776 { 1777 sioPowerGoodWatchdogTimerStart(); 1778 setPowerState(PowerState::waitForSIOPowerGood); 1779 } 1780 else 1781 { 1782 setPowerState(PowerState::on); 1783 } 1784 break; 1785 } 1786 case Event::psPowerOKWatchdogTimerExpired: 1787 setPowerState(PowerState::off); 1788 psPowerOKFailedLog(); 1789 break; 1790 case Event::sioPowerGoodAssert: 1791 psPowerOKWatchdogTimer.cancel(); 1792 setPowerState(PowerState::on); 1793 break; 1794 default: 1795 lg2::info("No action taken."); 1796 break; 1797 } 1798 } 1799 1800 static void powerStateWaitForSIOPowerGood(const Event event) 1801 { 1802 logEvent(__FUNCTION__, event); 1803 switch (event) 1804 { 1805 case Event::sioPowerGoodAssert: 1806 sioPowerGoodWatchdogTimer.cancel(); 1807 setPowerState(PowerState::on); 1808 break; 1809 case Event::sioPowerGoodWatchdogTimerExpired: 1810 setPowerState(PowerState::off); 1811 systemPowerGoodFailedLog(); 1812 break; 1813 default: 1814 lg2::info("No action taken."); 1815 break; 1816 } 1817 } 1818 1819 static void powerStateOff(const Event event) 1820 { 1821 logEvent(__FUNCTION__, event); 1822 switch (event) 1823 { 1824 case Event::psPowerOKAssert: 1825 { 1826 if (sioEnabled == true) 1827 { 1828 sioPowerGoodWatchdogTimerStart(); 1829 setPowerState(PowerState::waitForSIOPowerGood); 1830 } 1831 else 1832 { 1833 setPowerState(PowerState::on); 1834 } 1835 break; 1836 } 1837 case Event::sioS5DeAssert: 1838 psPowerOKWatchdogTimerStart(); 1839 setPowerState(PowerState::waitForPSPowerOK); 1840 break; 1841 case Event::sioPowerGoodAssert: 1842 setPowerState(PowerState::on); 1843 break; 1844 case Event::powerButtonPressed: 1845 psPowerOKWatchdogTimerStart(); 1846 setPowerState(PowerState::waitForPSPowerOK); 1847 break; 1848 case Event::powerOnRequest: 1849 psPowerOKWatchdogTimerStart(); 1850 setPowerState(PowerState::waitForPSPowerOK); 1851 powerOn(); 1852 break; 1853 default: 1854 lg2::info("No action taken."); 1855 break; 1856 } 1857 } 1858 1859 static void powerStateTransitionToOff(const Event event) 1860 { 1861 logEvent(__FUNCTION__, event); 1862 switch (event) 1863 { 1864 case Event::psPowerOKDeAssert: 1865 // Cancel any GPIO assertions held during the transition 1866 gpioAssertTimer.cancel(); 1867 setPowerState(PowerState::off); 1868 break; 1869 default: 1870 lg2::info("No action taken."); 1871 break; 1872 } 1873 } 1874 1875 static void powerStateGracefulTransitionToOff(const Event event) 1876 { 1877 logEvent(__FUNCTION__, event); 1878 switch (event) 1879 { 1880 case Event::psPowerOKDeAssert: 1881 gracefulPowerOffTimer.cancel(); 1882 setPowerState(PowerState::off); 1883 break; 1884 case Event::gracefulPowerOffTimerExpired: 1885 setPowerState(PowerState::on); 1886 break; 1887 case Event::powerOffRequest: 1888 gracefulPowerOffTimer.cancel(); 1889 setPowerState(PowerState::transitionToOff); 1890 forcePowerOff(); 1891 break; 1892 case Event::powerCycleRequest: 1893 gracefulPowerOffTimer.cancel(); 1894 setPowerState(PowerState::transitionToCycleOff); 1895 forcePowerOff(); 1896 break; 1897 case Event::resetRequest: 1898 gracefulPowerOffTimer.cancel(); 1899 setPowerState(PowerState::on); 1900 reset(); 1901 break; 1902 default: 1903 lg2::info("No action taken."); 1904 break; 1905 } 1906 } 1907 1908 static void powerStateCycleOff(const Event event) 1909 { 1910 logEvent(__FUNCTION__, event); 1911 switch (event) 1912 { 1913 case Event::psPowerOKAssert: 1914 { 1915 powerCycleTimer.cancel(); 1916 if (sioEnabled == true) 1917 { 1918 sioPowerGoodWatchdogTimerStart(); 1919 setPowerState(PowerState::waitForSIOPowerGood); 1920 } 1921 else 1922 { 1923 setPowerState(PowerState::on); 1924 } 1925 break; 1926 } 1927 case Event::sioS5DeAssert: 1928 powerCycleTimer.cancel(); 1929 psPowerOKWatchdogTimerStart(); 1930 setPowerState(PowerState::waitForPSPowerOK); 1931 break; 1932 case Event::powerButtonPressed: 1933 powerCycleTimer.cancel(); 1934 psPowerOKWatchdogTimerStart(); 1935 setPowerState(PowerState::waitForPSPowerOK); 1936 break; 1937 case Event::powerCycleTimerExpired: 1938 psPowerOKWatchdogTimerStart(); 1939 setPowerState(PowerState::waitForPSPowerOK); 1940 powerOn(); 1941 break; 1942 default: 1943 lg2::info("No action taken."); 1944 break; 1945 } 1946 } 1947 1948 static void powerStateTransitionToCycleOff(const Event event) 1949 { 1950 logEvent(__FUNCTION__, event); 1951 switch (event) 1952 { 1953 case Event::psPowerOKDeAssert: 1954 // Cancel any GPIO assertions held during the transition 1955 gpioAssertTimer.cancel(); 1956 setPowerState(PowerState::cycleOff); 1957 powerCycleTimerStart(); 1958 break; 1959 default: 1960 lg2::info("No action taken."); 1961 break; 1962 } 1963 } 1964 1965 static void powerStateGracefulTransitionToCycleOff(const Event event) 1966 { 1967 logEvent(__FUNCTION__, event); 1968 switch (event) 1969 { 1970 case Event::psPowerOKDeAssert: 1971 gracefulPowerOffTimer.cancel(); 1972 setPowerState(PowerState::cycleOff); 1973 powerCycleTimerStart(); 1974 break; 1975 case Event::gracefulPowerOffTimerExpired: 1976 setPowerState(PowerState::on); 1977 break; 1978 case Event::powerOffRequest: 1979 gracefulPowerOffTimer.cancel(); 1980 setPowerState(PowerState::transitionToOff); 1981 forcePowerOff(); 1982 break; 1983 case Event::powerCycleRequest: 1984 gracefulPowerOffTimer.cancel(); 1985 setPowerState(PowerState::transitionToCycleOff); 1986 forcePowerOff(); 1987 break; 1988 case Event::resetRequest: 1989 gracefulPowerOffTimer.cancel(); 1990 setPowerState(PowerState::on); 1991 reset(); 1992 break; 1993 default: 1994 lg2::info("No action taken."); 1995 break; 1996 } 1997 } 1998 1999 static void powerStateCheckForWarmReset(const Event event) 2000 { 2001 logEvent(__FUNCTION__, event); 2002 switch (event) 2003 { 2004 case Event::sioS5Assert: 2005 warmResetCheckTimer.cancel(); 2006 setPowerState(PowerState::transitionToOff); 2007 break; 2008 case Event::warmResetDetected: 2009 setPowerState(PowerState::on); 2010 break; 2011 case Event::psPowerOKDeAssert: 2012 warmResetCheckTimer.cancel(); 2013 setPowerState(PowerState::off); 2014 // DC power is unexpectedly lost, beep 2015 beep(beepPowerFail); 2016 break; 2017 default: 2018 lg2::info("No action taken."); 2019 break; 2020 } 2021 } 2022 2023 static void psPowerOKHandler(bool state) 2024 { 2025 Event powerControlEvent = (state == powerOkConfig.polarity) 2026 ? Event::psPowerOKAssert 2027 : Event::psPowerOKDeAssert; 2028 sendPowerControlEvent(powerControlEvent); 2029 } 2030 2031 static void sioPowerGoodHandler(bool state) 2032 { 2033 Event powerControlEvent = (state == sioPwrGoodConfig.polarity) 2034 ? Event::sioPowerGoodAssert 2035 : Event::sioPowerGoodDeAssert; 2036 sendPowerControlEvent(powerControlEvent); 2037 } 2038 2039 static void sioOnControlHandler(bool state) 2040 { 2041 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE", 2042 static_cast<int>(state)); 2043 } 2044 2045 static void sioS5Handler(bool state) 2046 { 2047 Event powerControlEvent = (state == sioS5Config.polarity) 2048 ? Event::sioS5Assert 2049 : Event::sioS5DeAssert; 2050 sendPowerControlEvent(powerControlEvent); 2051 } 2052 2053 static void powerButtonHandler(bool state) 2054 { 2055 bool asserted = state == powerButtonConfig.polarity; 2056 powerButtonIface->set_property("ButtonPressed", asserted); 2057 if (asserted) 2058 { 2059 powerButtonPressLog(); 2060 if (!powerButtonMask) 2061 { 2062 sendPowerControlEvent(Event::powerButtonPressed); 2063 addRestartCause(RestartCause::powerButton); 2064 } 2065 else 2066 { 2067 lg2::info("power button press masked"); 2068 } 2069 } 2070 } 2071 2072 static void resetButtonHandler(bool state) 2073 { 2074 bool asserted = state == resetButtonConfig.polarity; 2075 resetButtonIface->set_property("ButtonPressed", asserted); 2076 if (asserted) 2077 { 2078 resetButtonPressLog(); 2079 if (!resetButtonMask) 2080 { 2081 sendPowerControlEvent(Event::resetButtonPressed); 2082 addRestartCause(RestartCause::resetButton); 2083 } 2084 else 2085 { 2086 lg2::info("reset button press masked"); 2087 } 2088 } 2089 } 2090 2091 #ifdef CHASSIS_SYSTEM_RESET 2092 static constexpr auto systemdBusname = "org.freedesktop.systemd1"; 2093 static constexpr auto systemdPath = "/org/freedesktop/systemd1"; 2094 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager"; 2095 static constexpr auto systemTargetName = "chassis-system-reset.target"; 2096 2097 void systemReset() 2098 { 2099 conn->async_method_call( 2100 [](boost::system::error_code ec) { 2101 if (ec) 2102 { 2103 lg2::error("Failed to call chassis system reset: {ERR}", "ERR", 2104 ec.message()); 2105 } 2106 }, 2107 systemdBusname, systemdPath, systemdInterface, "StartUnit", 2108 systemTargetName, "replace"); 2109 } 2110 #endif 2111 2112 static void nmiSetEnableProperty(bool value) 2113 { 2114 conn->async_method_call( 2115 [](boost::system::error_code ec) { 2116 if (ec) 2117 { 2118 lg2::error("failed to set NMI source"); 2119 } 2120 }, 2121 "xyz.openbmc_project.Settings", 2122 "/xyz/openbmc_project/Chassis/Control/NMISource", 2123 "org.freedesktop.DBus.Properties", "Set", 2124 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled", 2125 std::variant<bool>{value}); 2126 } 2127 2128 static void nmiReset(void) 2129 { 2130 const static constexpr int nmiOutPulseTimeMs = 200; 2131 2132 lg2::info("NMI out action"); 2133 nmiOutLine.set_value(nmiOutConfig.polarity); 2134 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", 2135 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity); 2136 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs)); 2137 gpioAssertTimer.async_wait([](const boost::system::error_code ec) { 2138 // restore the NMI_OUT GPIO line back to the opposite value 2139 nmiOutLine.set_value(!nmiOutConfig.polarity); 2140 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName); 2141 if (ec) 2142 { 2143 // operation_aborted is expected if timer is canceled before 2144 // completion. 2145 if (ec != boost::asio::error::operation_aborted) 2146 { 2147 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 2148 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG", 2149 ec.message()); 2150 } 2151 } 2152 }); 2153 // log to redfish 2154 nmiDiagIntLog(); 2155 lg2::info("NMI out action completed"); 2156 // reset Enable Property 2157 nmiSetEnableProperty(false); 2158 } 2159 2160 static void nmiSourcePropertyMonitor(void) 2161 { 2162 lg2::info("NMI Source Property Monitor"); 2163 2164 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch = 2165 std::make_unique<sdbusplus::bus::match_t>( 2166 *conn, 2167 "type='signal',interface='org.freedesktop.DBus.Properties'," 2168 "member='PropertiesChanged'," 2169 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'", 2170 [](sdbusplus::message_t& msg) { 2171 std::string interfaceName; 2172 boost::container::flat_map<std::string, 2173 std::variant<bool, std::string>> 2174 propertiesChanged; 2175 std::string state; 2176 bool value = true; 2177 try 2178 { 2179 msg.read(interfaceName, propertiesChanged); 2180 if (propertiesChanged.begin()->first == "Enabled") 2181 { 2182 value = 2183 std::get<bool>(propertiesChanged.begin()->second); 2184 lg2::info( 2185 "NMI Enabled propertiesChanged value: {VALUE}", 2186 "VALUE", value); 2187 nmiEnabled = value; 2188 if (nmiEnabled) 2189 { 2190 nmiReset(); 2191 } 2192 } 2193 } 2194 catch (const std::exception& e) 2195 { 2196 lg2::error("Unable to read NMI source: {ERROR}", "ERROR", 2197 e); 2198 return; 2199 } 2200 }); 2201 } 2202 2203 static void setNmiSource() 2204 { 2205 conn->async_method_call( 2206 [](boost::system::error_code ec) { 2207 if (ec) 2208 { 2209 lg2::error("failed to set NMI source"); 2210 } 2211 }, 2212 "xyz.openbmc_project.Settings", 2213 "/xyz/openbmc_project/Chassis/Control/NMISource", 2214 "org.freedesktop.DBus.Properties", "Set", 2215 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource", 2216 std::variant<std::string>{ 2217 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"}); 2218 // set Enable Property 2219 nmiSetEnableProperty(true); 2220 } 2221 2222 static void nmiButtonHandler(bool state) 2223 { 2224 // Don't handle event if host not running and config doesn't force it 2225 if (!nmiWhenPoweredOff && 2226 getHostState(powerState) != 2227 "xyz.openbmc_project.State.Host.HostState.Running") 2228 { 2229 return; 2230 } 2231 2232 bool asserted = state == nmiButtonConfig.polarity; 2233 nmiButtonIface->set_property("ButtonPressed", asserted); 2234 if (asserted) 2235 { 2236 nmiButtonPressLog(); 2237 if (nmiButtonMasked) 2238 { 2239 lg2::info("NMI button press masked"); 2240 } 2241 else 2242 { 2243 setNmiSource(); 2244 } 2245 } 2246 } 2247 2248 static void idButtonHandler(bool state) 2249 { 2250 bool asserted = state == idButtonConfig.polarity; 2251 idButtonIface->set_property("ButtonPressed", asserted); 2252 } 2253 2254 static void pltRstHandler(bool pltRst) 2255 { 2256 if (pltRst) 2257 { 2258 sendPowerControlEvent(Event::pltRstDeAssert); 2259 } 2260 else 2261 { 2262 sendPowerControlEvent(Event::pltRstAssert); 2263 } 2264 } 2265 2266 [[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg) 2267 { 2268 std::string interfaceName; 2269 boost::container::flat_map<std::string, std::variant<bool>> 2270 propertiesChanged; 2271 try 2272 { 2273 msg.read(interfaceName, propertiesChanged); 2274 } 2275 catch (const std::exception& e) 2276 { 2277 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e); 2278 return; 2279 } 2280 if (propertiesChanged.empty()) 2281 { 2282 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received"); 2283 return; 2284 } 2285 2286 for (auto& [property, value] : propertiesChanged) 2287 { 2288 if (property == "ESpiPlatformReset") 2289 { 2290 bool* pltRst = std::get_if<bool>(&value); 2291 if (pltRst == nullptr) 2292 { 2293 lg2::error("{PROPERTY} property invalid", "PROPERTY", property); 2294 return; 2295 } 2296 pltRstHandler(*pltRst); 2297 } 2298 } 2299 } 2300 2301 static void postCompleteHandler(bool state) 2302 { 2303 bool asserted = state == postCompleteConfig.polarity; 2304 if (asserted) 2305 { 2306 sendPowerControlEvent(Event::postCompleteAssert); 2307 setOperatingSystemState(OperatingSystemStateStage::Standby); 2308 } 2309 else 2310 { 2311 sendPowerControlEvent(Event::postCompleteDeAssert); 2312 setOperatingSystemState(OperatingSystemStateStage::Inactive); 2313 } 2314 } 2315 2316 static int loadConfigValues() 2317 { 2318 const std::string configFilePath = 2319 "/usr/share/x86-power-control/power-config-host" + power_control::node + 2320 ".json"; 2321 std::ifstream configFile(configFilePath.c_str()); 2322 if (!configFile.is_open()) 2323 { 2324 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'", 2325 "PATH", configFilePath); 2326 return -1; 2327 } 2328 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true); 2329 2330 if (jsonData.is_discarded()) 2331 { 2332 lg2::error("Power config readings JSON parser failure"); 2333 return -1; 2334 } 2335 auto gpios = jsonData["gpio_configs"]; 2336 auto timers = jsonData["timing_configs"]; 2337 2338 ConfigData* tempGpioData; 2339 2340 for (nlohmann::json& gpioConfig : gpios) 2341 { 2342 if (!gpioConfig.contains("Name")) 2343 { 2344 lg2::error("The 'Name' field must be defined in Json file"); 2345 return -1; 2346 } 2347 2348 // Iterate through the powersignal map to check if the gpio json config 2349 // entry is valid 2350 std::string gpioName = gpioConfig["Name"]; 2351 auto signalMapIter = powerSignalMap.find(gpioName); 2352 if (signalMapIter == powerSignalMap.end()) 2353 { 2354 lg2::error( 2355 "{GPIO_NAME} is not a recognized power-control signal name", 2356 "GPIO_NAME", gpioName); 2357 return -1; 2358 } 2359 2360 // assign the power signal name to the corresponding structure reference 2361 // from map then fillup the structure with coressponding json config 2362 // value 2363 tempGpioData = signalMapIter->second; 2364 tempGpioData->name = gpioName; 2365 2366 if (!gpioConfig.contains("Type")) 2367 { 2368 lg2::error("The \'Type\' field must be defined in Json file"); 2369 return -1; 2370 } 2371 2372 std::string signalType = gpioConfig["Type"]; 2373 if (signalType == "GPIO") 2374 { 2375 tempGpioData->type = ConfigType::GPIO; 2376 } 2377 else if (signalType == "DBUS") 2378 { 2379 tempGpioData->type = ConfigType::DBUS; 2380 } 2381 else 2382 { 2383 lg2::error("{TYPE} is not a recognized power-control signal type", 2384 "TYPE", signalType); 2385 return -1; 2386 } 2387 2388 if (tempGpioData->type == ConfigType::GPIO) 2389 { 2390 if (gpioConfig.contains("LineName")) 2391 { 2392 tempGpioData->lineName = gpioConfig["LineName"]; 2393 } 2394 else 2395 { 2396 lg2::error( 2397 "The \'LineName\' field must be defined for GPIO configuration"); 2398 return -1; 2399 } 2400 if (gpioConfig.contains("Polarity")) 2401 { 2402 std::string polarity = gpioConfig["Polarity"]; 2403 if (polarity == "ActiveLow") 2404 { 2405 tempGpioData->polarity = false; 2406 } 2407 else if (polarity == "ActiveHigh") 2408 { 2409 tempGpioData->polarity = true; 2410 } 2411 else 2412 { 2413 lg2::error( 2414 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}", 2415 "POLARITY", polarity); 2416 return -1; 2417 } 2418 } 2419 else 2420 { 2421 lg2::error("Polarity field not found for {GPIO_NAME}", 2422 "GPIO_NAME", tempGpioData->lineName); 2423 return -1; 2424 } 2425 } 2426 else 2427 { 2428 // if dbus based gpio config is defined read and update the dbus 2429 // params corresponding to the gpio config instance 2430 for (auto& [key, dbusParamName] : dbusParams) 2431 { 2432 if (!gpioConfig.contains(dbusParamName)) 2433 { 2434 lg2::error( 2435 "The {DBUS_NAME} field must be defined for Dbus configuration ", 2436 "DBUS_NAME", dbusParamName); 2437 return -1; 2438 } 2439 } 2440 tempGpioData->dbusName = 2441 gpioConfig[dbusParams[DbusConfigType::name]]; 2442 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]]; 2443 tempGpioData->interface = 2444 gpioConfig[dbusParams[DbusConfigType::interface]]; 2445 tempGpioData->lineName = 2446 gpioConfig[dbusParams[DbusConfigType::property]]; 2447 2448 // dbus-based inputs must be active-high. 2449 tempGpioData->polarity = true; 2450 2451 // MatchRegex is optional 2452 auto item = gpioConfig.find("MatchRegex"); 2453 if (item != gpioConfig.end()) 2454 { 2455 try 2456 { 2457 tempGpioData->matchRegex = std::regex(*item); 2458 } 2459 catch (const std::regex_error& e) 2460 { 2461 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME", 2462 gpioName, "ERR", e.what()); 2463 return -1; 2464 } 2465 } 2466 } 2467 } 2468 2469 // read and store the timer values from json config to Timer Map 2470 for (auto& [key, timerValue] : TimerMap) 2471 { 2472 if (timers.contains(key.c_str())) 2473 { 2474 timerValue = timers[key.c_str()]; 2475 } 2476 } 2477 2478 // If "events_configs" key is not in json config, fallback to null 2479 auto events = jsonData.value("event_configs", 2480 nlohmann::json(nlohmann::json::value_t::null)); 2481 if (events.is_object()) 2482 { 2483 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true); 2484 } 2485 2486 return 0; 2487 } 2488 2489 template <typename T> 2490 static std::optional<T> 2491 getMessageValue(sdbusplus::message_t& msg, const std::string& name) 2492 { 2493 std::string event; 2494 std::string thresholdInterface; 2495 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged; 2496 2497 msg.read(thresholdInterface, propertiesChanged); 2498 if (propertiesChanged.empty()) 2499 { 2500 return std::nullopt; 2501 } 2502 2503 event = propertiesChanged.begin()->first; 2504 if (event.empty() || event != name) 2505 { 2506 return std::nullopt; 2507 } 2508 2509 return std::get<T>(propertiesChanged.begin()->second); 2510 } 2511 2512 static bool getDbusMsgGPIOState(sdbusplus::message_t& msg, 2513 const ConfigData& config, bool& value) 2514 { 2515 try 2516 { 2517 if (config.matchRegex.has_value()) 2518 { 2519 std::optional<std::string> s = 2520 getMessageValue<std::string>(msg, config.lineName); 2521 if (!s.has_value()) 2522 { 2523 return false; 2524 } 2525 2526 std::smatch m; 2527 value = std::regex_match(s.value(), m, config.matchRegex.value()); 2528 } 2529 else 2530 { 2531 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName); 2532 if (!v.has_value()) 2533 { 2534 return false; 2535 } 2536 value = v.value(); 2537 } 2538 return true; 2539 } 2540 catch (const std::exception& e) 2541 { 2542 lg2::error( 2543 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}", 2544 "DBUS_NAME", config.lineName, "ERROR", e); 2545 return false; 2546 } 2547 } 2548 2549 static sdbusplus::bus::match_t 2550 dbusGPIOMatcher(const ConfigData& cfg, std::function<void(bool)> onMatch) 2551 { 2552 auto pulseEventMatcherCallback = 2553 [&cfg, onMatch](sdbusplus::message_t& msg) { 2554 bool value = false; 2555 if (!getDbusMsgGPIOState(msg, cfg, value)) 2556 { 2557 return; 2558 } 2559 onMatch(value); 2560 }; 2561 2562 return sdbusplus::bus::match_t( 2563 static_cast<sdbusplus::bus_t&>(*conn), 2564 "type='signal',interface='org.freedesktop.DBus.Properties',member='" 2565 "PropertiesChanged',arg0='" + 2566 cfg.interface + "',path='" + cfg.path + "',sender='" + 2567 cfg.dbusName + "'", 2568 std::move(pulseEventMatcherCallback)); 2569 } 2570 2571 // D-Bus property read functions 2572 void reschedulePropertyRead(const ConfigData& configData); 2573 2574 int getProperty(const ConfigData& configData) 2575 { 2576 std::variant<bool> resp; 2577 2578 try 2579 { 2580 auto method = conn->new_method_call( 2581 configData.dbusName.c_str(), configData.path.c_str(), 2582 "org.freedesktop.DBus.Properties", "Get"); 2583 method.append(configData.interface.c_str(), 2584 configData.lineName.c_str()); 2585 2586 auto reply = conn->call(method); 2587 if (reply.is_method_error()) 2588 { 2589 lg2::error( 2590 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}", 2591 "PROPERTY", configData.lineName, "INTERFACE", 2592 configData.interface, "PATH", configData.path); 2593 return -1; 2594 } 2595 2596 reply.read(resp); 2597 } 2598 catch (const sdbusplus::exception_t& e) 2599 { 2600 lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY", 2601 configData.lineName, "WHAT", e.what()); 2602 reschedulePropertyRead(configData); 2603 return -1; 2604 } 2605 2606 auto respValue = std::get_if<bool>(&resp); 2607 if (!respValue) 2608 { 2609 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type", 2610 "PROPERTY", configData.lineName); 2611 return -1; 2612 } 2613 return (*respValue); 2614 } 2615 2616 void setInitialValue(const ConfigData& configData, bool initialValue) 2617 { 2618 if (configData.name == "PowerOk") 2619 { 2620 powerState = (initialValue ? PowerState::on : PowerState::off); 2621 hostIface->set_property("CurrentHostState", 2622 std::string(getHostState(powerState))); 2623 } 2624 else if (configData.name == "PowerButton") 2625 { 2626 powerButtonIface->set_property("ButtonPressed", !initialValue); 2627 } 2628 else if (configData.name == "ResetButton") 2629 { 2630 resetButtonIface->set_property("ButtonPressed", !initialValue); 2631 } 2632 else if (configData.name == "NMIButton") 2633 { 2634 nmiButtonIface->set_property("ButtonPressed", !initialValue); 2635 } 2636 else if (configData.name == "IdButton") 2637 { 2638 idButtonIface->set_property("ButtonPressed", !initialValue); 2639 } 2640 else if (configData.name == "PostComplete") 2641 { 2642 OperatingSystemStateStage osState = 2643 (initialValue == postCompleteConfig.polarity 2644 ? OperatingSystemStateStage::Standby 2645 : OperatingSystemStateStage::Inactive); 2646 setOperatingSystemState(osState); 2647 } 2648 else 2649 { 2650 lg2::error("Unknown name {NAME}", "NAME", configData.name); 2651 } 2652 } 2653 2654 void reschedulePropertyRead(const ConfigData& configData) 2655 { 2656 auto item = dBusRetryTimers.find(configData.name); 2657 2658 if (item == dBusRetryTimers.end()) 2659 { 2660 auto newItem = dBusRetryTimers.insert( 2661 {configData.name, boost::asio::steady_timer(io)}); 2662 2663 if (!newItem.second) 2664 { 2665 lg2::error("Failed to add new timer for {NAME}", "NAME", 2666 configData.name); 2667 return; 2668 } 2669 2670 item = newItem.first; 2671 } 2672 2673 auto& timer = item->second; 2674 timer.expires_after( 2675 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"])); 2676 timer.async_wait([&configData](const boost::system::error_code ec) { 2677 if (ec) 2678 { 2679 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME", 2680 configData.name, "MSG", ec.message()); 2681 dBusRetryTimers.erase(configData.name); 2682 return; 2683 } 2684 2685 int property = getProperty(configData); 2686 2687 if (property >= 0) 2688 { 2689 setInitialValue(configData, (property > 0)); 2690 dBusRetryTimers.erase(configData.name); 2691 } 2692 }); 2693 } 2694 } // namespace power_control 2695 2696 int main(int argc, char* argv[]) 2697 { 2698 using namespace power_control; 2699 2700 if (argc > 1) 2701 { 2702 node = argv[1]; 2703 } 2704 lg2::info("Start Chassis power control service for host : {NODE}", "NODE", 2705 node); 2706 2707 conn = std::make_shared<sdbusplus::asio::connection>(io); 2708 2709 // Load GPIO's through json config file 2710 if (loadConfigValues() == -1) 2711 { 2712 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node); 2713 } 2714 /* Currently for single host based systems additional busname is added 2715 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0. 2716 Going forward for single hosts the old bus name without zero numbering 2717 will be removed when all other applications adapted to the 2718 bus name with zero numbering (xyz.openbmc_project.State.Host0). */ 2719 2720 if (node == "0") 2721 { 2722 // Request all the dbus names 2723 conn->request_name(hostDbusName.c_str()); 2724 conn->request_name(chassisDbusName.c_str()); 2725 conn->request_name(osDbusName.c_str()); 2726 conn->request_name(buttonDbusName.c_str()); 2727 conn->request_name(nmiDbusName.c_str()); 2728 conn->request_name(rstCauseDbusName.c_str()); 2729 } 2730 2731 hostDbusName += node; 2732 chassisDbusName += node; 2733 osDbusName += node; 2734 buttonDbusName += node; 2735 nmiDbusName += node; 2736 rstCauseDbusName += node; 2737 2738 // Request all the dbus names 2739 conn->request_name(hostDbusName.c_str()); 2740 conn->request_name(chassisDbusName.c_str()); 2741 conn->request_name(osDbusName.c_str()); 2742 conn->request_name(buttonDbusName.c_str()); 2743 conn->request_name(nmiDbusName.c_str()); 2744 conn->request_name(rstCauseDbusName.c_str()); 2745 2746 if (sioPwrGoodConfig.lineName.empty() || 2747 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty()) 2748 { 2749 sioEnabled = false; 2750 lg2::info("SIO control GPIOs not defined, disable SIO support."); 2751 } 2752 2753 // Request PS_PWROK GPIO events 2754 if (powerOkConfig.type == ConfigType::GPIO) 2755 { 2756 if (!requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler, 2757 psPowerOKLine, psPowerOKEvent)) 2758 { 2759 return -1; 2760 } 2761 } 2762 else if (powerOkConfig.type == ConfigType::DBUS) 2763 { 2764 static sdbusplus::bus::match_t powerOkEventMonitor = 2765 power_control::dbusGPIOMatcher(powerOkConfig, psPowerOKHandler); 2766 } 2767 else 2768 { 2769 lg2::error("PowerOk name should be configured from json config file"); 2770 return -1; 2771 } 2772 2773 if (sioEnabled == true) 2774 { 2775 // Request SIO_POWER_GOOD GPIO events 2776 if (sioPwrGoodConfig.type == ConfigType::GPIO) 2777 { 2778 if (!requestGPIOEvents(sioPwrGoodConfig.lineName, 2779 sioPowerGoodHandler, sioPowerGoodLine, 2780 sioPowerGoodEvent)) 2781 { 2782 return -1; 2783 } 2784 } 2785 else if (sioPwrGoodConfig.type == ConfigType::DBUS) 2786 { 2787 static sdbusplus::bus::match_t sioPwrGoodEventMonitor = 2788 power_control::dbusGPIOMatcher(sioPwrGoodConfig, 2789 sioPowerGoodHandler); 2790 } 2791 else 2792 { 2793 lg2::error( 2794 "sioPwrGood name should be configured from json config file"); 2795 return -1; 2796 } 2797 2798 // Request SIO_ONCONTROL GPIO events 2799 if (sioOnControlConfig.type == ConfigType::GPIO) 2800 { 2801 if (!requestGPIOEvents(sioOnControlConfig.lineName, 2802 sioOnControlHandler, sioOnControlLine, 2803 sioOnControlEvent)) 2804 { 2805 return -1; 2806 } 2807 } 2808 else if (sioOnControlConfig.type == ConfigType::DBUS) 2809 { 2810 static sdbusplus::bus::match_t sioOnControlEventMonitor = 2811 power_control::dbusGPIOMatcher(sioOnControlConfig, 2812 sioOnControlHandler); 2813 } 2814 else 2815 { 2816 lg2::error( 2817 "sioOnControl name should be configured from jsonconfig file\n"); 2818 return -1; 2819 } 2820 2821 // Request SIO_S5 GPIO events 2822 if (sioS5Config.type == ConfigType::GPIO) 2823 { 2824 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler, 2825 sioS5Line, sioS5Event)) 2826 { 2827 return -1; 2828 } 2829 } 2830 else if (sioS5Config.type == ConfigType::DBUS) 2831 { 2832 static sdbusplus::bus::match_t sioS5EventMonitor = 2833 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler); 2834 } 2835 else 2836 { 2837 lg2::error("sioS5 name should be configured from json config file"); 2838 return -1; 2839 } 2840 } 2841 2842 // Request POWER_BUTTON GPIO events 2843 if (powerButtonConfig.type == ConfigType::GPIO) 2844 { 2845 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler, 2846 powerButtonLine, powerButtonEvent)) 2847 { 2848 return -1; 2849 } 2850 } 2851 else if (powerButtonConfig.type == ConfigType::DBUS) 2852 { 2853 static sdbusplus::bus::match_t powerButtonEventMonitor = 2854 power_control::dbusGPIOMatcher(powerButtonConfig, 2855 powerButtonHandler); 2856 } 2857 2858 // Request RESET_BUTTON GPIO events 2859 if (resetButtonConfig.type == ConfigType::GPIO) 2860 { 2861 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler, 2862 resetButtonLine, resetButtonEvent)) 2863 { 2864 return -1; 2865 } 2866 } 2867 else if (resetButtonConfig.type == ConfigType::DBUS) 2868 { 2869 static sdbusplus::bus::match_t resetButtonEventMonitor = 2870 power_control::dbusGPIOMatcher(resetButtonConfig, 2871 resetButtonHandler); 2872 } 2873 2874 // Request NMI_BUTTON GPIO events 2875 if (nmiButtonConfig.type == ConfigType::GPIO) 2876 { 2877 if (!nmiButtonConfig.lineName.empty()) 2878 { 2879 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler, 2880 nmiButtonLine, nmiButtonEvent); 2881 } 2882 } 2883 else if (nmiButtonConfig.type == ConfigType::DBUS) 2884 { 2885 static sdbusplus::bus::match_t nmiButtonEventMonitor = 2886 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler); 2887 } 2888 2889 // Request ID_BUTTON GPIO events 2890 if (idButtonConfig.type == ConfigType::GPIO) 2891 { 2892 if (!idButtonConfig.lineName.empty()) 2893 { 2894 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler, 2895 idButtonLine, idButtonEvent); 2896 } 2897 } 2898 else if (idButtonConfig.type == ConfigType::DBUS) 2899 { 2900 static sdbusplus::bus::match_t idButtonEventMonitor = 2901 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler); 2902 } 2903 2904 #ifdef USE_PLT_RST 2905 sdbusplus::bus::match_t pltRstMatch( 2906 *conn, 2907 "type='signal',interface='org.freedesktop.DBus.Properties',member='" 2908 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'", 2909 hostMiscHandler); 2910 #endif 2911 2912 // Request POST_COMPLETE GPIO events 2913 if (postCompleteConfig.type == ConfigType::GPIO) 2914 { 2915 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler, 2916 postCompleteLine, postCompleteEvent)) 2917 { 2918 return -1; 2919 } 2920 } 2921 else if (postCompleteConfig.type == ConfigType::DBUS) 2922 { 2923 static sdbusplus::bus::match_t postCompleteEventMonitor = 2924 power_control::dbusGPIOMatcher(postCompleteConfig, 2925 postCompleteHandler); 2926 } 2927 else 2928 { 2929 lg2::error( 2930 "postComplete name should be configured from json config file"); 2931 return -1; 2932 } 2933 2934 // initialize NMI_OUT GPIO. 2935 if (!nmiOutConfig.lineName.empty()) 2936 { 2937 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity, 2938 nmiOutLine); 2939 } 2940 2941 // Initialize POWER_OUT and RESET_OUT GPIO. 2942 gpiod::line line; 2943 if (!powerOutConfig.lineName.empty()) 2944 { 2945 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity, 2946 line)) 2947 { 2948 return -1; 2949 } 2950 } 2951 else 2952 { 2953 lg2::error("powerOut name should be configured from json config file"); 2954 return -1; 2955 } 2956 2957 if (!resetOutConfig.lineName.empty()) 2958 { 2959 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity, 2960 line)) 2961 { 2962 return -1; 2963 } 2964 } 2965 else 2966 { 2967 lg2::error("ResetOut name should be configured from json config file"); 2968 return -1; 2969 } 2970 // Release line 2971 line.reset(); 2972 2973 // Initialize the power state and operating system state 2974 powerState = PowerState::off; 2975 operatingSystemState = OperatingSystemStateStage::Inactive; 2976 // Check power good 2977 2978 if (powerOkConfig.type == ConfigType::GPIO) 2979 { 2980 if (psPowerOKLine.get_value() > 0 || 2981 (sioEnabled && 2982 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity))) 2983 { 2984 powerState = PowerState::on; 2985 } 2986 } 2987 else 2988 { 2989 if (getProperty(powerOkConfig)) 2990 { 2991 powerState = PowerState::on; 2992 } 2993 } 2994 // Check if we need to start the Power Restore policy 2995 if (powerState != PowerState::on) 2996 { 2997 powerRestore.run(); 2998 } 2999 3000 if (nmiOutLine) 3001 nmiSourcePropertyMonitor(); 3002 3003 lg2::info("Initializing power state."); 3004 logStateTransition(powerState); 3005 3006 // Power Control Service 3007 sdbusplus::asio::object_server hostServer = 3008 sdbusplus::asio::object_server(conn); 3009 3010 // Power Control Interface 3011 hostIface = 3012 hostServer.add_interface("/xyz/openbmc_project/state/host" + node, 3013 "xyz.openbmc_project.State.Host"); 3014 // Interface for IPMI/Redfish initiated host state transitions 3015 hostIface->register_property( 3016 "RequestedHostTransition", 3017 std::string("xyz.openbmc_project.State.Host.Transition.Off"), 3018 [](const std::string& requested, std::string& resp) { 3019 if (requested == "xyz.openbmc_project.State.Host.Transition.Off") 3020 { 3021 // if power button is masked, ignore this 3022 if (!powerButtonMask) 3023 { 3024 sendPowerControlEvent(Event::gracefulPowerOffRequest); 3025 addRestartCause(RestartCause::command); 3026 } 3027 else 3028 { 3029 lg2::info("Power Button Masked."); 3030 throw std::invalid_argument("Transition Request Masked"); 3031 return 0; 3032 } 3033 } 3034 else if (requested == 3035 "xyz.openbmc_project.State.Host.Transition.On") 3036 { 3037 // if power button is masked, ignore this 3038 if (!powerButtonMask) 3039 { 3040 sendPowerControlEvent(Event::powerOnRequest); 3041 addRestartCause(RestartCause::command); 3042 } 3043 else 3044 { 3045 lg2::info("Power Button Masked."); 3046 throw std::invalid_argument("Transition Request Masked"); 3047 return 0; 3048 } 3049 } 3050 else if (requested == 3051 "xyz.openbmc_project.State.Host.Transition.Reboot") 3052 { 3053 // if power button is masked, ignore this 3054 if (!powerButtonMask) 3055 { 3056 sendPowerControlEvent(Event::powerCycleRequest); 3057 addRestartCause(RestartCause::command); 3058 } 3059 else 3060 { 3061 lg2::info("Power Button Masked."); 3062 throw std::invalid_argument("Transition Request Masked"); 3063 return 0; 3064 } 3065 } 3066 else if ( 3067 requested == 3068 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot") 3069 { 3070 // if reset button is masked, ignore this 3071 if (!resetButtonMask) 3072 { 3073 sendPowerControlEvent(Event::gracefulPowerCycleRequest); 3074 addRestartCause(RestartCause::command); 3075 } 3076 else 3077 { 3078 lg2::info("Reset Button Masked."); 3079 throw std::invalid_argument("Transition Request Masked"); 3080 return 0; 3081 } 3082 } 3083 else if ( 3084 requested == 3085 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot") 3086 { 3087 // if reset button is masked, ignore this 3088 if (!resetButtonMask) 3089 { 3090 sendPowerControlEvent(Event::resetRequest); 3091 addRestartCause(RestartCause::command); 3092 } 3093 else 3094 { 3095 lg2::info("Reset Button Masked."); 3096 throw std::invalid_argument("Transition Request Masked"); 3097 return 0; 3098 } 3099 } 3100 else 3101 { 3102 lg2::error("Unrecognized host state transition request."); 3103 throw std::invalid_argument("Unrecognized Transition Request"); 3104 return 0; 3105 } 3106 resp = requested; 3107 return 1; 3108 }); 3109 hostIface->register_property("CurrentHostState", 3110 std::string(getHostState(powerState))); 3111 3112 hostIface->initialize(); 3113 3114 // Chassis Control Service 3115 sdbusplus::asio::object_server chassisServer = 3116 sdbusplus::asio::object_server(conn); 3117 3118 // Chassis Control Interface 3119 chassisIface = 3120 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node, 3121 "xyz.openbmc_project.State.Chassis"); 3122 3123 chassisIface->register_property( 3124 "RequestedPowerTransition", 3125 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"), 3126 [](const std::string& requested, std::string& resp) { 3127 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off") 3128 { 3129 // if power button is masked, ignore this 3130 if (!powerButtonMask) 3131 { 3132 sendPowerControlEvent(Event::powerOffRequest); 3133 addRestartCause(RestartCause::command); 3134 } 3135 else 3136 { 3137 lg2::info("Power Button Masked."); 3138 throw std::invalid_argument("Transition Request Masked"); 3139 return 0; 3140 } 3141 } 3142 else if (requested == 3143 "xyz.openbmc_project.State.Chassis.Transition.On") 3144 { 3145 // if power button is masked, ignore this 3146 if (!powerButtonMask) 3147 { 3148 sendPowerControlEvent(Event::powerOnRequest); 3149 addRestartCause(RestartCause::command); 3150 } 3151 else 3152 { 3153 lg2::info("Power Button Masked."); 3154 throw std::invalid_argument("Transition Request Masked"); 3155 return 0; 3156 } 3157 } 3158 else if (requested == 3159 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 3160 { 3161 // if power button is masked, ignore this 3162 if (!powerButtonMask) 3163 { 3164 sendPowerControlEvent(Event::powerCycleRequest); 3165 addRestartCause(RestartCause::command); 3166 } 3167 else 3168 { 3169 lg2::info("Power Button Masked."); 3170 throw std::invalid_argument("Transition Request Masked"); 3171 return 0; 3172 } 3173 } 3174 else 3175 { 3176 lg2::error("Unrecognized chassis state transition request."); 3177 throw std::invalid_argument("Unrecognized Transition Request"); 3178 return 0; 3179 } 3180 resp = requested; 3181 return 1; 3182 }); 3183 chassisIface->register_property("CurrentPowerState", 3184 std::string(getChassisState(powerState))); 3185 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs()); 3186 3187 chassisIface->initialize(); 3188 3189 #ifdef CHASSIS_SYSTEM_RESET 3190 // Chassis System Service 3191 sdbusplus::asio::object_server chassisSysServer = 3192 sdbusplus::asio::object_server(conn); 3193 3194 // Chassis System Interface 3195 chassisSysIface = chassisSysServer.add_interface( 3196 "/xyz/openbmc_project/state/chassis_system0", 3197 "xyz.openbmc_project.State.Chassis"); 3198 3199 chassisSysIface->register_property( 3200 "RequestedPowerTransition", 3201 std::string("xyz.openbmc_project.State.Chassis.Transition.On"), 3202 [](const std::string& requested, std::string& resp) { 3203 if (requested == 3204 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 3205 { 3206 systemReset(); 3207 addRestartCause(RestartCause::command); 3208 } 3209 else 3210 { 3211 lg2::error( 3212 "Unrecognized chassis system state transition request."); 3213 throw std::invalid_argument("Unrecognized Transition Request"); 3214 return 0; 3215 } 3216 resp = requested; 3217 return 1; 3218 }); 3219 chassisSysIface->register_property( 3220 "CurrentPowerState", std::string(getChassisState(powerState))); 3221 chassisSysIface->register_property("LastStateChangeTime", 3222 getCurrentTimeMs()); 3223 3224 chassisSysIface->initialize(); 3225 3226 if (!slotPowerConfig.lineName.empty()) 3227 { 3228 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine)) 3229 { 3230 return -1; 3231 } 3232 3233 slotPowerState = SlotPowerState::off; 3234 if (slotPowerLine.get_value() > 0) 3235 { 3236 slotPowerState = SlotPowerState::on; 3237 } 3238 3239 chassisSlotIface = chassisSysServer.add_interface( 3240 "/xyz/openbmc_project/state/chassis_system" + node, 3241 "xyz.openbmc_project.State.Chassis"); 3242 chassisSlotIface->register_property( 3243 "RequestedPowerTransition", 3244 std::string("xyz.openbmc_project.State.Chassis.Transition.On"), 3245 [](const std::string& requested, std::string& resp) { 3246 if (requested == 3247 "xyz.openbmc_project.State.Chassis.Transition.On") 3248 { 3249 slotPowerOn(); 3250 } 3251 else if (requested == 3252 "xyz.openbmc_project.State.Chassis.Transition.Off") 3253 { 3254 slotPowerOff(); 3255 } 3256 else if ( 3257 requested == 3258 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 3259 { 3260 slotPowerCycle(); 3261 } 3262 else 3263 { 3264 lg2::error( 3265 "Unrecognized chassis system state transition request.\n"); 3266 throw std::invalid_argument( 3267 "Unrecognized Transition Request"); 3268 return 0; 3269 } 3270 resp = requested; 3271 return 1; 3272 }); 3273 chassisSlotIface->register_property( 3274 "CurrentPowerState", std::string(getSlotState(slotPowerState))); 3275 chassisSlotIface->register_property("LastStateChangeTime", 3276 getCurrentTimeMs()); 3277 chassisSlotIface->initialize(); 3278 } 3279 #endif 3280 // Buttons Service 3281 sdbusplus::asio::object_server buttonsServer = 3282 sdbusplus::asio::object_server(conn); 3283 3284 if (!powerButtonConfig.lineName.empty()) 3285 { 3286 // Power Button Interface 3287 power_control::powerButtonIface = buttonsServer.add_interface( 3288 "/xyz/openbmc_project/chassis/buttons/power", 3289 "xyz.openbmc_project.Chassis.Buttons"); 3290 3291 powerButtonIface->register_property( 3292 "ButtonMasked", false, [](const bool requested, bool& current) { 3293 if (requested) 3294 { 3295 if (powerButtonMask) 3296 { 3297 return 1; 3298 } 3299 if (!setGPIOOutput(powerOutConfig.lineName, 3300 !powerOutConfig.polarity, 3301 powerButtonMask)) 3302 { 3303 throw std::runtime_error("Failed to request GPIO"); 3304 return 0; 3305 } 3306 lg2::info("Power Button Masked."); 3307 } 3308 else 3309 { 3310 if (!powerButtonMask) 3311 { 3312 return 1; 3313 } 3314 lg2::info("Power Button Un-masked"); 3315 powerButtonMask.reset(); 3316 } 3317 // Update the mask setting 3318 current = requested; 3319 return 1; 3320 }); 3321 3322 // Check power button state 3323 bool powerButtonPressed; 3324 if (powerButtonConfig.type == ConfigType::GPIO) 3325 { 3326 powerButtonPressed = powerButtonLine.get_value() == 0; 3327 } 3328 else 3329 { 3330 powerButtonPressed = getProperty(powerButtonConfig) == 0; 3331 } 3332 3333 powerButtonIface->register_property("ButtonPressed", 3334 powerButtonPressed); 3335 3336 powerButtonIface->initialize(); 3337 } 3338 3339 if (!resetButtonConfig.lineName.empty()) 3340 { 3341 // Reset Button Interface 3342 3343 resetButtonIface = buttonsServer.add_interface( 3344 "/xyz/openbmc_project/chassis/buttons/reset", 3345 "xyz.openbmc_project.Chassis.Buttons"); 3346 3347 resetButtonIface->register_property( 3348 "ButtonMasked", false, [](const bool requested, bool& current) { 3349 if (requested) 3350 { 3351 if (resetButtonMask) 3352 { 3353 return 1; 3354 } 3355 if (!setGPIOOutput(resetOutConfig.lineName, 3356 !resetOutConfig.polarity, 3357 resetButtonMask)) 3358 { 3359 throw std::runtime_error("Failed to request GPIO"); 3360 return 0; 3361 } 3362 lg2::info("Reset Button Masked."); 3363 } 3364 else 3365 { 3366 if (!resetButtonMask) 3367 { 3368 return 1; 3369 } 3370 lg2::info("Reset Button Un-masked"); 3371 resetButtonMask.reset(); 3372 } 3373 // Update the mask setting 3374 current = requested; 3375 return 1; 3376 }); 3377 3378 // Check reset button state 3379 bool resetButtonPressed; 3380 if (resetButtonConfig.type == ConfigType::GPIO) 3381 { 3382 resetButtonPressed = resetButtonLine.get_value() == 0; 3383 } 3384 else 3385 { 3386 resetButtonPressed = getProperty(resetButtonConfig) == 0; 3387 } 3388 3389 resetButtonIface->register_property("ButtonPressed", 3390 resetButtonPressed); 3391 3392 resetButtonIface->initialize(); 3393 } 3394 3395 if (nmiButtonLine) 3396 { 3397 // NMI Button Interface 3398 nmiButtonIface = buttonsServer.add_interface( 3399 "/xyz/openbmc_project/chassis/buttons/nmi", 3400 "xyz.openbmc_project.Chassis.Buttons"); 3401 3402 nmiButtonIface->register_property( 3403 "ButtonMasked", false, [](const bool requested, bool& current) { 3404 if (nmiButtonMasked == requested) 3405 { 3406 // NMI button mask is already set as requested, so no change 3407 return 1; 3408 } 3409 if (requested) 3410 { 3411 lg2::info("NMI Button Masked."); 3412 nmiButtonMasked = true; 3413 } 3414 else 3415 { 3416 lg2::info("NMI Button Un-masked."); 3417 nmiButtonMasked = false; 3418 } 3419 // Update the mask setting 3420 current = nmiButtonMasked; 3421 return 1; 3422 }); 3423 3424 // Check NMI button state 3425 bool nmiButtonPressed; 3426 if (nmiButtonConfig.type == ConfigType::GPIO) 3427 { 3428 nmiButtonPressed = nmiButtonLine.get_value() == 0; 3429 } 3430 else 3431 { 3432 nmiButtonPressed = getProperty(nmiButtonConfig) == 0; 3433 } 3434 3435 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed); 3436 3437 nmiButtonIface->initialize(); 3438 } 3439 3440 if (nmiOutLine) 3441 { 3442 // NMI out Service 3443 sdbusplus::asio::object_server nmiOutServer = 3444 sdbusplus::asio::object_server(conn); 3445 3446 // NMI out Interface 3447 nmiOutIface = nmiOutServer.add_interface( 3448 "/xyz/openbmc_project/control/host" + node + "/nmi", 3449 "xyz.openbmc_project.Control.Host.NMI"); 3450 nmiOutIface->register_method("NMI", nmiReset); 3451 nmiOutIface->initialize(); 3452 } 3453 3454 if (idButtonLine) 3455 { 3456 // ID Button Interface 3457 idButtonIface = buttonsServer.add_interface( 3458 "/xyz/openbmc_project/chassis/buttons/id", 3459 "xyz.openbmc_project.Chassis.Buttons"); 3460 3461 // Check ID button state 3462 bool idButtonPressed; 3463 if (idButtonConfig.type == ConfigType::GPIO) 3464 { 3465 idButtonPressed = idButtonLine.get_value() == 0; 3466 } 3467 else 3468 { 3469 idButtonPressed = getProperty(idButtonConfig) == 0; 3470 } 3471 3472 idButtonIface->register_property("ButtonPressed", idButtonPressed); 3473 3474 idButtonIface->initialize(); 3475 } 3476 3477 // OS State Service 3478 sdbusplus::asio::object_server osServer = 3479 sdbusplus::asio::object_server(conn); 3480 3481 // OS State Interface 3482 osIface = osServer.add_interface( 3483 "/xyz/openbmc_project/state/host" + node, 3484 "xyz.openbmc_project.State.OperatingSystem.Status"); 3485 3486 // Get the initial OS state based on POST complete 3487 // Asserted, OS state is "Standby" (ready to boot) 3488 // De-Asserted, OS state is "Inactive" 3489 OperatingSystemStateStage osState; 3490 if (postCompleteConfig.type == ConfigType::GPIO) 3491 { 3492 osState = postCompleteLine.get_value() == postCompleteConfig.polarity 3493 ? OperatingSystemStateStage::Standby 3494 : OperatingSystemStateStage::Inactive; 3495 } 3496 else 3497 { 3498 osState = getProperty(postCompleteConfig) > 0 3499 ? OperatingSystemStateStage::Standby 3500 : OperatingSystemStateStage::Inactive; 3501 } 3502 3503 osIface->register_property( 3504 "OperatingSystemState", 3505 std::string(getOperatingSystemStateStage(osState))); 3506 3507 osIface->initialize(); 3508 3509 // Restart Cause Service 3510 sdbusplus::asio::object_server restartCauseServer = 3511 sdbusplus::asio::object_server(conn); 3512 3513 // Restart Cause Interface 3514 restartCauseIface = restartCauseServer.add_interface( 3515 "/xyz/openbmc_project/control/host" + node + "/restart_cause", 3516 "xyz.openbmc_project.Control.Host.RestartCause"); 3517 3518 restartCauseIface->register_property( 3519 "RestartCause", 3520 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown")); 3521 3522 restartCauseIface->register_property( 3523 "RequestedRestartCause", 3524 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"), 3525 [](const std::string& requested, std::string& resp) { 3526 if (requested == 3527 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer") 3528 { 3529 addRestartCause(RestartCause::watchdog); 3530 } 3531 else 3532 { 3533 throw std::invalid_argument( 3534 "Unrecognized RestartCause Request"); 3535 return 0; 3536 } 3537 3538 lg2::info("RestartCause requested: {RESTART_CAUSE}", 3539 "RESTART_CAUSE", requested); 3540 resp = requested; 3541 return 1; 3542 }); 3543 3544 restartCauseIface->initialize(); 3545 3546 currentHostStateMonitor(); 3547 3548 if (!hpmStbyEnConfig.lineName.empty()) 3549 { 3550 // Set to indicate BMC's power control module is ready to take 3551 // the inputs [PWR_GOOD] from the HPM FPGA 3552 gpiod::line hpmLine; 3553 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity, 3554 hpmLine)) 3555 { 3556 return -1; 3557 } 3558 } 3559 3560 io.run(); 3561 3562 return 0; 3563 } 3564