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