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 char const* setingsService = "xyz.openbmc_project.Settings"; 881 static constexpr char const* powerRestorePolicyIface = 882 "xyz.openbmc_project.Control.Power.RestorePolicy"; 883 #ifdef USE_ACBOOT 884 static constexpr char const* powerACBootObject = 885 "/xyz/openbmc_project/control/host0/ac_boot"; 886 static constexpr char const* 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 = 937 "/xyz/openbmc_project/control/host" + 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([this](const boost::system::error_code 1064 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( 1146 "{GPIO_NAME} fd handler error: {ERROR_MSG}", 1147 "GPIO_NAME", name, "ERROR_MSG", ec.message()); 1148 // TODO: throw here to force power-control to 1149 // restart? 1150 return; 1151 } 1152 gpiod::line_event line_event = line.event_read(); 1153 eventHandler(line_event.event_type == 1154 gpiod::line_event::RISING_EDGE); 1155 waitForGPIOEvent(name, eventHandler, line, event); 1156 }); 1157 } 1158 1159 static bool requestGPIOEvents( 1160 const std::string& name, const std::function<void(bool)>& handler, 1161 gpiod::line& gpioLine, 1162 boost::asio::posix::stream_descriptor& gpioEventDescriptor) 1163 { 1164 // Find the GPIO line 1165 gpioLine = gpiod::find_line(name); 1166 if (!gpioLine) 1167 { 1168 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name); 1169 return false; 1170 } 1171 1172 try 1173 { 1174 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}}); 1175 } 1176 catch (const std::exception& e) 1177 { 1178 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}", 1179 "GPIO_NAME", name, "ERROR", e); 1180 return false; 1181 } 1182 1183 int gpioLineFd = gpioLine.event_get_fd(); 1184 if (gpioLineFd < 0) 1185 { 1186 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name); 1187 return false; 1188 } 1189 1190 gpioEventDescriptor.assign(gpioLineFd); 1191 1192 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor); 1193 return true; 1194 } 1195 1196 static bool setGPIOOutput(const std::string& name, const int value, 1197 gpiod::line& gpioLine) 1198 { 1199 // Find the GPIO line 1200 gpioLine = gpiod::find_line(name); 1201 if (!gpioLine) 1202 { 1203 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name); 1204 return false; 1205 } 1206 1207 // Request GPIO output to specified value 1208 try 1209 { 1210 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}}, 1211 value); 1212 } 1213 catch (const std::exception& e) 1214 { 1215 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME", 1216 name, "ERROR", e); 1217 return false; 1218 } 1219 1220 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name, 1221 "GPIO_VALUE", value); 1222 return true; 1223 } 1224 1225 static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine, 1226 const std::string& name, const int value, 1227 const int durationMs) 1228 { 1229 // Set the masked GPIO line to the specified value 1230 maskedGPIOLine.set_value(value); 1231 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name, 1232 "GPIO_VALUE", value); 1233 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs)); 1234 gpioAssertTimer.async_wait( 1235 [maskedGPIOLine, value, name](const boost::system::error_code ec) { 1236 // Set the masked GPIO line back to the opposite value 1237 maskedGPIOLine.set_value(!value); 1238 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name); 1239 if (ec) 1240 { 1241 // operation_aborted is expected if timer is canceled before 1242 // completion. 1243 if (ec != boost::asio::error::operation_aborted) 1244 { 1245 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 1246 "GPIO_NAME", name, "ERROR_MSG", ec.message()); 1247 } 1248 } 1249 }); 1250 return 0; 1251 } 1252 1253 static int setGPIOOutputForMs(const ConfigData& config, const int value, 1254 const int durationMs) 1255 { 1256 // If the requested GPIO is masked, use the mask line to set the output 1257 if (powerButtonMask && config.lineName == powerOutConfig.lineName) 1258 { 1259 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value, 1260 durationMs); 1261 } 1262 if (resetButtonMask && config.lineName == resetOutConfig.lineName) 1263 { 1264 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value, 1265 durationMs); 1266 } 1267 1268 // No mask set, so request and set the GPIO normally 1269 gpiod::line gpioLine; 1270 if (!setGPIOOutput(config.lineName, value, gpioLine)) 1271 { 1272 return -1; 1273 } 1274 const std::string name = config.lineName; 1275 1276 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs)); 1277 gpioAssertTimer.async_wait( 1278 [gpioLine, value, name](const boost::system::error_code ec) { 1279 // Set the GPIO line back to the opposite value 1280 gpioLine.set_value(!value); 1281 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name); 1282 if (ec) 1283 { 1284 // operation_aborted is expected if timer is canceled before 1285 // completion. 1286 if (ec != boost::asio::error::operation_aborted) 1287 { 1288 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 1289 "GPIO_NAME", name, "ERROR_MSG", ec.message()); 1290 } 1291 } 1292 }); 1293 return 0; 1294 } 1295 1296 static int assertGPIOForMs(const ConfigData& config, const int durationMs) 1297 { 1298 return setGPIOOutputForMs(config, config.polarity, durationMs); 1299 } 1300 1301 static void powerOn() 1302 { 1303 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]); 1304 } 1305 #ifdef CHASSIS_SYSTEM_RESET 1306 static int slotPowerOn() 1307 { 1308 if (power_control::slotPowerState != power_control::SlotPowerState::on) 1309 { 1310 1311 slotPowerLine.set_value(1); 1312 1313 if (slotPowerLine.get_value() > 0) 1314 { 1315 setSlotPowerState(SlotPowerState::on); 1316 lg2::info("Slot Power is switched On\n"); 1317 } 1318 else 1319 { 1320 return -1; 1321 } 1322 } 1323 else 1324 { 1325 lg2::info("Slot Power is already in 'On' state\n"); 1326 return -1; 1327 } 1328 return 0; 1329 } 1330 static int slotPowerOff() 1331 { 1332 if (power_control::slotPowerState != power_control::SlotPowerState::off) 1333 { 1334 slotPowerLine.set_value(0); 1335 1336 if (!(slotPowerLine.get_value() > 0)) 1337 { 1338 setSlotPowerState(SlotPowerState::off); 1339 setPowerState(PowerState::off); 1340 lg2::info("Slot Power is switched Off\n"); 1341 } 1342 else 1343 { 1344 return -1; 1345 } 1346 } 1347 else 1348 { 1349 lg2::info("Slot Power is already in 'Off' state\n"); 1350 return -1; 1351 } 1352 return 0; 1353 } 1354 static void slotPowerCycle() 1355 { 1356 lg2::info("Slot Power Cycle started\n"); 1357 slotPowerOff(); 1358 slotPowerCycleTimer.expires_after( 1359 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"])); 1360 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) { 1361 if (ec) 1362 { 1363 if (ec != boost::asio::error::operation_aborted) 1364 { 1365 lg2::error( 1366 "Slot Power cycle timer async_wait failed: {ERROR_MSG}", 1367 "ERROR_MSG", ec.message()); 1368 } 1369 lg2::info("Slot Power cycle timer canceled\n"); 1370 return; 1371 } 1372 lg2::info("Slot Power cycle timer completed\n"); 1373 slotPowerOn(); 1374 lg2::info("Slot Power Cycle Completed\n"); 1375 }); 1376 } 1377 #endif 1378 static void gracefulPowerOff() 1379 { 1380 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]); 1381 } 1382 1383 static void forcePowerOff() 1384 { 1385 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0) 1386 { 1387 return; 1388 } 1389 1390 // If the force off timer expires, then the power-button override failed 1391 gpioAssertTimer.async_wait([](const boost::system::error_code ec) { 1392 if (ec) 1393 { 1394 // operation_aborted is expected if timer is canceled before 1395 // completion. 1396 if (ec != boost::asio::error::operation_aborted) 1397 { 1398 lg2::error("Force power off async_wait failed: {ERROR_MSG}", 1399 "ERROR_MSG", ec.message()); 1400 } 1401 return; 1402 } 1403 1404 lg2::error("Power-button override failed. Not sure what to do now."); 1405 }); 1406 } 1407 1408 static void reset() 1409 { 1410 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]); 1411 } 1412 1413 static void gracefulPowerOffTimerStart() 1414 { 1415 lg2::info("Graceful power-off timer started"); 1416 gracefulPowerOffTimer.expires_after( 1417 std::chrono::seconds(TimerMap["GracefulPowerOffS"])); 1418 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) { 1419 if (ec) 1420 { 1421 // operation_aborted is expected if timer is canceled before 1422 // completion. 1423 if (ec != boost::asio::error::operation_aborted) 1424 { 1425 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}", 1426 "ERROR_MSG", ec.message()); 1427 } 1428 lg2::info("Graceful power-off timer canceled"); 1429 return; 1430 } 1431 lg2::info("Graceful power-off timer completed"); 1432 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired); 1433 }); 1434 } 1435 1436 static void powerCycleTimerStart() 1437 { 1438 lg2::info("Power-cycle timer started"); 1439 powerCycleTimer.expires_after( 1440 std::chrono::milliseconds(TimerMap["PowerCycleMs"])); 1441 powerCycleTimer.async_wait([](const boost::system::error_code ec) { 1442 if (ec) 1443 { 1444 // operation_aborted is expected if timer is canceled before 1445 // completion. 1446 if (ec != boost::asio::error::operation_aborted) 1447 { 1448 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}", 1449 "ERROR_MSG", ec.message()); 1450 } 1451 lg2::info("Power-cycle timer canceled"); 1452 return; 1453 } 1454 lg2::info("Power-cycle timer completed"); 1455 sendPowerControlEvent(Event::powerCycleTimerExpired); 1456 }); 1457 } 1458 1459 static void psPowerOKWatchdogTimerStart() 1460 { 1461 lg2::info("power supply power OK watchdog timer started"); 1462 psPowerOKWatchdogTimer.expires_after( 1463 std::chrono::milliseconds(TimerMap["PsPowerOKWatchdogMs"])); 1464 psPowerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) { 1465 if (ec) 1466 { 1467 // operation_aborted is expected if timer is canceled before 1468 // completion. 1469 if (ec != boost::asio::error::operation_aborted) 1470 { 1471 lg2::error( 1472 "power supply power OK watchdog async_wait failed: {ERROR_MSG}", 1473 "ERROR_MSG", ec.message()); 1474 } 1475 lg2::info("power supply power OK watchdog timer canceled"); 1476 return; 1477 } 1478 lg2::info("power supply power OK watchdog timer expired"); 1479 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired); 1480 }); 1481 } 1482 1483 static void warmResetCheckTimerStart() 1484 { 1485 lg2::info("Warm reset check timer started"); 1486 warmResetCheckTimer.expires_after( 1487 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"])); 1488 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) { 1489 if (ec) 1490 { 1491 // operation_aborted is expected if timer is canceled before 1492 // completion. 1493 if (ec != boost::asio::error::operation_aborted) 1494 { 1495 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}", 1496 "ERROR_MSG", ec.message()); 1497 } 1498 lg2::info("Warm reset check timer canceled"); 1499 return; 1500 } 1501 lg2::info("Warm reset check timer completed"); 1502 sendPowerControlEvent(Event::warmResetDetected); 1503 }); 1504 } 1505 1506 static void pohCounterTimerStart() 1507 { 1508 lg2::info("POH timer started"); 1509 // Set the time-out as 1 hour, to align with POH command in ipmid 1510 pohCounterTimer.expires_after(std::chrono::hours(1)); 1511 pohCounterTimer.async_wait([](const boost::system::error_code& ec) { 1512 if (ec) 1513 { 1514 // operation_aborted is expected if timer is canceled before 1515 // completion. 1516 if (ec != boost::asio::error::operation_aborted) 1517 { 1518 lg2::error("POH timer async_wait failed: {ERROR_MSG}", 1519 "ERROR_MSG", ec.message()); 1520 } 1521 lg2::info("POH timer canceled"); 1522 return; 1523 } 1524 1525 if (getHostState(powerState) != 1526 "xyz.openbmc_project.State.Host.HostState.Running") 1527 { 1528 return; 1529 } 1530 1531 conn->async_method_call( 1532 [](boost::system::error_code ec, 1533 const std::variant<uint32_t>& pohCounterProperty) { 1534 if (ec) 1535 { 1536 lg2::error("error getting poh counter"); 1537 return; 1538 } 1539 const uint32_t* pohCounter = 1540 std::get_if<uint32_t>(&pohCounterProperty); 1541 if (pohCounter == nullptr) 1542 { 1543 lg2::error("unable to read poh counter"); 1544 return; 1545 } 1546 1547 conn->async_method_call( 1548 [](boost::system::error_code ec) { 1549 if (ec) 1550 { 1551 lg2::error("failed to set poh counter"); 1552 } 1553 }, 1554 "xyz.openbmc_project.Settings", 1555 "/xyz/openbmc_project/state/chassis0", 1556 "org.freedesktop.DBus.Properties", "Set", 1557 "xyz.openbmc_project.State.PowerOnHours", "POHCounter", 1558 std::variant<uint32_t>(*pohCounter + 1)); 1559 }, 1560 "xyz.openbmc_project.Settings", 1561 "/xyz/openbmc_project/state/chassis0", 1562 "org.freedesktop.DBus.Properties", "Get", 1563 "xyz.openbmc_project.State.PowerOnHours", "POHCounter"); 1564 1565 pohCounterTimerStart(); 1566 }); 1567 } 1568 1569 static void currentHostStateMonitor() 1570 { 1571 if (getHostState(powerState) == 1572 "xyz.openbmc_project.State.Host.HostState.Running") 1573 { 1574 pohCounterTimerStart(); 1575 // Clear the restart cause set for the next restart 1576 clearRestartCause(); 1577 } 1578 else 1579 { 1580 pohCounterTimer.cancel(); 1581 // Set the restart cause set for this restart 1582 setRestartCause(); 1583 } 1584 1585 static auto match = sdbusplus::bus::match_t( 1586 *conn, 1587 "type='signal',member='PropertiesChanged', " 1588 "interface='org.freedesktop.DBus.Properties', " 1589 "arg0='xyz.openbmc_project.State.Host'", 1590 [](sdbusplus::message_t& message) { 1591 std::string intfName; 1592 std::map<std::string, std::variant<std::string>> properties; 1593 1594 try 1595 { 1596 message.read(intfName, properties); 1597 } 1598 catch (const std::exception& e) 1599 { 1600 lg2::error("Unable to read host state: {ERROR}", "ERROR", e); 1601 return; 1602 } 1603 if (properties.empty()) 1604 { 1605 lg2::error("ERROR: Empty PropertiesChanged signal received"); 1606 return; 1607 } 1608 1609 // We only want to check for CurrentHostState 1610 if (properties.begin()->first != "CurrentHostState") 1611 { 1612 return; 1613 } 1614 std::string* currentHostState = 1615 std::get_if<std::string>(&(properties.begin()->second)); 1616 if (currentHostState == nullptr) 1617 { 1618 lg2::error("{PROPERTY} property invalid", "PROPERTY", 1619 properties.begin()->first); 1620 return; 1621 } 1622 1623 if (*currentHostState == 1624 "xyz.openbmc_project.State.Host.HostState.Running") 1625 { 1626 pohCounterTimerStart(); 1627 // Clear the restart cause set for the next restart 1628 clearRestartCause(); 1629 sd_journal_send("MESSAGE=Host system DC power is on", 1630 "PRIORITY=%i", LOG_INFO, 1631 "REDFISH_MESSAGE_ID=%s", 1632 "OpenBMC.0.1.DCPowerOn", NULL); 1633 } 1634 else 1635 { 1636 pohCounterTimer.cancel(); 1637 // POST_COMPLETE GPIO event is not working in some platforms 1638 // when power state is changed to OFF. This resulted in 1639 // 'OperatingSystemState' to stay at 'Standby', even though 1640 // system is OFF. Set 'OperatingSystemState' to 'Inactive' 1641 // if HostState is trurned to OFF. 1642 setOperatingSystemState(OperatingSystemStateStage::Inactive); 1643 1644 // Set the restart cause set for this restart 1645 setRestartCause(); 1646 #ifdef USE_ACBOOT 1647 resetACBootProperty(); 1648 #endif // USE_ACBOOT 1649 sd_journal_send("MESSAGE=Host system DC power is off", 1650 "PRIORITY=%i", LOG_INFO, 1651 "REDFISH_MESSAGE_ID=%s", 1652 "OpenBMC.0.1.DCPowerOff", NULL); 1653 } 1654 }); 1655 } 1656 1657 static void sioPowerGoodWatchdogTimerStart() 1658 { 1659 lg2::info("SIO power good watchdog timer started"); 1660 sioPowerGoodWatchdogTimer.expires_after( 1661 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"])); 1662 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code 1663 ec) { 1664 if (ec) 1665 { 1666 // operation_aborted is expected if timer is canceled before 1667 // completion. 1668 if (ec != boost::asio::error::operation_aborted) 1669 { 1670 lg2::error( 1671 "SIO power good watchdog async_wait failed: {ERROR_MSG}", 1672 "ERROR_MSG", ec.message()); 1673 } 1674 lg2::info("SIO power good watchdog timer canceled"); 1675 return; 1676 } 1677 lg2::info("SIO power good watchdog timer completed"); 1678 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired); 1679 }); 1680 } 1681 1682 static void powerStateOn(const Event event) 1683 { 1684 logEvent(__FUNCTION__, event); 1685 switch (event) 1686 { 1687 case Event::psPowerOKDeAssert: 1688 setPowerState(PowerState::off); 1689 // DC power is unexpectedly lost, beep 1690 beep(beepPowerFail); 1691 break; 1692 case Event::sioS5Assert: 1693 setPowerState(PowerState::transitionToOff); 1694 #if IGNORE_SOFT_RESETS_DURING_POST 1695 // Only recognize soft resets once host gets past POST COMPLETE 1696 if (operatingSystemState != OperatingSystemStateStage::Standby) 1697 { 1698 ignoreNextSoftReset = true; 1699 } 1700 #endif 1701 addRestartCause(RestartCause::softReset); 1702 break; 1703 #if USE_PLT_RST 1704 case Event::pltRstAssert: 1705 #else 1706 case Event::postCompleteDeAssert: 1707 #endif 1708 setPowerState(PowerState::checkForWarmReset); 1709 #if IGNORE_SOFT_RESETS_DURING_POST 1710 // Only recognize soft resets once host gets past POST COMPLETE 1711 if (operatingSystemState != OperatingSystemStateStage::Standby) 1712 { 1713 ignoreNextSoftReset = true; 1714 } 1715 #endif 1716 addRestartCause(RestartCause::softReset); 1717 warmResetCheckTimerStart(); 1718 break; 1719 case Event::powerButtonPressed: 1720 setPowerState(PowerState::gracefulTransitionToOff); 1721 gracefulPowerOffTimerStart(); 1722 break; 1723 case Event::powerOffRequest: 1724 setPowerState(PowerState::transitionToOff); 1725 forcePowerOff(); 1726 break; 1727 case Event::gracefulPowerOffRequest: 1728 setPowerState(PowerState::gracefulTransitionToOff); 1729 gracefulPowerOffTimerStart(); 1730 gracefulPowerOff(); 1731 break; 1732 case Event::powerCycleRequest: 1733 setPowerState(PowerState::transitionToCycleOff); 1734 forcePowerOff(); 1735 break; 1736 case Event::gracefulPowerCycleRequest: 1737 setPowerState(PowerState::gracefulTransitionToCycleOff); 1738 gracefulPowerOffTimerStart(); 1739 gracefulPowerOff(); 1740 break; 1741 case Event::resetRequest: 1742 reset(); 1743 break; 1744 default: 1745 lg2::info("No action taken."); 1746 break; 1747 } 1748 } 1749 1750 static void powerStateWaitForPSPowerOK(const Event event) 1751 { 1752 logEvent(__FUNCTION__, event); 1753 switch (event) 1754 { 1755 case Event::psPowerOKAssert: 1756 { 1757 // Cancel any GPIO assertions held during the transition 1758 gpioAssertTimer.cancel(); 1759 psPowerOKWatchdogTimer.cancel(); 1760 if (sioEnabled == true) 1761 { 1762 sioPowerGoodWatchdogTimerStart(); 1763 setPowerState(PowerState::waitForSIOPowerGood); 1764 } 1765 else 1766 { 1767 setPowerState(PowerState::on); 1768 } 1769 break; 1770 } 1771 case Event::psPowerOKWatchdogTimerExpired: 1772 setPowerState(PowerState::off); 1773 psPowerOKFailedLog(); 1774 break; 1775 case Event::sioPowerGoodAssert: 1776 psPowerOKWatchdogTimer.cancel(); 1777 setPowerState(PowerState::on); 1778 break; 1779 default: 1780 lg2::info("No action taken."); 1781 break; 1782 } 1783 } 1784 1785 static void powerStateWaitForSIOPowerGood(const Event event) 1786 { 1787 logEvent(__FUNCTION__, event); 1788 switch (event) 1789 { 1790 case Event::sioPowerGoodAssert: 1791 sioPowerGoodWatchdogTimer.cancel(); 1792 setPowerState(PowerState::on); 1793 break; 1794 case Event::sioPowerGoodWatchdogTimerExpired: 1795 setPowerState(PowerState::off); 1796 systemPowerGoodFailedLog(); 1797 break; 1798 default: 1799 lg2::info("No action taken."); 1800 break; 1801 } 1802 } 1803 1804 static void powerStateOff(const Event event) 1805 { 1806 logEvent(__FUNCTION__, event); 1807 switch (event) 1808 { 1809 case Event::psPowerOKAssert: 1810 { 1811 if (sioEnabled == true) 1812 { 1813 sioPowerGoodWatchdogTimerStart(); 1814 setPowerState(PowerState::waitForSIOPowerGood); 1815 } 1816 else 1817 { 1818 setPowerState(PowerState::on); 1819 } 1820 break; 1821 } 1822 case Event::sioS5DeAssert: 1823 psPowerOKWatchdogTimerStart(); 1824 setPowerState(PowerState::waitForPSPowerOK); 1825 break; 1826 case Event::sioPowerGoodAssert: 1827 setPowerState(PowerState::on); 1828 break; 1829 case Event::powerButtonPressed: 1830 psPowerOKWatchdogTimerStart(); 1831 setPowerState(PowerState::waitForPSPowerOK); 1832 break; 1833 case Event::powerOnRequest: 1834 psPowerOKWatchdogTimerStart(); 1835 setPowerState(PowerState::waitForPSPowerOK); 1836 powerOn(); 1837 break; 1838 default: 1839 lg2::info("No action taken."); 1840 break; 1841 } 1842 } 1843 1844 static void powerStateTransitionToOff(const Event event) 1845 { 1846 logEvent(__FUNCTION__, event); 1847 switch (event) 1848 { 1849 case Event::psPowerOKDeAssert: 1850 // Cancel any GPIO assertions held during the transition 1851 gpioAssertTimer.cancel(); 1852 setPowerState(PowerState::off); 1853 break; 1854 default: 1855 lg2::info("No action taken."); 1856 break; 1857 } 1858 } 1859 1860 static void powerStateGracefulTransitionToOff(const Event event) 1861 { 1862 logEvent(__FUNCTION__, event); 1863 switch (event) 1864 { 1865 case Event::psPowerOKDeAssert: 1866 gracefulPowerOffTimer.cancel(); 1867 setPowerState(PowerState::off); 1868 break; 1869 case Event::gracefulPowerOffTimerExpired: 1870 setPowerState(PowerState::on); 1871 break; 1872 case Event::powerOffRequest: 1873 gracefulPowerOffTimer.cancel(); 1874 setPowerState(PowerState::transitionToOff); 1875 forcePowerOff(); 1876 break; 1877 case Event::powerCycleRequest: 1878 gracefulPowerOffTimer.cancel(); 1879 setPowerState(PowerState::transitionToCycleOff); 1880 forcePowerOff(); 1881 break; 1882 case Event::resetRequest: 1883 gracefulPowerOffTimer.cancel(); 1884 setPowerState(PowerState::on); 1885 reset(); 1886 break; 1887 default: 1888 lg2::info("No action taken."); 1889 break; 1890 } 1891 } 1892 1893 static void powerStateCycleOff(const Event event) 1894 { 1895 logEvent(__FUNCTION__, event); 1896 switch (event) 1897 { 1898 case Event::psPowerOKAssert: 1899 { 1900 powerCycleTimer.cancel(); 1901 if (sioEnabled == true) 1902 { 1903 sioPowerGoodWatchdogTimerStart(); 1904 setPowerState(PowerState::waitForSIOPowerGood); 1905 } 1906 else 1907 { 1908 setPowerState(PowerState::on); 1909 } 1910 break; 1911 } 1912 case Event::sioS5DeAssert: 1913 powerCycleTimer.cancel(); 1914 psPowerOKWatchdogTimerStart(); 1915 setPowerState(PowerState::waitForPSPowerOK); 1916 break; 1917 case Event::powerButtonPressed: 1918 powerCycleTimer.cancel(); 1919 psPowerOKWatchdogTimerStart(); 1920 setPowerState(PowerState::waitForPSPowerOK); 1921 break; 1922 case Event::powerCycleTimerExpired: 1923 psPowerOKWatchdogTimerStart(); 1924 setPowerState(PowerState::waitForPSPowerOK); 1925 powerOn(); 1926 break; 1927 default: 1928 lg2::info("No action taken."); 1929 break; 1930 } 1931 } 1932 1933 static void powerStateTransitionToCycleOff(const Event event) 1934 { 1935 logEvent(__FUNCTION__, event); 1936 switch (event) 1937 { 1938 case Event::psPowerOKDeAssert: 1939 // Cancel any GPIO assertions held during the transition 1940 gpioAssertTimer.cancel(); 1941 setPowerState(PowerState::cycleOff); 1942 powerCycleTimerStart(); 1943 break; 1944 default: 1945 lg2::info("No action taken."); 1946 break; 1947 } 1948 } 1949 1950 static void powerStateGracefulTransitionToCycleOff(const Event event) 1951 { 1952 logEvent(__FUNCTION__, event); 1953 switch (event) 1954 { 1955 case Event::psPowerOKDeAssert: 1956 gracefulPowerOffTimer.cancel(); 1957 setPowerState(PowerState::cycleOff); 1958 powerCycleTimerStart(); 1959 break; 1960 case Event::gracefulPowerOffTimerExpired: 1961 setPowerState(PowerState::on); 1962 break; 1963 case Event::powerOffRequest: 1964 gracefulPowerOffTimer.cancel(); 1965 setPowerState(PowerState::transitionToOff); 1966 forcePowerOff(); 1967 break; 1968 case Event::powerCycleRequest: 1969 gracefulPowerOffTimer.cancel(); 1970 setPowerState(PowerState::transitionToCycleOff); 1971 forcePowerOff(); 1972 break; 1973 case Event::resetRequest: 1974 gracefulPowerOffTimer.cancel(); 1975 setPowerState(PowerState::on); 1976 reset(); 1977 break; 1978 default: 1979 lg2::info("No action taken."); 1980 break; 1981 } 1982 } 1983 1984 static void powerStateCheckForWarmReset(const Event event) 1985 { 1986 logEvent(__FUNCTION__, event); 1987 switch (event) 1988 { 1989 case Event::sioS5Assert: 1990 warmResetCheckTimer.cancel(); 1991 setPowerState(PowerState::transitionToOff); 1992 break; 1993 case Event::warmResetDetected: 1994 setPowerState(PowerState::on); 1995 break; 1996 case Event::psPowerOKDeAssert: 1997 warmResetCheckTimer.cancel(); 1998 setPowerState(PowerState::off); 1999 // DC power is unexpectedly lost, beep 2000 beep(beepPowerFail); 2001 break; 2002 default: 2003 lg2::info("No action taken."); 2004 break; 2005 } 2006 } 2007 2008 static void psPowerOKHandler(bool state) 2009 { 2010 Event powerControlEvent = 2011 state ? Event::psPowerOKAssert : Event::psPowerOKDeAssert; 2012 sendPowerControlEvent(powerControlEvent); 2013 } 2014 2015 static void sioPowerGoodHandler(bool state) 2016 { 2017 Event powerControlEvent = 2018 state ? Event::sioPowerGoodAssert : Event::sioPowerGoodDeAssert; 2019 sendPowerControlEvent(powerControlEvent); 2020 } 2021 2022 static void sioOnControlHandler(bool state) 2023 { 2024 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE", 2025 static_cast<int>(state)); 2026 } 2027 2028 static void sioS5Handler(bool state) 2029 { 2030 Event powerControlEvent = state ? Event::sioS5DeAssert : Event::sioS5Assert; 2031 sendPowerControlEvent(powerControlEvent); 2032 } 2033 2034 static void powerButtonHandler(bool state) 2035 { 2036 powerButtonIface->set_property("ButtonPressed", !state); 2037 if (!state) 2038 { 2039 powerButtonPressLog(); 2040 if (!powerButtonMask) 2041 { 2042 sendPowerControlEvent(Event::powerButtonPressed); 2043 addRestartCause(RestartCause::powerButton); 2044 } 2045 else 2046 { 2047 lg2::info("power button press masked"); 2048 } 2049 } 2050 } 2051 2052 static void resetButtonHandler(bool state) 2053 { 2054 resetButtonIface->set_property("ButtonPressed", !state); 2055 if (!state) 2056 { 2057 resetButtonPressLog(); 2058 if (!resetButtonMask) 2059 { 2060 sendPowerControlEvent(Event::resetButtonPressed); 2061 addRestartCause(RestartCause::resetButton); 2062 } 2063 else 2064 { 2065 lg2::info("reset button press masked"); 2066 } 2067 } 2068 } 2069 2070 #ifdef CHASSIS_SYSTEM_RESET 2071 static constexpr auto systemdBusname = "org.freedesktop.systemd1"; 2072 static constexpr auto systemdPath = "/org/freedesktop/systemd1"; 2073 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager"; 2074 static constexpr auto systemTargetName = "chassis-system-reset.target"; 2075 2076 void systemReset() 2077 { 2078 conn->async_method_call( 2079 [](boost::system::error_code ec) { 2080 if (ec) 2081 { 2082 lg2::error("Failed to call chassis system reset: {ERR}", "ERR", 2083 ec.message()); 2084 } 2085 }, 2086 systemdBusname, systemdPath, systemdInterface, "StartUnit", 2087 systemTargetName, "replace"); 2088 } 2089 #endif 2090 2091 static void nmiSetEnableProperty(bool value) 2092 { 2093 conn->async_method_call( 2094 [](boost::system::error_code ec) { 2095 if (ec) 2096 { 2097 lg2::error("failed to set NMI source"); 2098 } 2099 }, 2100 "xyz.openbmc_project.Settings", 2101 "/xyz/openbmc_project/Chassis/Control/NMISource", 2102 "org.freedesktop.DBus.Properties", "Set", 2103 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled", 2104 std::variant<bool>{value}); 2105 } 2106 2107 static void nmiReset(void) 2108 { 2109 const static constexpr int nmiOutPulseTimeMs = 200; 2110 2111 lg2::info("NMI out action"); 2112 nmiOutLine.set_value(nmiOutConfig.polarity); 2113 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", 2114 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity); 2115 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs)); 2116 gpioAssertTimer.async_wait([](const boost::system::error_code ec) { 2117 // restore the NMI_OUT GPIO line back to the opposite value 2118 nmiOutLine.set_value(!nmiOutConfig.polarity); 2119 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName); 2120 if (ec) 2121 { 2122 // operation_aborted is expected if timer is canceled before 2123 // completion. 2124 if (ec != boost::asio::error::operation_aborted) 2125 { 2126 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 2127 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG", 2128 ec.message()); 2129 } 2130 } 2131 }); 2132 // log to redfish 2133 nmiDiagIntLog(); 2134 lg2::info("NMI out action completed"); 2135 // reset Enable Property 2136 nmiSetEnableProperty(false); 2137 } 2138 2139 static void nmiSourcePropertyMonitor(void) 2140 { 2141 lg2::info("NMI Source Property Monitor"); 2142 2143 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch = 2144 std::make_unique<sdbusplus::bus::match_t>( 2145 *conn, 2146 "type='signal',interface='org.freedesktop.DBus.Properties'," 2147 "member='PropertiesChanged'," 2148 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'", 2149 [](sdbusplus::message_t& msg) { 2150 std::string interfaceName; 2151 boost::container::flat_map<std::string, 2152 std::variant<bool, std::string>> 2153 propertiesChanged; 2154 std::string state; 2155 bool value = true; 2156 try 2157 { 2158 msg.read(interfaceName, propertiesChanged); 2159 if (propertiesChanged.begin()->first == "Enabled") 2160 { 2161 value = 2162 std::get<bool>(propertiesChanged.begin()->second); 2163 lg2::info( 2164 "NMI Enabled propertiesChanged value: {VALUE}", 2165 "VALUE", value); 2166 nmiEnabled = value; 2167 if (nmiEnabled) 2168 { 2169 nmiReset(); 2170 } 2171 } 2172 } 2173 catch (const std::exception& e) 2174 { 2175 lg2::error("Unable to read NMI source: {ERROR}", "ERROR", 2176 e); 2177 return; 2178 } 2179 }); 2180 } 2181 2182 static void setNmiSource() 2183 { 2184 conn->async_method_call( 2185 [](boost::system::error_code ec) { 2186 if (ec) 2187 { 2188 lg2::error("failed to set NMI source"); 2189 } 2190 }, 2191 "xyz.openbmc_project.Settings", 2192 "/xyz/openbmc_project/Chassis/Control/NMISource", 2193 "org.freedesktop.DBus.Properties", "Set", 2194 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource", 2195 std::variant<std::string>{ 2196 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FpBtn"}); 2197 // set Enable Property 2198 nmiSetEnableProperty(true); 2199 } 2200 2201 static void nmiButtonHandler(bool state) 2202 { 2203 nmiButtonIface->set_property("ButtonPressed", !state); 2204 if (!state) 2205 { 2206 nmiButtonPressLog(); 2207 if (nmiButtonMasked) 2208 { 2209 lg2::info("NMI button press masked"); 2210 } 2211 else 2212 { 2213 setNmiSource(); 2214 } 2215 } 2216 } 2217 2218 static void idButtonHandler(bool state) 2219 { 2220 idButtonIface->set_property("ButtonPressed", !state); 2221 } 2222 2223 static void pltRstHandler(bool pltRst) 2224 { 2225 if (pltRst) 2226 { 2227 sendPowerControlEvent(Event::pltRstDeAssert); 2228 } 2229 else 2230 { 2231 sendPowerControlEvent(Event::pltRstAssert); 2232 } 2233 } 2234 2235 [[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg) 2236 { 2237 std::string interfaceName; 2238 boost::container::flat_map<std::string, std::variant<bool>> 2239 propertiesChanged; 2240 try 2241 { 2242 msg.read(interfaceName, propertiesChanged); 2243 } 2244 catch (const std::exception& e) 2245 { 2246 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e); 2247 return; 2248 } 2249 if (propertiesChanged.empty()) 2250 { 2251 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received"); 2252 return; 2253 } 2254 2255 for (auto& [property, value] : propertiesChanged) 2256 { 2257 if (property == "ESpiPlatformReset") 2258 { 2259 bool* pltRst = std::get_if<bool>(&value); 2260 if (pltRst == nullptr) 2261 { 2262 lg2::error("{PROPERTY} property invalid", "PROPERTY", property); 2263 return; 2264 } 2265 pltRstHandler(*pltRst); 2266 } 2267 } 2268 } 2269 2270 static void postCompleteHandler(bool state) 2271 { 2272 if (!state) 2273 { 2274 sendPowerControlEvent(Event::postCompleteAssert); 2275 setOperatingSystemState(OperatingSystemStateStage::Standby); 2276 } 2277 else 2278 { 2279 sendPowerControlEvent(Event::postCompleteDeAssert); 2280 setOperatingSystemState(OperatingSystemStateStage::Inactive); 2281 } 2282 } 2283 2284 static int loadConfigValues() 2285 { 2286 const std::string configFilePath = 2287 "/usr/share/x86-power-control/power-config-host" + power_control::node + 2288 ".json"; 2289 std::ifstream configFile(configFilePath.c_str()); 2290 if (!configFile.is_open()) 2291 { 2292 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'", 2293 "PATH", configFilePath); 2294 return -1; 2295 } 2296 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true); 2297 2298 if (jsonData.is_discarded()) 2299 { 2300 lg2::error("Power config readings JSON parser failure"); 2301 return -1; 2302 } 2303 auto gpios = jsonData["gpio_configs"]; 2304 auto timers = jsonData["timing_configs"]; 2305 2306 ConfigData* tempGpioData; 2307 2308 for (nlohmann::json& gpioConfig : gpios) 2309 { 2310 if (!gpioConfig.contains("Name")) 2311 { 2312 lg2::error("The 'Name' field must be defined in Json file"); 2313 return -1; 2314 } 2315 2316 // Iterate through the powersignal map to check if the gpio json config 2317 // entry is valid 2318 std::string gpioName = gpioConfig["Name"]; 2319 auto signalMapIter = powerSignalMap.find(gpioName); 2320 if (signalMapIter == powerSignalMap.end()) 2321 { 2322 lg2::error( 2323 "{GPIO_NAME} is not a recognized power-control signal name", 2324 "GPIO_NAME", gpioName); 2325 return -1; 2326 } 2327 2328 // assign the power signal name to the corresponding structure reference 2329 // from map then fillup the structure with coressponding json config 2330 // value 2331 tempGpioData = signalMapIter->second; 2332 tempGpioData->name = gpioName; 2333 2334 if (!gpioConfig.contains("Type")) 2335 { 2336 lg2::error("The \'Type\' field must be defined in Json file"); 2337 return -1; 2338 } 2339 2340 std::string signalType = gpioConfig["Type"]; 2341 if (signalType == "GPIO") 2342 { 2343 tempGpioData->type = ConfigType::GPIO; 2344 } 2345 else if (signalType == "DBUS") 2346 { 2347 tempGpioData->type = ConfigType::DBUS; 2348 } 2349 else 2350 { 2351 lg2::error("{TYPE} is not a recognized power-control signal type", 2352 "TYPE", signalType); 2353 return -1; 2354 } 2355 2356 if (tempGpioData->type == ConfigType::GPIO) 2357 { 2358 if (gpioConfig.contains("LineName")) 2359 { 2360 tempGpioData->lineName = gpioConfig["LineName"]; 2361 } 2362 else 2363 { 2364 lg2::error( 2365 "The \'LineName\' field must be defined for GPIO configuration"); 2366 return -1; 2367 } 2368 if (gpioConfig.contains("Polarity")) 2369 { 2370 std::string polarity = gpioConfig["Polarity"]; 2371 if (polarity == "ActiveLow") 2372 { 2373 tempGpioData->polarity = false; 2374 } 2375 else if (polarity == "ActiveHigh") 2376 { 2377 tempGpioData->polarity = true; 2378 } 2379 else 2380 { 2381 lg2::error( 2382 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}", 2383 "POLARITY", polarity); 2384 return -1; 2385 } 2386 } 2387 else 2388 { 2389 lg2::error("Polarity field not found for {GPIO_NAME}", 2390 "GPIO_NAME", tempGpioData->lineName); 2391 return -1; 2392 } 2393 } 2394 else 2395 { 2396 // if dbus based gpio config is defined read and update the dbus 2397 // params corresponding to the gpio config instance 2398 for (auto& [key, dbusParamName] : dbusParams) 2399 { 2400 if (!gpioConfig.contains(dbusParamName)) 2401 { 2402 lg2::error( 2403 "The {DBUS_NAME} field must be defined for Dbus configuration ", 2404 "DBUS_NAME", dbusParamName); 2405 return -1; 2406 } 2407 } 2408 tempGpioData->dbusName = 2409 gpioConfig[dbusParams[DbusConfigType::name]]; 2410 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]]; 2411 tempGpioData->interface = 2412 gpioConfig[dbusParams[DbusConfigType::interface]]; 2413 tempGpioData->lineName = 2414 gpioConfig[dbusParams[DbusConfigType::property]]; 2415 } 2416 } 2417 2418 // read and store the timer values from json config to Timer Map 2419 for (auto& [key, timerValue] : TimerMap) 2420 { 2421 if (timers.contains(key.c_str())) 2422 { 2423 timerValue = timers[key.c_str()]; 2424 } 2425 } 2426 2427 return 0; 2428 } 2429 2430 static bool getDbusMsgGPIOState(sdbusplus::message_t& msg, 2431 const std::string& lineName, bool& value) 2432 { 2433 std::string thresholdInterface; 2434 std::string event; 2435 boost::container::flat_map<std::string, std::variant<bool>> 2436 propertiesChanged; 2437 try 2438 { 2439 msg.read(thresholdInterface, propertiesChanged); 2440 if (propertiesChanged.empty()) 2441 { 2442 return false; 2443 } 2444 2445 event = propertiesChanged.begin()->first; 2446 if (event.empty() || event != lineName) 2447 { 2448 return false; 2449 } 2450 2451 value = std::get<bool>(propertiesChanged.begin()->second); 2452 return true; 2453 } 2454 catch (const std::exception& e) 2455 { 2456 lg2::error( 2457 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}", 2458 "DBUS_NAME", lineName, "ERROR", e); 2459 return false; 2460 } 2461 } 2462 2463 static sdbusplus::bus::match_t 2464 dbusGPIOMatcher(const ConfigData& cfg, std::function<void(bool)> onMatch) 2465 { 2466 auto pulseEventMatcherCallback = [&cfg, 2467 onMatch](sdbusplus::message_t& msg) { 2468 bool value = false; 2469 if (!getDbusMsgGPIOState(msg, cfg.lineName, value)) 2470 { 2471 return; 2472 } 2473 onMatch(value); 2474 }; 2475 2476 return sdbusplus::bus::match_t( 2477 static_cast<sdbusplus::bus_t&>(*conn), 2478 "type='signal',interface='org.freedesktop.DBus.Properties',member='" 2479 "PropertiesChanged',arg0='" + 2480 cfg.interface + "'", 2481 std::move(pulseEventMatcherCallback)); 2482 } 2483 2484 int getProperty(ConfigData& configData) 2485 { 2486 auto method = conn->new_method_call( 2487 configData.dbusName.c_str(), configData.path.c_str(), 2488 "org.freedesktop.DBus.Properties", "Get"); 2489 method.append(configData.interface.c_str(), configData.lineName.c_str()); 2490 2491 auto reply = conn->call(method); 2492 if (reply.is_method_error()) 2493 { 2494 lg2::error( 2495 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}", 2496 "PROPERTY", configData.lineName, "INTERFACE", configData.interface, 2497 "PATH", configData.path); 2498 return -1; 2499 } 2500 std::variant<bool> resp; 2501 reply.read(resp); 2502 auto respValue = std::get_if<bool>(&resp); 2503 if (!respValue) 2504 { 2505 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type", 2506 "PROPERTY", configData.lineName); 2507 return -1; 2508 } 2509 return (*respValue); 2510 } 2511 } // namespace power_control 2512 2513 int main(int argc, char* argv[]) 2514 { 2515 using namespace power_control; 2516 2517 if (argc > 1) 2518 { 2519 node = argv[1]; 2520 } 2521 lg2::info("Start Chassis power control service for host : {NODE}", "NODE", 2522 node); 2523 2524 conn = std::make_shared<sdbusplus::asio::connection>(io); 2525 2526 // Load GPIO's through json config file 2527 if (loadConfigValues() == -1) 2528 { 2529 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node); 2530 } 2531 /* Currently for single host based systems additional busname is added 2532 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0. 2533 Going forward for single hosts the old bus name without zero numbering 2534 will be removed when all other applications adapted to the 2535 bus name with zero numbering (xyz.openbmc_project.State.Host0). */ 2536 2537 if (node == "0") 2538 { 2539 // Request all the dbus names 2540 conn->request_name(hostDbusName.c_str()); 2541 conn->request_name(chassisDbusName.c_str()); 2542 conn->request_name(osDbusName.c_str()); 2543 conn->request_name(buttonDbusName.c_str()); 2544 conn->request_name(nmiDbusName.c_str()); 2545 conn->request_name(rstCauseDbusName.c_str()); 2546 } 2547 2548 hostDbusName += node; 2549 chassisDbusName += node; 2550 osDbusName += node; 2551 buttonDbusName += node; 2552 nmiDbusName += node; 2553 rstCauseDbusName += node; 2554 2555 // Request all the dbus names 2556 conn->request_name(hostDbusName.c_str()); 2557 conn->request_name(chassisDbusName.c_str()); 2558 conn->request_name(osDbusName.c_str()); 2559 conn->request_name(buttonDbusName.c_str()); 2560 conn->request_name(nmiDbusName.c_str()); 2561 conn->request_name(rstCauseDbusName.c_str()); 2562 2563 if (sioPwrGoodConfig.lineName.empty() || 2564 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty()) 2565 { 2566 sioEnabled = false; 2567 lg2::info("SIO control GPIOs not defined, disable SIO support."); 2568 } 2569 2570 // Request PS_PWROK GPIO events 2571 if (powerOkConfig.type == ConfigType::GPIO) 2572 { 2573 if (!requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler, 2574 psPowerOKLine, psPowerOKEvent)) 2575 { 2576 return -1; 2577 } 2578 } 2579 else if (powerOkConfig.type == ConfigType::DBUS) 2580 { 2581 2582 static sdbusplus::bus::match_t powerOkEventMonitor = 2583 power_control::dbusGPIOMatcher(powerOkConfig, psPowerOKHandler); 2584 } 2585 else 2586 { 2587 lg2::error("PowerOk name should be configured from json config file"); 2588 return -1; 2589 } 2590 2591 if (sioEnabled == true) 2592 { 2593 // Request SIO_POWER_GOOD GPIO events 2594 if (sioPwrGoodConfig.type == ConfigType::GPIO) 2595 { 2596 if (!requestGPIOEvents(sioPwrGoodConfig.lineName, 2597 sioPowerGoodHandler, sioPowerGoodLine, 2598 sioPowerGoodEvent)) 2599 { 2600 return -1; 2601 } 2602 } 2603 else if (sioPwrGoodConfig.type == ConfigType::DBUS) 2604 { 2605 static sdbusplus::bus::match_t sioPwrGoodEventMonitor = 2606 power_control::dbusGPIOMatcher(sioPwrGoodConfig, 2607 sioPowerGoodHandler); 2608 } 2609 else 2610 { 2611 lg2::error( 2612 "sioPwrGood name should be configured from json config file"); 2613 return -1; 2614 } 2615 2616 // Request SIO_ONCONTROL GPIO events 2617 if (sioOnControlConfig.type == ConfigType::GPIO) 2618 { 2619 if (!requestGPIOEvents(sioOnControlConfig.lineName, 2620 sioOnControlHandler, sioOnControlLine, 2621 sioOnControlEvent)) 2622 { 2623 return -1; 2624 } 2625 } 2626 else if (sioOnControlConfig.type == ConfigType::DBUS) 2627 { 2628 static sdbusplus::bus::match_t sioOnControlEventMonitor = 2629 power_control::dbusGPIOMatcher(sioOnControlConfig, 2630 sioOnControlHandler); 2631 } 2632 else 2633 { 2634 lg2::error( 2635 "sioOnControl name should be configured from jsonconfig file\n"); 2636 return -1; 2637 } 2638 2639 // Request SIO_S5 GPIO events 2640 if (sioS5Config.type == ConfigType::GPIO) 2641 { 2642 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler, 2643 sioS5Line, sioS5Event)) 2644 { 2645 return -1; 2646 } 2647 } 2648 else if (sioS5Config.type == ConfigType::DBUS) 2649 { 2650 static sdbusplus::bus::match_t sioS5EventMonitor = 2651 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler); 2652 } 2653 else 2654 { 2655 lg2::error("sioS5 name should be configured from json config file"); 2656 return -1; 2657 } 2658 } 2659 2660 // Request POWER_BUTTON GPIO events 2661 if (powerButtonConfig.type == ConfigType::GPIO) 2662 { 2663 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler, 2664 powerButtonLine, powerButtonEvent)) 2665 { 2666 return -1; 2667 } 2668 } 2669 else if (powerButtonConfig.type == ConfigType::DBUS) 2670 { 2671 static sdbusplus::bus::match_t powerButtonEventMonitor = 2672 power_control::dbusGPIOMatcher(powerButtonConfig, 2673 powerButtonHandler); 2674 } 2675 2676 // Request RESET_BUTTON GPIO events 2677 if (resetButtonConfig.type == ConfigType::GPIO) 2678 { 2679 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler, 2680 resetButtonLine, resetButtonEvent)) 2681 { 2682 return -1; 2683 } 2684 } 2685 else if (resetButtonConfig.type == ConfigType::DBUS) 2686 { 2687 static sdbusplus::bus::match_t resetButtonEventMonitor = 2688 power_control::dbusGPIOMatcher(resetButtonConfig, 2689 resetButtonHandler); 2690 } 2691 2692 // Request NMI_BUTTON GPIO events 2693 if (nmiButtonConfig.type == ConfigType::GPIO) 2694 { 2695 if (!nmiButtonConfig.lineName.empty()) 2696 { 2697 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler, 2698 nmiButtonLine, nmiButtonEvent); 2699 } 2700 } 2701 else if (nmiButtonConfig.type == ConfigType::DBUS) 2702 { 2703 static sdbusplus::bus::match_t nmiButtonEventMonitor = 2704 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler); 2705 } 2706 2707 // Request ID_BUTTON GPIO events 2708 if (idButtonConfig.type == ConfigType::GPIO) 2709 { 2710 if (!idButtonConfig.lineName.empty()) 2711 { 2712 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler, 2713 idButtonLine, idButtonEvent); 2714 } 2715 } 2716 else if (idButtonConfig.type == ConfigType::DBUS) 2717 { 2718 static sdbusplus::bus::match_t idButtonEventMonitor = 2719 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler); 2720 } 2721 2722 #ifdef USE_PLT_RST 2723 sdbusplus::bus::match_t pltRstMatch( 2724 *conn, 2725 "type='signal',interface='org.freedesktop.DBus.Properties',member='" 2726 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'", 2727 hostMiscHandler); 2728 #endif 2729 2730 // Request POST_COMPLETE GPIO events 2731 if (postCompleteConfig.type == ConfigType::GPIO) 2732 { 2733 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler, 2734 postCompleteLine, postCompleteEvent)) 2735 { 2736 return -1; 2737 } 2738 } 2739 else if (postCompleteConfig.type == ConfigType::DBUS) 2740 { 2741 static sdbusplus::bus::match_t postCompleteEventMonitor = 2742 power_control::dbusGPIOMatcher(postCompleteConfig, 2743 postCompleteHandler); 2744 } 2745 else 2746 { 2747 lg2::error( 2748 "postComplete name should be configured from json config file"); 2749 return -1; 2750 } 2751 2752 // initialize NMI_OUT GPIO. 2753 if (!nmiOutConfig.lineName.empty()) 2754 { 2755 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity, 2756 nmiOutLine); 2757 } 2758 2759 // Initialize POWER_OUT and RESET_OUT GPIO. 2760 gpiod::line line; 2761 if (!powerOutConfig.lineName.empty()) 2762 { 2763 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity, 2764 line)) 2765 { 2766 return -1; 2767 } 2768 } 2769 else 2770 { 2771 lg2::error("powerOut name should be configured from json config file"); 2772 return -1; 2773 } 2774 2775 if (!resetOutConfig.lineName.empty()) 2776 { 2777 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity, 2778 line)) 2779 { 2780 return -1; 2781 } 2782 } 2783 else 2784 { 2785 lg2::error("ResetOut name should be configured from json config file"); 2786 return -1; 2787 } 2788 // Release line 2789 line.reset(); 2790 2791 // Initialize the power state and operating system state 2792 powerState = PowerState::off; 2793 operatingSystemState = OperatingSystemStateStage::Inactive; 2794 // Check power good 2795 2796 if (powerOkConfig.type == ConfigType::GPIO) 2797 { 2798 if (psPowerOKLine.get_value() > 0 || 2799 (sioEnabled && 2800 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity))) 2801 { 2802 powerState = PowerState::on; 2803 } 2804 } 2805 else 2806 { 2807 if (getProperty(powerOkConfig)) 2808 { 2809 powerState = PowerState::on; 2810 } 2811 } 2812 // Check if we need to start the Power Restore policy 2813 if (powerState != PowerState::on) 2814 { 2815 powerRestore.run(); 2816 } 2817 2818 if (nmiOutLine) 2819 nmiSourcePropertyMonitor(); 2820 2821 lg2::info("Initializing power state."); 2822 logStateTransition(powerState); 2823 2824 // Power Control Service 2825 sdbusplus::asio::object_server hostServer = 2826 sdbusplus::asio::object_server(conn); 2827 2828 // Power Control Interface 2829 hostIface = 2830 hostServer.add_interface("/xyz/openbmc_project/state/host" + node, 2831 "xyz.openbmc_project.State.Host"); 2832 // Interface for IPMI/Redfish initiated host state transitions 2833 hostIface->register_property( 2834 "RequestedHostTransition", 2835 std::string("xyz.openbmc_project.State.Host.Transition.Off"), 2836 [](const std::string& requested, std::string& resp) { 2837 if (requested == "xyz.openbmc_project.State.Host.Transition.Off") 2838 { 2839 // if power button is masked, ignore this 2840 if (!powerButtonMask) 2841 { 2842 sendPowerControlEvent(Event::gracefulPowerOffRequest); 2843 addRestartCause(RestartCause::command); 2844 } 2845 else 2846 { 2847 lg2::info("Power Button Masked."); 2848 throw std::invalid_argument("Transition Request Masked"); 2849 return 0; 2850 } 2851 } 2852 else if (requested == 2853 "xyz.openbmc_project.State.Host.Transition.On") 2854 { 2855 // if power button is masked, ignore this 2856 if (!powerButtonMask) 2857 { 2858 sendPowerControlEvent(Event::powerOnRequest); 2859 addRestartCause(RestartCause::command); 2860 } 2861 else 2862 { 2863 lg2::info("Power Button Masked."); 2864 throw std::invalid_argument("Transition Request Masked"); 2865 return 0; 2866 } 2867 } 2868 else if (requested == 2869 "xyz.openbmc_project.State.Host.Transition.Reboot") 2870 { 2871 // if power button is masked, ignore this 2872 if (!powerButtonMask) 2873 { 2874 sendPowerControlEvent(Event::powerCycleRequest); 2875 addRestartCause(RestartCause::command); 2876 } 2877 else 2878 { 2879 lg2::info("Power Button Masked."); 2880 throw std::invalid_argument("Transition Request Masked"); 2881 return 0; 2882 } 2883 } 2884 else if ( 2885 requested == 2886 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot") 2887 { 2888 // if reset button is masked, ignore this 2889 if (!resetButtonMask) 2890 { 2891 sendPowerControlEvent(Event::gracefulPowerCycleRequest); 2892 addRestartCause(RestartCause::command); 2893 } 2894 else 2895 { 2896 lg2::info("Reset Button Masked."); 2897 throw std::invalid_argument("Transition Request Masked"); 2898 return 0; 2899 } 2900 } 2901 else if ( 2902 requested == 2903 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot") 2904 { 2905 // if reset button is masked, ignore this 2906 if (!resetButtonMask) 2907 { 2908 sendPowerControlEvent(Event::resetRequest); 2909 addRestartCause(RestartCause::command); 2910 } 2911 else 2912 { 2913 lg2::info("Reset Button Masked."); 2914 throw std::invalid_argument("Transition Request Masked"); 2915 return 0; 2916 } 2917 } 2918 else 2919 { 2920 lg2::error("Unrecognized host state transition request."); 2921 throw std::invalid_argument("Unrecognized Transition Request"); 2922 return 0; 2923 } 2924 resp = requested; 2925 return 1; 2926 }); 2927 hostIface->register_property("CurrentHostState", 2928 std::string(getHostState(powerState))); 2929 2930 hostIface->initialize(); 2931 2932 // Chassis Control Service 2933 sdbusplus::asio::object_server chassisServer = 2934 sdbusplus::asio::object_server(conn); 2935 2936 // Chassis Control Interface 2937 chassisIface = 2938 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node, 2939 "xyz.openbmc_project.State.Chassis"); 2940 2941 chassisIface->register_property( 2942 "RequestedPowerTransition", 2943 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"), 2944 [](const std::string& requested, std::string& resp) { 2945 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off") 2946 { 2947 // if power button is masked, ignore this 2948 if (!powerButtonMask) 2949 { 2950 sendPowerControlEvent(Event::powerOffRequest); 2951 addRestartCause(RestartCause::command); 2952 } 2953 else 2954 { 2955 lg2::info("Power Button Masked."); 2956 throw std::invalid_argument("Transition Request Masked"); 2957 return 0; 2958 } 2959 } 2960 else if (requested == 2961 "xyz.openbmc_project.State.Chassis.Transition.On") 2962 { 2963 // if power button is masked, ignore this 2964 if (!powerButtonMask) 2965 { 2966 sendPowerControlEvent(Event::powerOnRequest); 2967 addRestartCause(RestartCause::command); 2968 } 2969 else 2970 { 2971 lg2::info("Power Button Masked."); 2972 throw std::invalid_argument("Transition Request Masked"); 2973 return 0; 2974 } 2975 } 2976 else if (requested == 2977 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 2978 { 2979 // if power button is masked, ignore this 2980 if (!powerButtonMask) 2981 { 2982 sendPowerControlEvent(Event::powerCycleRequest); 2983 addRestartCause(RestartCause::command); 2984 } 2985 else 2986 { 2987 lg2::info("Power Button Masked."); 2988 throw std::invalid_argument("Transition Request Masked"); 2989 return 0; 2990 } 2991 } 2992 else 2993 { 2994 lg2::error("Unrecognized chassis state transition request."); 2995 throw std::invalid_argument("Unrecognized Transition Request"); 2996 return 0; 2997 } 2998 resp = requested; 2999 return 1; 3000 }); 3001 chassisIface->register_property("CurrentPowerState", 3002 std::string(getChassisState(powerState))); 3003 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs()); 3004 3005 chassisIface->initialize(); 3006 3007 #ifdef CHASSIS_SYSTEM_RESET 3008 // Chassis System Service 3009 sdbusplus::asio::object_server chassisSysServer = 3010 sdbusplus::asio::object_server(conn); 3011 3012 // Chassis System Interface 3013 chassisSysIface = chassisSysServer.add_interface( 3014 "/xyz/openbmc_project/state/chassis_system0", 3015 "xyz.openbmc_project.State.Chassis"); 3016 3017 chassisSysIface->register_property( 3018 "RequestedPowerTransition", 3019 std::string("xyz.openbmc_project.State.Chassis.Transition.On"), 3020 [](const std::string& requested, std::string& resp) { 3021 if (requested == 3022 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 3023 { 3024 systemReset(); 3025 addRestartCause(RestartCause::command); 3026 } 3027 else 3028 { 3029 lg2::error( 3030 "Unrecognized chassis system state transition request."); 3031 throw std::invalid_argument("Unrecognized Transition Request"); 3032 return 0; 3033 } 3034 resp = requested; 3035 return 1; 3036 }); 3037 chassisSysIface->register_property( 3038 "CurrentPowerState", std::string(getChassisState(powerState))); 3039 chassisSysIface->register_property("LastStateChangeTime", 3040 getCurrentTimeMs()); 3041 3042 chassisSysIface->initialize(); 3043 3044 if (!slotPowerConfig.lineName.empty()) 3045 { 3046 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine)) 3047 { 3048 return -1; 3049 } 3050 3051 slotPowerState = SlotPowerState::off; 3052 if (slotPowerLine.get_value() > 0) 3053 { 3054 slotPowerState = SlotPowerState::on; 3055 } 3056 3057 chassisSlotIface = chassisSysServer.add_interface( 3058 "/xyz/openbmc_project/state/chassis_system" + node, 3059 "xyz.openbmc_project.State.Chassis"); 3060 chassisSlotIface->register_property( 3061 "RequestedPowerTransition", 3062 std::string("xyz.openbmc_project.State.Chassis.Transition.On"), 3063 [](const std::string& requested, std::string& resp) { 3064 if (requested == 3065 "xyz.openbmc_project.State.Chassis.Transition.On") 3066 { 3067 slotPowerOn(); 3068 } 3069 else if (requested == 3070 "xyz.openbmc_project.State.Chassis.Transition.Off") 3071 { 3072 slotPowerOff(); 3073 } 3074 else if ( 3075 requested == 3076 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 3077 { 3078 slotPowerCycle(); 3079 } 3080 else 3081 { 3082 lg2::error( 3083 "Unrecognized chassis system state transition request.\n"); 3084 throw std::invalid_argument( 3085 "Unrecognized Transition Request"); 3086 return 0; 3087 } 3088 resp = requested; 3089 return 1; 3090 }); 3091 chassisSlotIface->register_property( 3092 "CurrentPowerState", std::string(getSlotState(slotPowerState))); 3093 chassisSlotIface->register_property("LastStateChangeTime", 3094 getCurrentTimeMs()); 3095 chassisSlotIface->initialize(); 3096 } 3097 #endif 3098 // Buttons Service 3099 sdbusplus::asio::object_server buttonsServer = 3100 sdbusplus::asio::object_server(conn); 3101 3102 if (!powerButtonConfig.lineName.empty()) 3103 { 3104 // Power Button Interface 3105 power_control::powerButtonIface = buttonsServer.add_interface( 3106 "/xyz/openbmc_project/chassis/buttons/power", 3107 "xyz.openbmc_project.Chassis.Buttons"); 3108 3109 powerButtonIface->register_property( 3110 "ButtonMasked", false, [](const bool requested, bool& current) { 3111 if (requested) 3112 { 3113 if (powerButtonMask) 3114 { 3115 return 1; 3116 } 3117 if (!setGPIOOutput(powerOutConfig.lineName, 3118 !powerOutConfig.polarity, 3119 powerButtonMask)) 3120 { 3121 throw std::runtime_error("Failed to request GPIO"); 3122 return 0; 3123 } 3124 lg2::info("Power Button Masked."); 3125 } 3126 else 3127 { 3128 if (!powerButtonMask) 3129 { 3130 return 1; 3131 } 3132 lg2::info("Power Button Un-masked"); 3133 powerButtonMask.reset(); 3134 } 3135 // Update the mask setting 3136 current = requested; 3137 return 1; 3138 }); 3139 3140 // Check power button state 3141 bool powerButtonPressed; 3142 if (powerButtonConfig.type == ConfigType::GPIO) 3143 { 3144 powerButtonPressed = powerButtonLine.get_value() == 0; 3145 } 3146 else 3147 { 3148 powerButtonPressed = getProperty(powerButtonConfig) == 0; 3149 } 3150 3151 powerButtonIface->register_property("ButtonPressed", 3152 powerButtonPressed); 3153 3154 powerButtonIface->initialize(); 3155 } 3156 3157 if (!resetButtonConfig.lineName.empty()) 3158 { 3159 // Reset Button Interface 3160 3161 resetButtonIface = buttonsServer.add_interface( 3162 "/xyz/openbmc_project/chassis/buttons/reset", 3163 "xyz.openbmc_project.Chassis.Buttons"); 3164 3165 resetButtonIface->register_property( 3166 "ButtonMasked", false, [](const bool requested, bool& current) { 3167 if (requested) 3168 { 3169 if (resetButtonMask) 3170 { 3171 return 1; 3172 } 3173 if (!setGPIOOutput(resetOutConfig.lineName, 3174 !resetOutConfig.polarity, 3175 resetButtonMask)) 3176 { 3177 throw std::runtime_error("Failed to request GPIO"); 3178 return 0; 3179 } 3180 lg2::info("Reset Button Masked."); 3181 } 3182 else 3183 { 3184 if (!resetButtonMask) 3185 { 3186 return 1; 3187 } 3188 lg2::info("Reset Button Un-masked"); 3189 resetButtonMask.reset(); 3190 } 3191 // Update the mask setting 3192 current = requested; 3193 return 1; 3194 }); 3195 3196 // Check reset button state 3197 bool resetButtonPressed; 3198 if (resetButtonConfig.type == ConfigType::GPIO) 3199 { 3200 resetButtonPressed = resetButtonLine.get_value() == 0; 3201 } 3202 else 3203 { 3204 resetButtonPressed = getProperty(resetButtonConfig) == 0; 3205 } 3206 3207 resetButtonIface->register_property("ButtonPressed", 3208 resetButtonPressed); 3209 3210 resetButtonIface->initialize(); 3211 } 3212 3213 if (nmiButtonLine) 3214 { 3215 // NMI Button Interface 3216 nmiButtonIface = buttonsServer.add_interface( 3217 "/xyz/openbmc_project/chassis/buttons/nmi", 3218 "xyz.openbmc_project.Chassis.Buttons"); 3219 3220 nmiButtonIface->register_property( 3221 "ButtonMasked", false, [](const bool requested, bool& current) { 3222 if (nmiButtonMasked == requested) 3223 { 3224 // NMI button mask is already set as requested, so no change 3225 return 1; 3226 } 3227 if (requested) 3228 { 3229 lg2::info("NMI Button Masked."); 3230 nmiButtonMasked = true; 3231 } 3232 else 3233 { 3234 lg2::info("NMI Button Un-masked."); 3235 nmiButtonMasked = false; 3236 } 3237 // Update the mask setting 3238 current = nmiButtonMasked; 3239 return 1; 3240 }); 3241 3242 // Check NMI button state 3243 bool nmiButtonPressed; 3244 if (nmiButtonConfig.type == ConfigType::GPIO) 3245 { 3246 nmiButtonPressed = nmiButtonLine.get_value() == 0; 3247 } 3248 else 3249 { 3250 nmiButtonPressed = getProperty(nmiButtonConfig) == 0; 3251 } 3252 3253 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed); 3254 3255 nmiButtonIface->initialize(); 3256 } 3257 3258 if (nmiOutLine) 3259 { 3260 // NMI out Service 3261 sdbusplus::asio::object_server nmiOutServer = 3262 sdbusplus::asio::object_server(conn); 3263 3264 // NMI out Interface 3265 nmiOutIface = nmiOutServer.add_interface( 3266 "/xyz/openbmc_project/control/host" + node + "/nmi", 3267 "xyz.openbmc_project.Control.Host.NMI"); 3268 nmiOutIface->register_method("NMI", nmiReset); 3269 nmiOutIface->initialize(); 3270 } 3271 3272 if (idButtonLine) 3273 { 3274 // ID Button Interface 3275 idButtonIface = buttonsServer.add_interface( 3276 "/xyz/openbmc_project/chassis/buttons/id", 3277 "xyz.openbmc_project.Chassis.Buttons"); 3278 3279 // Check ID button state 3280 bool idButtonPressed; 3281 if (idButtonConfig.type == ConfigType::GPIO) 3282 { 3283 idButtonPressed = idButtonLine.get_value() == 0; 3284 } 3285 else 3286 { 3287 idButtonPressed = getProperty(idButtonConfig) == 0; 3288 } 3289 3290 idButtonIface->register_property("ButtonPressed", idButtonPressed); 3291 3292 idButtonIface->initialize(); 3293 } 3294 3295 // OS State Service 3296 sdbusplus::asio::object_server osServer = 3297 sdbusplus::asio::object_server(conn); 3298 3299 // OS State Interface 3300 osIface = osServer.add_interface( 3301 "/xyz/openbmc_project/state/os", 3302 "xyz.openbmc_project.State.OperatingSystem.Status"); 3303 3304 // Get the initial OS state based on POST complete 3305 // 0: Asserted, OS state is "Standby" (ready to boot) 3306 // 1: De-Asserted, OS state is "Inactive" 3307 OperatingSystemStateStage osState; 3308 if (postCompleteConfig.type == ConfigType::GPIO) 3309 { 3310 osState = postCompleteLine.get_value() > 0 3311 ? OperatingSystemStateStage::Inactive 3312 : OperatingSystemStateStage::Standby; 3313 } 3314 else 3315 { 3316 osState = getProperty(postCompleteConfig) > 0 3317 ? OperatingSystemStateStage::Inactive 3318 : OperatingSystemStateStage::Standby; 3319 } 3320 3321 osIface->register_property( 3322 "OperatingSystemState", 3323 std::string(getOperatingSystemStateStage(osState))); 3324 3325 osIface->initialize(); 3326 3327 // Restart Cause Service 3328 sdbusplus::asio::object_server restartCauseServer = 3329 sdbusplus::asio::object_server(conn); 3330 3331 // Restart Cause Interface 3332 restartCauseIface = restartCauseServer.add_interface( 3333 "/xyz/openbmc_project/control/host" + node + "/restart_cause", 3334 "xyz.openbmc_project.Control.Host.RestartCause"); 3335 3336 restartCauseIface->register_property( 3337 "RestartCause", 3338 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown")); 3339 3340 restartCauseIface->register_property( 3341 "RequestedRestartCause", 3342 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"), 3343 [](const std::string& requested, std::string& resp) { 3344 if (requested == 3345 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer") 3346 { 3347 addRestartCause(RestartCause::watchdog); 3348 } 3349 else 3350 { 3351 throw std::invalid_argument( 3352 "Unrecognized RestartCause Request"); 3353 return 0; 3354 } 3355 3356 lg2::info("RestartCause requested: {RESTART_CAUSE}", 3357 "RESTART_CAUSE", requested); 3358 resp = requested; 3359 return 1; 3360 }); 3361 3362 restartCauseIface->initialize(); 3363 3364 currentHostStateMonitor(); 3365 3366 io.run(); 3367 3368 return 0; 3369 } 3370