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