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