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 setPowerState(PowerState::waitForPSPowerOK); 1790 break; 1791 case Event::sioPowerGoodAssert: 1792 setPowerState(PowerState::on); 1793 break; 1794 case Event::powerButtonPressed: 1795 psPowerOKWatchdogTimerStart(); 1796 setPowerState(PowerState::waitForPSPowerOK); 1797 break; 1798 case Event::powerOnRequest: 1799 psPowerOKWatchdogTimerStart(); 1800 setPowerState(PowerState::waitForPSPowerOK); 1801 powerOn(); 1802 break; 1803 default: 1804 lg2::info("No action taken."); 1805 break; 1806 } 1807 } 1808 1809 static void powerStateTransitionToOff(const Event event) 1810 { 1811 logEvent(__FUNCTION__, event); 1812 switch (event) 1813 { 1814 case Event::psPowerOKDeAssert: 1815 // Cancel any GPIO assertions held during the transition 1816 gpioAssertTimer.cancel(); 1817 setPowerState(PowerState::off); 1818 break; 1819 default: 1820 lg2::info("No action taken."); 1821 break; 1822 } 1823 } 1824 1825 static void powerStateGracefulTransitionToOff(const Event event) 1826 { 1827 logEvent(__FUNCTION__, event); 1828 switch (event) 1829 { 1830 case Event::psPowerOKDeAssert: 1831 gracefulPowerOffTimer.cancel(); 1832 setPowerState(PowerState::off); 1833 break; 1834 case Event::gracefulPowerOffTimerExpired: 1835 setPowerState(PowerState::on); 1836 break; 1837 case Event::powerOffRequest: 1838 gracefulPowerOffTimer.cancel(); 1839 setPowerState(PowerState::transitionToOff); 1840 forcePowerOff(); 1841 break; 1842 case Event::powerCycleRequest: 1843 gracefulPowerOffTimer.cancel(); 1844 setPowerState(PowerState::transitionToCycleOff); 1845 forcePowerOff(); 1846 break; 1847 case Event::resetRequest: 1848 gracefulPowerOffTimer.cancel(); 1849 setPowerState(PowerState::on); 1850 reset(); 1851 break; 1852 default: 1853 lg2::info("No action taken."); 1854 break; 1855 } 1856 } 1857 1858 static void powerStateCycleOff(const Event event) 1859 { 1860 logEvent(__FUNCTION__, event); 1861 switch (event) 1862 { 1863 case Event::psPowerOKAssert: 1864 { 1865 powerCycleTimer.cancel(); 1866 if (sioEnabled == true) 1867 { 1868 sioPowerGoodWatchdogTimerStart(); 1869 setPowerState(PowerState::waitForSIOPowerGood); 1870 } 1871 else 1872 { 1873 setPowerState(PowerState::on); 1874 } 1875 break; 1876 } 1877 case Event::sioS5DeAssert: 1878 powerCycleTimer.cancel(); 1879 setPowerState(PowerState::waitForPSPowerOK); 1880 break; 1881 case Event::powerButtonPressed: 1882 powerCycleTimer.cancel(); 1883 psPowerOKWatchdogTimerStart(); 1884 setPowerState(PowerState::waitForPSPowerOK); 1885 break; 1886 case Event::powerCycleTimerExpired: 1887 psPowerOKWatchdogTimerStart(); 1888 setPowerState(PowerState::waitForPSPowerOK); 1889 powerOn(); 1890 break; 1891 default: 1892 lg2::info("No action taken."); 1893 break; 1894 } 1895 } 1896 1897 static void powerStateTransitionToCycleOff(const Event event) 1898 { 1899 logEvent(__FUNCTION__, event); 1900 switch (event) 1901 { 1902 case Event::psPowerOKDeAssert: 1903 // Cancel any GPIO assertions held during the transition 1904 gpioAssertTimer.cancel(); 1905 setPowerState(PowerState::cycleOff); 1906 powerCycleTimerStart(); 1907 break; 1908 default: 1909 lg2::info("No action taken."); 1910 break; 1911 } 1912 } 1913 1914 static void powerStateGracefulTransitionToCycleOff(const Event event) 1915 { 1916 logEvent(__FUNCTION__, event); 1917 switch (event) 1918 { 1919 case Event::psPowerOKDeAssert: 1920 gracefulPowerOffTimer.cancel(); 1921 setPowerState(PowerState::cycleOff); 1922 powerCycleTimerStart(); 1923 break; 1924 case Event::gracefulPowerOffTimerExpired: 1925 setPowerState(PowerState::on); 1926 break; 1927 case Event::powerOffRequest: 1928 gracefulPowerOffTimer.cancel(); 1929 setPowerState(PowerState::transitionToOff); 1930 forcePowerOff(); 1931 break; 1932 case Event::powerCycleRequest: 1933 gracefulPowerOffTimer.cancel(); 1934 setPowerState(PowerState::transitionToCycleOff); 1935 forcePowerOff(); 1936 break; 1937 case Event::resetRequest: 1938 gracefulPowerOffTimer.cancel(); 1939 setPowerState(PowerState::on); 1940 reset(); 1941 break; 1942 default: 1943 lg2::info("No action taken."); 1944 break; 1945 } 1946 } 1947 1948 static void powerStateCheckForWarmReset(const Event event) 1949 { 1950 logEvent(__FUNCTION__, event); 1951 switch (event) 1952 { 1953 case Event::sioS5Assert: 1954 warmResetCheckTimer.cancel(); 1955 setPowerState(PowerState::transitionToOff); 1956 break; 1957 case Event::warmResetDetected: 1958 setPowerState(PowerState::on); 1959 break; 1960 case Event::psPowerOKDeAssert: 1961 warmResetCheckTimer.cancel(); 1962 setPowerState(PowerState::off); 1963 // DC power is unexpectedly lost, beep 1964 beep(beepPowerFail); 1965 break; 1966 default: 1967 lg2::info("No action taken."); 1968 break; 1969 } 1970 } 1971 1972 static void psPowerOKHandler(bool state) 1973 { 1974 Event powerControlEvent = 1975 state ? Event::psPowerOKAssert : Event::psPowerOKDeAssert; 1976 sendPowerControlEvent(powerControlEvent); 1977 } 1978 1979 static void sioPowerGoodHandler(bool state) 1980 { 1981 Event powerControlEvent = 1982 state ? Event::sioPowerGoodAssert : Event::sioPowerGoodDeAssert; 1983 sendPowerControlEvent(powerControlEvent); 1984 } 1985 1986 static void sioOnControlHandler(bool state) 1987 { 1988 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE", 1989 static_cast<int>(state)); 1990 } 1991 1992 static void sioS5Handler(bool state) 1993 { 1994 Event powerControlEvent = state ? Event::sioS5DeAssert : Event::sioS5Assert; 1995 sendPowerControlEvent(powerControlEvent); 1996 } 1997 1998 static void powerButtonHandler(bool state) 1999 { 2000 powerButtonIface->set_property("ButtonPressed", !state); 2001 if (!state) 2002 { 2003 powerButtonPressLog(); 2004 if (!powerButtonMask) 2005 { 2006 sendPowerControlEvent(Event::powerButtonPressed); 2007 addRestartCause(RestartCause::powerButton); 2008 } 2009 else 2010 { 2011 lg2::info("power button press masked"); 2012 } 2013 } 2014 } 2015 2016 static void resetButtonHandler(bool state) 2017 { 2018 resetButtonIface->set_property("ButtonPressed", !state); 2019 if (!state) 2020 { 2021 resetButtonPressLog(); 2022 if (!resetButtonMask) 2023 { 2024 sendPowerControlEvent(Event::resetButtonPressed); 2025 addRestartCause(RestartCause::resetButton); 2026 } 2027 else 2028 { 2029 lg2::info("reset button press masked"); 2030 } 2031 } 2032 } 2033 2034 #ifdef CHASSIS_SYSTEM_RESET 2035 static constexpr auto systemdBusname = "org.freedesktop.systemd1"; 2036 static constexpr auto systemdPath = "/org/freedesktop/systemd1"; 2037 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager"; 2038 static constexpr auto systemTargetName = "chassis-system-reset.target"; 2039 2040 void systemReset() 2041 { 2042 conn->async_method_call( 2043 [](boost::system::error_code ec) { 2044 if (ec) 2045 { 2046 lg2::error("Failed to call chassis system reset: {ERR}", "ERR", 2047 ec.message()); 2048 } 2049 }, 2050 systemdBusname, systemdPath, systemdInterface, "StartUnit", 2051 systemTargetName, "replace"); 2052 } 2053 #endif 2054 2055 static void nmiSetEnableProperty(bool value) 2056 { 2057 conn->async_method_call( 2058 [](boost::system::error_code ec) { 2059 if (ec) 2060 { 2061 lg2::error("failed to set NMI source"); 2062 } 2063 }, 2064 "xyz.openbmc_project.Settings", 2065 "/xyz/openbmc_project/Chassis/Control/NMISource", 2066 "org.freedesktop.DBus.Properties", "Set", 2067 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled", 2068 std::variant<bool>{value}); 2069 } 2070 2071 static void nmiReset(void) 2072 { 2073 const static constexpr int nmiOutPulseTimeMs = 200; 2074 2075 lg2::info("NMI out action"); 2076 nmiOutLine.set_value(!nmiOutConfig.polarity); 2077 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", 2078 nmiOutConfig.lineName, "GPIO_VALUE", !nmiOutConfig.polarity); 2079 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs)); 2080 gpioAssertTimer.async_wait([](const boost::system::error_code ec) { 2081 // restore the NMI_OUT GPIO line back to the opposite value 2082 nmiOutLine.set_value(nmiOutConfig.polarity); 2083 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName); 2084 if (ec) 2085 { 2086 // operation_aborted is expected if timer is canceled before 2087 // completion. 2088 if (ec != boost::asio::error::operation_aborted) 2089 { 2090 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}", 2091 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG", 2092 ec.message()); 2093 } 2094 } 2095 }); 2096 // log to redfish 2097 nmiDiagIntLog(); 2098 lg2::info("NMI out action completed"); 2099 // reset Enable Property 2100 nmiSetEnableProperty(false); 2101 } 2102 2103 static void nmiSourcePropertyMonitor(void) 2104 { 2105 lg2::info("NMI Source Property Monitor"); 2106 2107 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch = 2108 std::make_unique<sdbusplus::bus::match_t>( 2109 *conn, 2110 "type='signal',interface='org.freedesktop.DBus.Properties'," 2111 "member='PropertiesChanged'," 2112 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'", 2113 [](sdbusplus::message_t& msg) { 2114 std::string interfaceName; 2115 boost::container::flat_map<std::string, 2116 std::variant<bool, std::string>> 2117 propertiesChanged; 2118 std::string state; 2119 bool value = true; 2120 try 2121 { 2122 msg.read(interfaceName, propertiesChanged); 2123 if (propertiesChanged.begin()->first == "Enabled") 2124 { 2125 value = 2126 std::get<bool>(propertiesChanged.begin()->second); 2127 lg2::info( 2128 "NMI Enabled propertiesChanged value: {VALUE}", 2129 "VALUE", value); 2130 nmiEnabled = value; 2131 if (nmiEnabled) 2132 { 2133 nmiReset(); 2134 } 2135 } 2136 } 2137 catch (const std::exception& e) 2138 { 2139 lg2::error("Unable to read NMI source: {ERROR}", "ERROR", 2140 e); 2141 return; 2142 } 2143 }); 2144 } 2145 2146 static void setNmiSource() 2147 { 2148 conn->async_method_call( 2149 [](boost::system::error_code ec) { 2150 if (ec) 2151 { 2152 lg2::error("failed to set NMI source"); 2153 } 2154 }, 2155 "xyz.openbmc_project.Settings", 2156 "/xyz/openbmc_project/Chassis/Control/NMISource", 2157 "org.freedesktop.DBus.Properties", "Set", 2158 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource", 2159 std::variant<std::string>{ 2160 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FpBtn"}); 2161 // set Enable Property 2162 nmiSetEnableProperty(true); 2163 } 2164 2165 static void nmiButtonHandler(bool state) 2166 { 2167 nmiButtonIface->set_property("ButtonPressed", !state); 2168 if (!state) 2169 { 2170 nmiButtonPressLog(); 2171 if (nmiButtonMasked) 2172 { 2173 lg2::info("NMI button press masked"); 2174 } 2175 else 2176 { 2177 setNmiSource(); 2178 } 2179 } 2180 } 2181 2182 static void idButtonHandler(bool state) 2183 { 2184 idButtonIface->set_property("ButtonPressed", !state); 2185 } 2186 2187 static void pltRstHandler(bool pltRst) 2188 { 2189 if (pltRst) 2190 { 2191 sendPowerControlEvent(Event::pltRstDeAssert); 2192 } 2193 else 2194 { 2195 sendPowerControlEvent(Event::pltRstAssert); 2196 } 2197 } 2198 2199 [[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg) 2200 { 2201 std::string interfaceName; 2202 boost::container::flat_map<std::string, std::variant<bool>> 2203 propertiesChanged; 2204 try 2205 { 2206 msg.read(interfaceName, propertiesChanged); 2207 } 2208 catch (const std::exception& e) 2209 { 2210 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e); 2211 return; 2212 } 2213 if (propertiesChanged.empty()) 2214 { 2215 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received"); 2216 return; 2217 } 2218 2219 for (auto& [property, value] : propertiesChanged) 2220 { 2221 if (property == "ESpiPlatformReset") 2222 { 2223 bool* pltRst = std::get_if<bool>(&value); 2224 if (pltRst == nullptr) 2225 { 2226 lg2::error("{PROPERTY} property invalid", "PROPERTY", property); 2227 return; 2228 } 2229 pltRstHandler(*pltRst); 2230 } 2231 } 2232 } 2233 2234 static void postCompleteHandler(bool state) 2235 { 2236 if (!state) 2237 { 2238 sendPowerControlEvent(Event::postCompleteAssert); 2239 setOperatingSystemState(OperatingSystemStateStage::Standby); 2240 } 2241 else 2242 { 2243 sendPowerControlEvent(Event::postCompleteDeAssert); 2244 setOperatingSystemState(OperatingSystemStateStage::Inactive); 2245 } 2246 } 2247 2248 static int loadConfigValues() 2249 { 2250 const std::string configFilePath = 2251 "/usr/share/x86-power-control/power-config-host" + power_control::node + 2252 ".json"; 2253 std::ifstream configFile(configFilePath.c_str()); 2254 if (!configFile.is_open()) 2255 { 2256 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'", 2257 "PATH", configFilePath); 2258 return -1; 2259 } 2260 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true); 2261 2262 if (jsonData.is_discarded()) 2263 { 2264 lg2::error("Power config readings JSON parser failure"); 2265 return -1; 2266 } 2267 auto gpios = jsonData["gpio_configs"]; 2268 auto timers = jsonData["timing_configs"]; 2269 2270 ConfigData* tempGpioData; 2271 2272 for (nlohmann::json& gpioConfig : gpios) 2273 { 2274 if (!gpioConfig.contains("Name")) 2275 { 2276 lg2::error("The 'Name' field must be defined in Json file"); 2277 return -1; 2278 } 2279 2280 // Iterate through the powersignal map to check if the gpio json config 2281 // entry is valid 2282 std::string gpioName = gpioConfig["Name"]; 2283 auto signalMapIter = powerSignalMap.find(gpioName); 2284 if (signalMapIter == powerSignalMap.end()) 2285 { 2286 lg2::error( 2287 "{GPIO_NAME} is not a recognized power-control signal name", 2288 "GPIO_NAME", gpioName); 2289 return -1; 2290 } 2291 2292 // assign the power signal name to the corresponding structure reference 2293 // from map then fillup the structure with coressponding json config 2294 // value 2295 tempGpioData = signalMapIter->second; 2296 tempGpioData->name = gpioName; 2297 2298 if (!gpioConfig.contains("Type")) 2299 { 2300 lg2::error("The \'Type\' field must be defined in Json file"); 2301 return -1; 2302 } 2303 2304 std::string signalType = gpioConfig["Type"]; 2305 if (signalType == "GPIO") 2306 { 2307 tempGpioData->type = ConfigType::GPIO; 2308 } 2309 else if (signalType == "DBUS") 2310 { 2311 tempGpioData->type = ConfigType::DBUS; 2312 } 2313 else 2314 { 2315 lg2::error("{TYPE} is not a recognized power-control signal type", 2316 "TYPE", signalType); 2317 return -1; 2318 } 2319 2320 if (tempGpioData->type == ConfigType::GPIO) 2321 { 2322 if (gpioConfig.contains("LineName")) 2323 { 2324 tempGpioData->lineName = gpioConfig["LineName"]; 2325 } 2326 else 2327 { 2328 lg2::error( 2329 "The \'LineName\' field must be defined for GPIO configuration"); 2330 return -1; 2331 } 2332 if (gpioConfig.contains("Polarity")) 2333 { 2334 std::string polarity = gpioConfig["Polarity"]; 2335 if (polarity == "ActiveLow") 2336 { 2337 tempGpioData->polarity = false; 2338 } 2339 else if (polarity == "ActiveHigh") 2340 { 2341 tempGpioData->polarity = true; 2342 } 2343 else 2344 { 2345 lg2::error( 2346 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}", 2347 "POLARITY", polarity); 2348 return -1; 2349 } 2350 } 2351 else 2352 { 2353 lg2::error("Polarity field not found for {GPIO_NAME}", 2354 "GPIO_NAME", tempGpioData->lineName); 2355 return -1; 2356 } 2357 } 2358 else 2359 { 2360 // if dbus based gpio config is defined read and update the dbus 2361 // params corresponding to the gpio config instance 2362 for (auto& [key, dbusParamName] : dbusParams) 2363 { 2364 if (!gpioConfig.contains(dbusParamName)) 2365 { 2366 lg2::error( 2367 "The {DBUS_NAME} field must be defined for Dbus configuration ", 2368 "DBUS_NAME", dbusParamName); 2369 return -1; 2370 } 2371 } 2372 tempGpioData->dbusName = 2373 gpioConfig[dbusParams[DbusConfigType::name]]; 2374 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]]; 2375 tempGpioData->interface = 2376 gpioConfig[dbusParams[DbusConfigType::interface]]; 2377 tempGpioData->lineName = 2378 gpioConfig[dbusParams[DbusConfigType::property]]; 2379 } 2380 } 2381 2382 // read and store the timer values from json config to Timer Map 2383 for (auto& [key, timerValue] : TimerMap) 2384 { 2385 if (timers.contains(key.c_str())) 2386 { 2387 timerValue = timers[key.c_str()]; 2388 } 2389 } 2390 2391 return 0; 2392 } 2393 2394 static bool getDbusMsgGPIOState(sdbusplus::message_t& msg, 2395 const std::string& lineName, bool& value) 2396 { 2397 std::string thresholdInterface; 2398 std::string event; 2399 boost::container::flat_map<std::string, std::variant<bool>> 2400 propertiesChanged; 2401 try 2402 { 2403 msg.read(thresholdInterface, propertiesChanged); 2404 if (propertiesChanged.empty()) 2405 { 2406 return false; 2407 } 2408 2409 event = propertiesChanged.begin()->first; 2410 if (event.empty() || event != lineName) 2411 { 2412 return false; 2413 } 2414 2415 value = std::get<bool>(propertiesChanged.begin()->second); 2416 return true; 2417 } 2418 catch (const std::exception& e) 2419 { 2420 lg2::error( 2421 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}", 2422 "DBUS_NAME", lineName, "ERROR", e); 2423 return false; 2424 } 2425 } 2426 2427 static sdbusplus::bus::match_t 2428 dbusGPIOMatcher(const ConfigData& cfg, std::function<void(bool)> onMatch) 2429 { 2430 auto pulseEventMatcherCallback = [&cfg, 2431 onMatch](sdbusplus::message_t& msg) { 2432 bool value = false; 2433 if (!getDbusMsgGPIOState(msg, cfg.lineName, value)) 2434 { 2435 return; 2436 } 2437 onMatch(value); 2438 }; 2439 2440 return sdbusplus::bus::match_t( 2441 static_cast<sdbusplus::bus_t&>(*conn), 2442 "type='signal',interface='org.freedesktop.DBus.Properties',member='" 2443 "PropertiesChanged',arg0='" + 2444 cfg.interface + "'", 2445 std::move(pulseEventMatcherCallback)); 2446 } 2447 2448 int getProperty(ConfigData& configData) 2449 { 2450 auto method = conn->new_method_call( 2451 configData.dbusName.c_str(), configData.path.c_str(), 2452 "org.freedesktop.DBus.Properties", "Get"); 2453 method.append(configData.interface.c_str(), configData.lineName.c_str()); 2454 2455 auto reply = conn->call(method); 2456 if (reply.is_method_error()) 2457 { 2458 lg2::error( 2459 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}", 2460 "PROPERTY", configData.lineName, "INTERFACE", configData.interface, 2461 "PATH", configData.path); 2462 return -1; 2463 } 2464 std::variant<bool> resp; 2465 reply.read(resp); 2466 auto respValue = std::get_if<bool>(&resp); 2467 if (!respValue) 2468 { 2469 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type", 2470 "PROPERTY", configData.lineName); 2471 return -1; 2472 } 2473 return (*respValue); 2474 } 2475 } // namespace power_control 2476 2477 int main(int argc, char* argv[]) 2478 { 2479 using namespace power_control; 2480 2481 if (argc > 1) 2482 { 2483 node = argv[1]; 2484 } 2485 lg2::info("Start Chassis power control service for host : {NODE}", "NODE", 2486 node); 2487 2488 conn = std::make_shared<sdbusplus::asio::connection>(io); 2489 2490 // Load GPIO's through json config file 2491 if (loadConfigValues() == -1) 2492 { 2493 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node); 2494 } 2495 /* Currently for single host based systems additional busname is added 2496 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0. 2497 Going forward for single hosts the old bus name without zero numbering 2498 will be removed when all other applications adapted to the 2499 bus name with zero numbering (xyz.openbmc_project.State.Host0). */ 2500 2501 if (node == "0") 2502 { 2503 // Request all the dbus names 2504 conn->request_name(hostDbusName.c_str()); 2505 conn->request_name(chassisDbusName.c_str()); 2506 conn->request_name(osDbusName.c_str()); 2507 conn->request_name(buttonDbusName.c_str()); 2508 conn->request_name(nmiDbusName.c_str()); 2509 conn->request_name(rstCauseDbusName.c_str()); 2510 } 2511 2512 hostDbusName += node; 2513 chassisDbusName += node; 2514 osDbusName += node; 2515 buttonDbusName += node; 2516 nmiDbusName += node; 2517 rstCauseDbusName += node; 2518 2519 // Request all the dbus names 2520 conn->request_name(hostDbusName.c_str()); 2521 conn->request_name(chassisDbusName.c_str()); 2522 conn->request_name(osDbusName.c_str()); 2523 conn->request_name(buttonDbusName.c_str()); 2524 conn->request_name(nmiDbusName.c_str()); 2525 conn->request_name(rstCauseDbusName.c_str()); 2526 2527 if (sioPwrGoodConfig.lineName.empty() || 2528 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty()) 2529 { 2530 sioEnabled = false; 2531 lg2::info("SIO control GPIOs not defined, disable SIO support."); 2532 } 2533 2534 // Request PS_PWROK GPIO events 2535 if (powerOkConfig.type == ConfigType::GPIO) 2536 { 2537 if (!requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler, 2538 psPowerOKLine, psPowerOKEvent)) 2539 { 2540 return -1; 2541 } 2542 } 2543 else if (powerOkConfig.type == ConfigType::DBUS) 2544 { 2545 2546 static sdbusplus::bus::match_t powerOkEventMonitor = 2547 power_control::dbusGPIOMatcher(powerOkConfig, psPowerOKHandler); 2548 } 2549 else 2550 { 2551 lg2::error("PowerOk name should be configured from json config file"); 2552 return -1; 2553 } 2554 2555 if (sioEnabled == true) 2556 { 2557 // Request SIO_POWER_GOOD GPIO events 2558 if (sioPwrGoodConfig.type == ConfigType::GPIO) 2559 { 2560 if (!requestGPIOEvents(sioPwrGoodConfig.lineName, 2561 sioPowerGoodHandler, sioPowerGoodLine, 2562 sioPowerGoodEvent)) 2563 { 2564 return -1; 2565 } 2566 } 2567 else if (sioPwrGoodConfig.type == ConfigType::DBUS) 2568 { 2569 static sdbusplus::bus::match_t sioPwrGoodEventMonitor = 2570 power_control::dbusGPIOMatcher(sioPwrGoodConfig, 2571 sioPowerGoodHandler); 2572 } 2573 else 2574 { 2575 lg2::error( 2576 "sioPwrGood name should be configured from json config file"); 2577 return -1; 2578 } 2579 2580 // Request SIO_ONCONTROL GPIO events 2581 if (sioOnControlConfig.type == ConfigType::GPIO) 2582 { 2583 if (!requestGPIOEvents(sioOnControlConfig.lineName, 2584 sioOnControlHandler, sioOnControlLine, 2585 sioOnControlEvent)) 2586 { 2587 return -1; 2588 } 2589 } 2590 else if (sioOnControlConfig.type == ConfigType::DBUS) 2591 { 2592 static sdbusplus::bus::match_t sioOnControlEventMonitor = 2593 power_control::dbusGPIOMatcher(sioOnControlConfig, 2594 sioOnControlHandler); 2595 } 2596 else 2597 { 2598 lg2::error( 2599 "sioOnControl name should be configured from jsonconfig file\n"); 2600 return -1; 2601 } 2602 2603 // Request SIO_S5 GPIO events 2604 if (sioS5Config.type == ConfigType::GPIO) 2605 { 2606 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler, 2607 sioS5Line, sioS5Event)) 2608 { 2609 return -1; 2610 } 2611 } 2612 else if (sioS5Config.type == ConfigType::DBUS) 2613 { 2614 static sdbusplus::bus::match_t sioS5EventMonitor = 2615 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler); 2616 } 2617 else 2618 { 2619 lg2::error("sioS5 name should be configured from json config file"); 2620 return -1; 2621 } 2622 } 2623 2624 // Request POWER_BUTTON GPIO events 2625 if (powerButtonConfig.type == ConfigType::GPIO) 2626 { 2627 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler, 2628 powerButtonLine, powerButtonEvent)) 2629 { 2630 return -1; 2631 } 2632 } 2633 else if (powerButtonConfig.type == ConfigType::DBUS) 2634 { 2635 static sdbusplus::bus::match_t powerButtonEventMonitor = 2636 power_control::dbusGPIOMatcher(powerButtonConfig, 2637 powerButtonHandler); 2638 } 2639 2640 // Request RESET_BUTTON GPIO events 2641 if (resetButtonConfig.type == ConfigType::GPIO) 2642 { 2643 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler, 2644 resetButtonLine, resetButtonEvent)) 2645 { 2646 return -1; 2647 } 2648 } 2649 else if (resetButtonConfig.type == ConfigType::DBUS) 2650 { 2651 static sdbusplus::bus::match_t resetButtonEventMonitor = 2652 power_control::dbusGPIOMatcher(resetButtonConfig, 2653 resetButtonHandler); 2654 } 2655 2656 // Request NMI_BUTTON GPIO events 2657 if (nmiButtonConfig.type == ConfigType::GPIO) 2658 { 2659 if (!nmiButtonConfig.lineName.empty()) 2660 { 2661 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler, 2662 nmiButtonLine, nmiButtonEvent); 2663 } 2664 } 2665 else if (nmiButtonConfig.type == ConfigType::DBUS) 2666 { 2667 static sdbusplus::bus::match_t nmiButtonEventMonitor = 2668 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler); 2669 } 2670 2671 // Request ID_BUTTON GPIO events 2672 if (idButtonConfig.type == ConfigType::GPIO) 2673 { 2674 if (!idButtonConfig.lineName.empty()) 2675 { 2676 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler, 2677 idButtonLine, idButtonEvent); 2678 } 2679 } 2680 else if (idButtonConfig.type == ConfigType::DBUS) 2681 { 2682 static sdbusplus::bus::match_t idButtonEventMonitor = 2683 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler); 2684 } 2685 2686 #ifdef USE_PLT_RST 2687 sdbusplus::bus::match_t pltRstMatch( 2688 *conn, 2689 "type='signal',interface='org.freedesktop.DBus.Properties',member='" 2690 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'", 2691 hostMiscHandler); 2692 #endif 2693 2694 // Request POST_COMPLETE GPIO events 2695 if (postCompleteConfig.type == ConfigType::GPIO) 2696 { 2697 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler, 2698 postCompleteLine, postCompleteEvent)) 2699 { 2700 return -1; 2701 } 2702 } 2703 else if (postCompleteConfig.type == ConfigType::DBUS) 2704 { 2705 static sdbusplus::bus::match_t postCompleteEventMonitor = 2706 power_control::dbusGPIOMatcher(postCompleteConfig, 2707 postCompleteHandler); 2708 } 2709 else 2710 { 2711 lg2::error( 2712 "postComplete name should be configured from json config file"); 2713 return -1; 2714 } 2715 2716 // initialize NMI_OUT GPIO. 2717 if (!nmiOutConfig.lineName.empty()) 2718 { 2719 setGPIOOutput(nmiOutConfig.lineName, nmiOutConfig.polarity, nmiOutLine); 2720 } 2721 2722 // Initialize POWER_OUT and RESET_OUT GPIO. 2723 gpiod::line line; 2724 if (!powerOutConfig.lineName.empty()) 2725 { 2726 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity, 2727 line)) 2728 { 2729 return -1; 2730 } 2731 } 2732 else 2733 { 2734 lg2::error("powerOut name should be configured from json config file"); 2735 return -1; 2736 } 2737 2738 if (!resetOutConfig.lineName.empty()) 2739 { 2740 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity, 2741 line)) 2742 { 2743 return -1; 2744 } 2745 } 2746 else 2747 { 2748 lg2::error("ResetOut name should be configured from json config file"); 2749 return -1; 2750 } 2751 // Release line 2752 line.reset(); 2753 2754 // Initialize the power state 2755 powerState = PowerState::off; 2756 // Check power good 2757 2758 if (powerOkConfig.type == ConfigType::GPIO) 2759 { 2760 if (psPowerOKLine.get_value() > 0 || 2761 (sioEnabled && 2762 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity))) 2763 { 2764 powerState = PowerState::on; 2765 } 2766 } 2767 else 2768 { 2769 if (getProperty(powerOkConfig)) 2770 { 2771 powerState = PowerState::on; 2772 } 2773 } 2774 // Check if we need to start the Power Restore policy 2775 if (powerState != PowerState::on) 2776 { 2777 powerRestore.run(); 2778 } 2779 2780 if (nmiOutLine) 2781 nmiSourcePropertyMonitor(); 2782 2783 lg2::info("Initializing power state."); 2784 logStateTransition(powerState); 2785 2786 // Power Control Service 2787 sdbusplus::asio::object_server hostServer = 2788 sdbusplus::asio::object_server(conn); 2789 2790 // Power Control Interface 2791 hostIface = 2792 hostServer.add_interface("/xyz/openbmc_project/state/host" + node, 2793 "xyz.openbmc_project.State.Host"); 2794 // Interface for IPMI/Redfish initiated host state transitions 2795 hostIface->register_property( 2796 "RequestedHostTransition", 2797 std::string("xyz.openbmc_project.State.Host.Transition.Off"), 2798 [](const std::string& requested, std::string& resp) { 2799 if (requested == "xyz.openbmc_project.State.Host.Transition.Off") 2800 { 2801 // if power button is masked, ignore this 2802 if (!powerButtonMask) 2803 { 2804 sendPowerControlEvent(Event::gracefulPowerOffRequest); 2805 addRestartCause(RestartCause::command); 2806 } 2807 else 2808 { 2809 lg2::info("Power Button Masked."); 2810 throw std::invalid_argument("Transition Request Masked"); 2811 return 0; 2812 } 2813 } 2814 else if (requested == 2815 "xyz.openbmc_project.State.Host.Transition.On") 2816 { 2817 // if power button is masked, ignore this 2818 if (!powerButtonMask) 2819 { 2820 sendPowerControlEvent(Event::powerOnRequest); 2821 addRestartCause(RestartCause::command); 2822 } 2823 else 2824 { 2825 lg2::info("Power Button Masked."); 2826 throw std::invalid_argument("Transition Request Masked"); 2827 return 0; 2828 } 2829 } 2830 else if (requested == 2831 "xyz.openbmc_project.State.Host.Transition.Reboot") 2832 { 2833 // if power button is masked, ignore this 2834 if (!powerButtonMask) 2835 { 2836 sendPowerControlEvent(Event::powerCycleRequest); 2837 addRestartCause(RestartCause::command); 2838 } 2839 else 2840 { 2841 lg2::info("Power Button Masked."); 2842 throw std::invalid_argument("Transition Request Masked"); 2843 return 0; 2844 } 2845 } 2846 else if ( 2847 requested == 2848 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot") 2849 { 2850 // if reset button is masked, ignore this 2851 if (!resetButtonMask) 2852 { 2853 sendPowerControlEvent(Event::gracefulPowerCycleRequest); 2854 addRestartCause(RestartCause::command); 2855 } 2856 else 2857 { 2858 lg2::info("Reset Button Masked."); 2859 throw std::invalid_argument("Transition Request Masked"); 2860 return 0; 2861 } 2862 } 2863 else if ( 2864 requested == 2865 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot") 2866 { 2867 // if reset button is masked, ignore this 2868 if (!resetButtonMask) 2869 { 2870 sendPowerControlEvent(Event::resetRequest); 2871 addRestartCause(RestartCause::command); 2872 } 2873 else 2874 { 2875 lg2::info("Reset Button Masked."); 2876 throw std::invalid_argument("Transition Request Masked"); 2877 return 0; 2878 } 2879 } 2880 else 2881 { 2882 lg2::error("Unrecognized host state transition request."); 2883 throw std::invalid_argument("Unrecognized Transition Request"); 2884 return 0; 2885 } 2886 resp = requested; 2887 return 1; 2888 }); 2889 hostIface->register_property("CurrentHostState", 2890 std::string(getHostState(powerState))); 2891 2892 hostIface->initialize(); 2893 2894 // Chassis Control Service 2895 sdbusplus::asio::object_server chassisServer = 2896 sdbusplus::asio::object_server(conn); 2897 2898 // Chassis Control Interface 2899 chassisIface = 2900 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node, 2901 "xyz.openbmc_project.State.Chassis"); 2902 2903 chassisIface->register_property( 2904 "RequestedPowerTransition", 2905 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"), 2906 [](const std::string& requested, std::string& resp) { 2907 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off") 2908 { 2909 // if power button is masked, ignore this 2910 if (!powerButtonMask) 2911 { 2912 sendPowerControlEvent(Event::powerOffRequest); 2913 addRestartCause(RestartCause::command); 2914 } 2915 else 2916 { 2917 lg2::info("Power Button Masked."); 2918 throw std::invalid_argument("Transition Request Masked"); 2919 return 0; 2920 } 2921 } 2922 else if (requested == 2923 "xyz.openbmc_project.State.Chassis.Transition.On") 2924 { 2925 // if power button is masked, ignore this 2926 if (!powerButtonMask) 2927 { 2928 sendPowerControlEvent(Event::powerOnRequest); 2929 addRestartCause(RestartCause::command); 2930 } 2931 else 2932 { 2933 lg2::info("Power Button Masked."); 2934 throw std::invalid_argument("Transition Request Masked"); 2935 return 0; 2936 } 2937 } 2938 else if (requested == 2939 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 2940 { 2941 // if power button is masked, ignore this 2942 if (!powerButtonMask) 2943 { 2944 sendPowerControlEvent(Event::powerCycleRequest); 2945 addRestartCause(RestartCause::command); 2946 } 2947 else 2948 { 2949 lg2::info("Power Button Masked."); 2950 throw std::invalid_argument("Transition Request Masked"); 2951 return 0; 2952 } 2953 } 2954 else 2955 { 2956 lg2::error("Unrecognized chassis state transition request."); 2957 throw std::invalid_argument("Unrecognized Transition Request"); 2958 return 0; 2959 } 2960 resp = requested; 2961 return 1; 2962 }); 2963 chassisIface->register_property("CurrentPowerState", 2964 std::string(getChassisState(powerState))); 2965 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs()); 2966 2967 chassisIface->initialize(); 2968 2969 #ifdef CHASSIS_SYSTEM_RESET 2970 // Chassis System Service 2971 sdbusplus::asio::object_server chassisSysServer = 2972 sdbusplus::asio::object_server(conn); 2973 2974 // Chassis System Interface 2975 chassisSysIface = chassisSysServer.add_interface( 2976 "/xyz/openbmc_project/state/chassis_system0", 2977 "xyz.openbmc_project.State.Chassis"); 2978 2979 chassisSysIface->register_property( 2980 "RequestedPowerTransition", 2981 std::string("xyz.openbmc_project.State.Chassis.Transition.On"), 2982 [](const std::string& requested, std::string& resp) { 2983 if (requested == 2984 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 2985 { 2986 systemReset(); 2987 addRestartCause(RestartCause::command); 2988 } 2989 else 2990 { 2991 lg2::error( 2992 "Unrecognized chassis system state transition request."); 2993 throw std::invalid_argument("Unrecognized Transition Request"); 2994 return 0; 2995 } 2996 resp = requested; 2997 return 1; 2998 }); 2999 chassisSysIface->register_property( 3000 "CurrentPowerState", std::string(getChassisState(powerState))); 3001 chassisSysIface->register_property("LastStateChangeTime", 3002 getCurrentTimeMs()); 3003 3004 chassisSysIface->initialize(); 3005 3006 if (!slotPowerConfig.lineName.empty()) 3007 { 3008 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine)) 3009 { 3010 return -1; 3011 } 3012 3013 slotPowerState = SlotPowerState::off; 3014 if (slotPowerLine.get_value() > 0) 3015 { 3016 slotPowerState = SlotPowerState::on; 3017 } 3018 3019 chassisSlotIface = chassisSysServer.add_interface( 3020 "/xyz/openbmc_project/state/chassis_system" + node, 3021 "xyz.openbmc_project.State.Chassis"); 3022 chassisSlotIface->register_property( 3023 "RequestedPowerTransition", 3024 std::string("xyz.openbmc_project.State.Chassis.Transition.On"), 3025 [](const std::string& requested, std::string& resp) { 3026 if (requested == 3027 "xyz.openbmc_project.State.Chassis.Transition.On") 3028 { 3029 slotPowerOn(); 3030 } 3031 else if (requested == 3032 "xyz.openbmc_project.State.Chassis.Transition.Off") 3033 { 3034 slotPowerOff(); 3035 } 3036 else if ( 3037 requested == 3038 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle") 3039 { 3040 slotPowerCycle(); 3041 } 3042 else 3043 { 3044 lg2::error( 3045 "Unrecognized chassis system state transition request.\n"); 3046 throw std::invalid_argument( 3047 "Unrecognized Transition Request"); 3048 return 0; 3049 } 3050 resp = requested; 3051 return 1; 3052 }); 3053 chassisSlotIface->register_property( 3054 "CurrentPowerState", std::string(getSlotState(slotPowerState))); 3055 chassisSlotIface->register_property("LastStateChangeTime", 3056 getCurrentTimeMs()); 3057 chassisSlotIface->initialize(); 3058 } 3059 #endif 3060 // Buttons Service 3061 sdbusplus::asio::object_server buttonsServer = 3062 sdbusplus::asio::object_server(conn); 3063 3064 if (!powerButtonConfig.lineName.empty()) 3065 { 3066 // Power Button Interface 3067 power_control::powerButtonIface = buttonsServer.add_interface( 3068 "/xyz/openbmc_project/chassis/buttons/power", 3069 "xyz.openbmc_project.Chassis.Buttons"); 3070 3071 powerButtonIface->register_property( 3072 "ButtonMasked", false, [](const bool requested, bool& current) { 3073 if (requested) 3074 { 3075 if (powerButtonMask) 3076 { 3077 return 1; 3078 } 3079 if (!setGPIOOutput(powerOutConfig.lineName, 3080 !powerOutConfig.polarity, 3081 powerButtonMask)) 3082 { 3083 throw std::runtime_error("Failed to request GPIO"); 3084 return 0; 3085 } 3086 lg2::info("Power Button Masked."); 3087 } 3088 else 3089 { 3090 if (!powerButtonMask) 3091 { 3092 return 1; 3093 } 3094 lg2::info("Power Button Un-masked"); 3095 powerButtonMask.reset(); 3096 } 3097 // Update the mask setting 3098 current = requested; 3099 return 1; 3100 }); 3101 3102 // Check power button state 3103 bool powerButtonPressed; 3104 if (powerButtonConfig.type == ConfigType::GPIO) 3105 { 3106 powerButtonPressed = powerButtonLine.get_value() == 0; 3107 } 3108 else 3109 { 3110 powerButtonPressed = getProperty(powerButtonConfig) == 0; 3111 } 3112 3113 powerButtonIface->register_property("ButtonPressed", 3114 powerButtonPressed); 3115 3116 powerButtonIface->initialize(); 3117 } 3118 3119 if (!resetButtonConfig.lineName.empty()) 3120 { 3121 // Reset Button Interface 3122 3123 resetButtonIface = buttonsServer.add_interface( 3124 "/xyz/openbmc_project/chassis/buttons/reset", 3125 "xyz.openbmc_project.Chassis.Buttons"); 3126 3127 resetButtonIface->register_property( 3128 "ButtonMasked", false, [](const bool requested, bool& current) { 3129 if (requested) 3130 { 3131 if (resetButtonMask) 3132 { 3133 return 1; 3134 } 3135 if (!setGPIOOutput(resetOutConfig.lineName, 3136 !resetOutConfig.polarity, 3137 resetButtonMask)) 3138 { 3139 throw std::runtime_error("Failed to request GPIO"); 3140 return 0; 3141 } 3142 lg2::info("Reset Button Masked."); 3143 } 3144 else 3145 { 3146 if (!resetButtonMask) 3147 { 3148 return 1; 3149 } 3150 lg2::info("Reset Button Un-masked"); 3151 resetButtonMask.reset(); 3152 } 3153 // Update the mask setting 3154 current = requested; 3155 return 1; 3156 }); 3157 3158 // Check reset button state 3159 bool resetButtonPressed; 3160 if (resetButtonConfig.type == ConfigType::GPIO) 3161 { 3162 resetButtonPressed = resetButtonLine.get_value() == 0; 3163 } 3164 else 3165 { 3166 resetButtonPressed = getProperty(resetButtonConfig) == 0; 3167 } 3168 3169 resetButtonIface->register_property("ButtonPressed", 3170 resetButtonPressed); 3171 3172 resetButtonIface->initialize(); 3173 } 3174 3175 if (nmiButtonLine) 3176 { 3177 // NMI Button Interface 3178 nmiButtonIface = buttonsServer.add_interface( 3179 "/xyz/openbmc_project/chassis/buttons/nmi", 3180 "xyz.openbmc_project.Chassis.Buttons"); 3181 3182 nmiButtonIface->register_property( 3183 "ButtonMasked", false, [](const bool requested, bool& current) { 3184 if (nmiButtonMasked == requested) 3185 { 3186 // NMI button mask is already set as requested, so no change 3187 return 1; 3188 } 3189 if (requested) 3190 { 3191 lg2::info("NMI Button Masked."); 3192 nmiButtonMasked = true; 3193 } 3194 else 3195 { 3196 lg2::info("NMI Button Un-masked."); 3197 nmiButtonMasked = false; 3198 } 3199 // Update the mask setting 3200 current = nmiButtonMasked; 3201 return 1; 3202 }); 3203 3204 // Check NMI button state 3205 bool nmiButtonPressed; 3206 if (nmiButtonConfig.type == ConfigType::GPIO) 3207 { 3208 nmiButtonPressed = nmiButtonLine.get_value() == 0; 3209 } 3210 else 3211 { 3212 nmiButtonPressed = getProperty(nmiButtonConfig) == 0; 3213 } 3214 3215 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed); 3216 3217 nmiButtonIface->initialize(); 3218 } 3219 3220 if (nmiOutLine) 3221 { 3222 // NMI out Service 3223 sdbusplus::asio::object_server nmiOutServer = 3224 sdbusplus::asio::object_server(conn); 3225 3226 // NMI out Interface 3227 nmiOutIface = nmiOutServer.add_interface( 3228 "/xyz/openbmc_project/control/host" + node + "/nmi", 3229 "xyz.openbmc_project.Control.Host.NMI"); 3230 nmiOutIface->register_method("NMI", nmiReset); 3231 nmiOutIface->initialize(); 3232 } 3233 3234 if (idButtonLine) 3235 { 3236 // ID Button Interface 3237 idButtonIface = buttonsServer.add_interface( 3238 "/xyz/openbmc_project/chassis/buttons/id", 3239 "xyz.openbmc_project.Chassis.Buttons"); 3240 3241 // Check ID button state 3242 bool idButtonPressed; 3243 if (idButtonConfig.type == ConfigType::GPIO) 3244 { 3245 idButtonPressed = idButtonLine.get_value() == 0; 3246 } 3247 else 3248 { 3249 idButtonPressed = getProperty(idButtonConfig) == 0; 3250 } 3251 3252 idButtonIface->register_property("ButtonPressed", idButtonPressed); 3253 3254 idButtonIface->initialize(); 3255 } 3256 3257 // OS State Service 3258 sdbusplus::asio::object_server osServer = 3259 sdbusplus::asio::object_server(conn); 3260 3261 // OS State Interface 3262 osIface = osServer.add_interface( 3263 "/xyz/openbmc_project/state/os", 3264 "xyz.openbmc_project.State.OperatingSystem.Status"); 3265 3266 // Get the initial OS state based on POST complete 3267 // 0: Asserted, OS state is "Standby" (ready to boot) 3268 // 1: De-Asserted, OS state is "Inactive" 3269 OperatingSystemStateStage osState; 3270 if (postCompleteConfig.type == ConfigType::GPIO) 3271 { 3272 osState = postCompleteLine.get_value() > 0 3273 ? OperatingSystemStateStage::Inactive 3274 : OperatingSystemStateStage::Standby; 3275 } 3276 else 3277 { 3278 osState = getProperty(postCompleteConfig) > 0 3279 ? OperatingSystemStateStage::Inactive 3280 : OperatingSystemStateStage::Standby; 3281 } 3282 3283 osIface->register_property( 3284 "OperatingSystemState", 3285 std::string(getOperatingSystemStateStage(osState))); 3286 3287 osIface->initialize(); 3288 3289 // Restart Cause Service 3290 sdbusplus::asio::object_server restartCauseServer = 3291 sdbusplus::asio::object_server(conn); 3292 3293 // Restart Cause Interface 3294 restartCauseIface = restartCauseServer.add_interface( 3295 "/xyz/openbmc_project/control/host" + node + "/restart_cause", 3296 "xyz.openbmc_project.Control.Host.RestartCause"); 3297 3298 restartCauseIface->register_property( 3299 "RestartCause", 3300 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown")); 3301 3302 restartCauseIface->register_property( 3303 "RequestedRestartCause", 3304 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"), 3305 [](const std::string& requested, std::string& resp) { 3306 if (requested == 3307 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer") 3308 { 3309 addRestartCause(RestartCause::watchdog); 3310 } 3311 else 3312 { 3313 throw std::invalid_argument( 3314 "Unrecognized RestartCause Request"); 3315 return 0; 3316 } 3317 3318 lg2::info("RestartCause requested: {RESTART_CAUSE}", 3319 "RESTART_CAUSE", requested); 3320 resp = requested; 3321 return 1; 3322 }); 3323 3324 restartCauseIface->initialize(); 3325 3326 currentHostStateMonitor(); 3327 3328 io.run(); 3329 3330 return 0; 3331 } 3332