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