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