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