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