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