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