1 #include "config.h" 2 3 #include "chassis_state_manager.hpp" 4 5 #include "utils.hpp" 6 #include "xyz/openbmc_project/Common/error.hpp" 7 #include "xyz/openbmc_project/State/Shutdown/Power/error.hpp" 8 9 #include <fmt/format.h> 10 11 #include <cereal/archives/json.hpp> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/lg2.hpp> 14 #include <sdbusplus/bus.hpp> 15 #include <sdbusplus/exception.hpp> 16 #include <sdeventplus/event.hpp> 17 #include <sdeventplus/exception.hpp> 18 #include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp> 19 20 #include <filesystem> 21 #include <fstream> 22 23 namespace phosphor 24 { 25 namespace state 26 { 27 namespace manager 28 { 29 30 PHOSPHOR_LOG2_USING; 31 32 // When you see server:: you know we're referencing our base class 33 namespace server = sdbusplus::xyz::openbmc_project::State::server; 34 namespace decoratorServer = 35 sdbusplus::xyz::openbmc_project::State::Decorator::server; 36 37 using namespace phosphor::logging; 38 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 39 using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout; 40 using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator; 41 constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT = 42 "obmc-chassis-poweroff@{}.target"; 43 constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT = 44 "obmc-chassis-hard-poweroff@{}.target"; 45 constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target"; 46 constexpr auto RESET_HOST_SENSORS_SVC_FMT = 47 "phosphor-reset-sensor-states@{}.service"; 48 constexpr auto ACTIVE_STATE = "active"; 49 constexpr auto ACTIVATING_STATE = "activating"; 50 51 // Details at https://upower.freedesktop.org/docs/Device.html 52 constexpr uint TYPE_UPS = 3; 53 constexpr uint STATE_FULLY_CHARGED = 4; 54 constexpr uint BATTERY_LVL_FULL = 8; 55 56 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 57 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 58 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 59 60 constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; 61 constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit"; 62 63 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 64 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 65 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 66 constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device"; 67 constexpr auto POWERSYSINPUTS_INTERFACE = 68 "xyz.openbmc_project.State.Decorator.PowerSystemInputs"; 69 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 70 71 void Chassis::subscribeToSystemdSignals() 72 { 73 try 74 { 75 auto method = this->bus.new_method_call( 76 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe"); 77 this->bus.call(method); 78 } 79 catch (const sdbusplus::exception::exception& e) 80 { 81 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e); 82 elog<InternalFailure>(); 83 } 84 85 return; 86 } 87 88 void Chassis::createSystemdTargetTable() 89 { 90 systemdTargetTable = { 91 // Use the hard off target to ensure we shutdown immediately 92 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)}, 93 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)}}; 94 } 95 96 // TODO - Will be rewritten once sdbusplus client bindings are in place 97 // and persistent storage design is in place and sdbusplus 98 // has read property function 99 void Chassis::determineInitialState() 100 { 101 102 // Monitor for any properties changed signals on UPower device path 103 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>( 104 bus, 105 sdbusplus::bus::match::rules::propertiesChangedNamespace( 106 "/org/freedesktop/UPower", UPOWER_INTERFACE), 107 [this](auto& msg) { this->uPowerChangeEvent(msg); }); 108 109 // Monitor for any properties changed signals on PowerSystemInputs 110 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>( 111 bus, 112 sdbusplus::bus::match::rules::propertiesChangedNamespace( 113 fmt::format( 114 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id), 115 POWERSYSINPUTS_INTERFACE), 116 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); }); 117 118 determineStatusOfPower(); 119 120 std::variant<int> pgood = -1; 121 auto method = this->bus.new_method_call( 122 "org.openbmc.control.Power", "/org/openbmc/control/power0", 123 "org.freedesktop.DBus.Properties", "Get"); 124 125 method.append("org.openbmc.control.Power", "pgood"); 126 try 127 { 128 auto reply = this->bus.call(method); 129 reply.read(pgood); 130 131 if (std::get<int>(pgood) == 1) 132 { 133 info("Initial Chassis State will be On"); 134 server::Chassis::currentPowerState(PowerState::On); 135 server::Chassis::requestedPowerTransition(Transition::On); 136 return; 137 } 138 else 139 { 140 // The system is off. If we think it should be on then 141 // we probably lost AC while up, so set a new state 142 // change time. 143 uint64_t lastTime; 144 PowerState lastState; 145 146 if (deserializeStateChangeTime(lastTime, lastState)) 147 { 148 // If power was on before the BMC reboot and the reboot reason 149 // was not a pinhole reset, log an error 150 if (lastState == PowerState::On) 151 { 152 info( 153 "Chassis power was on before the BMC reboot and it is off now"); 154 155 // Reset host sensors since system is off now 156 startUnit(fmt::format(RESET_HOST_SENSORS_SVC_FMT, id)); 157 158 setStateChangeTime(); 159 160 // 0 indicates pinhole reset. 1 is NOT pinhole reset 161 if (phosphor::state::manager::utils::getGpioValue( 162 "reset-cause-pinhole") != 0) 163 { 164 if (standbyVoltageRegulatorFault()) 165 { 166 report<Regulator>(); 167 } 168 else 169 { 170 report<Blackout>(Entry::Level::Critical); 171 } 172 } 173 else 174 { 175 info("Pinhole reset"); 176 } 177 } 178 } 179 } 180 } 181 catch (const sdbusplus::exception::exception& e) 182 { 183 // It's acceptable for the pgood state service to not be available 184 // since it will notify us of the pgood state when it comes up. 185 if (e.name() != nullptr && 186 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0) 187 { 188 goto fail; 189 } 190 191 // Only log for unexpected error types. 192 error("Error performing call to get pgood: {ERROR}", "ERROR", e); 193 goto fail; 194 } 195 196 fail: 197 info("Initial Chassis State will be Off"); 198 server::Chassis::currentPowerState(PowerState::Off); 199 server::Chassis::requestedPowerTransition(Transition::Off); 200 201 return; 202 } 203 204 void Chassis::determineStatusOfPower() 205 { 206 // Default PowerStatus to good 207 server::Chassis::currentPowerStatus(PowerStatus::Good); 208 209 determineStatusOfUPSPower(); 210 if (server::Chassis::currentPowerStatus() != PowerStatus::Good) 211 { 212 return; 213 } 214 215 determineStatusOfPSUPower(); 216 } 217 218 void Chassis::determineStatusOfUPSPower() 219 { 220 // Find all implementations of the UPower interface 221 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 222 MAPPER_INTERFACE, "GetSubTree"); 223 224 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE})); 225 226 std::map<std::string, std::map<std::string, std::vector<std::string>>> 227 mapperResponse; 228 229 try 230 { 231 auto mapperResponseMsg = bus.call(mapper); 232 mapperResponseMsg.read(mapperResponse); 233 } 234 catch (const sdbusplus::exception::exception& e) 235 { 236 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e); 237 throw; 238 } 239 240 if (mapperResponse.empty()) 241 { 242 debug("No UPower devices found in system"); 243 } 244 245 // Iterate through all returned Upower interfaces and look for UPS's 246 for (const auto& [path, services] : mapperResponse) 247 { 248 for (const auto& serviceIter : services) 249 { 250 const std::string& service = serviceIter.first; 251 252 try 253 { 254 auto method = bus.new_method_call(service.c_str(), path.c_str(), 255 PROPERTY_INTERFACE, "GetAll"); 256 method.append(UPOWER_INTERFACE); 257 258 auto response = bus.call(method); 259 using Property = std::string; 260 using Value = std::variant<bool, uint>; 261 using PropertyMap = std::map<Property, Value>; 262 PropertyMap properties; 263 response.read(properties); 264 265 if (std::get<uint>(properties["Type"]) != TYPE_UPS) 266 { 267 info("UPower device {OBJ_PATH} is not a UPS device", 268 "OBJ_PATH", path); 269 continue; 270 } 271 272 if (std::get<bool>(properties["IsPresent"]) != true) 273 { 274 // There is a UPS detected but it is not officially 275 // "present" yet. Monitor it for state change. 276 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH", 277 path); 278 continue; 279 } 280 281 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED) 282 { 283 info("UPS is fully charged"); 284 } 285 else 286 { 287 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE", 288 std::get<uint>(properties["State"])); 289 server::Chassis::currentPowerStatus( 290 PowerStatus::UninterruptiblePowerSupply); 291 return; 292 } 293 294 if (std::get<uint>(properties["BatteryLevel"]) == 295 BATTERY_LVL_FULL) 296 { 297 info("UPS Battery Level is Full"); 298 // Only one UPS per system, we've found it and it's all 299 // good so exit function 300 return; 301 } 302 else 303 { 304 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}", 305 "UPS_BAT_LEVEL", 306 std::get<uint>(properties["BatteryLevel"])); 307 server::Chassis::currentPowerStatus( 308 PowerStatus::UninterruptiblePowerSupply); 309 return; 310 } 311 } 312 catch (const sdbusplus::exception::exception& e) 313 { 314 error("Error reading UPS property, error: {ERROR}, " 315 "service: {SERVICE} path: {PATH}", 316 "ERROR", e, "SERVICE", service, "PATH", path); 317 throw; 318 } 319 } 320 } 321 return; 322 } 323 324 void Chassis::determineStatusOfPSUPower() 325 { 326 // Find all implementations of the PowerSystemInputs interface 327 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 328 MAPPER_INTERFACE, "GetSubTree"); 329 330 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE})); 331 332 std::map<std::string, std::map<std::string, std::vector<std::string>>> 333 mapperResponse; 334 335 try 336 { 337 auto mapperResponseMsg = bus.call(mapper); 338 mapperResponseMsg.read(mapperResponse); 339 } 340 catch (const sdbusplus::exception::exception& e) 341 { 342 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}", 343 "ERROR", e); 344 throw; 345 } 346 347 for (const auto& [path, services] : mapperResponse) 348 { 349 for (const auto& serviceIter : services) 350 { 351 const std::string& service = serviceIter.first; 352 353 try 354 { 355 auto method = bus.new_method_call(service.c_str(), path.c_str(), 356 PROPERTY_INTERFACE, "GetAll"); 357 method.append(POWERSYSINPUTS_INTERFACE); 358 359 auto response = bus.call(method); 360 using Property = std::string; 361 using Value = std::variant<std::string>; 362 using PropertyMap = std::map<Property, Value>; 363 PropertyMap properties; 364 response.read(properties); 365 366 auto statusStr = std::get<std::string>(properties["Status"]); 367 auto status = 368 decoratorServer::PowerSystemInputs::convertStatusFromString( 369 statusStr); 370 371 if (status == decoratorServer::PowerSystemInputs::Status::Fault) 372 { 373 info("Power System Inputs status is in Fault state"); 374 server::Chassis::currentPowerStatus(PowerStatus::BrownOut); 375 return; 376 } 377 } 378 catch (const sdbusplus::exception::exception& e) 379 { 380 error( 381 "Error reading Power System Inputs property, error: {ERROR}, " 382 "service: {SERVICE} path: {PATH}", 383 "ERROR", e, "SERVICE", service, "PATH", path); 384 throw; 385 } 386 } 387 } 388 return; 389 } 390 391 void Chassis::uPowerChangeEvent(sdbusplus::message::message& msg) 392 { 393 debug("UPS Property Change Event Triggered"); 394 std::string statusInterface; 395 std::map<std::string, std::variant<uint, bool>> msgData; 396 msg.read(statusInterface, msgData); 397 398 // If the change is to any of the properties we are interested in, then call 399 // determineStatusOfPower(), which looks at all the power-related 400 // interfaces, to see if a power status change is needed 401 auto propertyMap = msgData.find("IsPresent"); 402 if (propertyMap != msgData.end()) 403 { 404 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO", 405 std::get<bool>(propertyMap->second)); 406 determineStatusOfPower(); 407 return; 408 } 409 410 propertyMap = msgData.find("State"); 411 if (propertyMap != msgData.end()) 412 { 413 info("UPS State changed to {UPS_STATE}", "UPS_STATE", 414 std::get<uint>(propertyMap->second)); 415 determineStatusOfPower(); 416 return; 417 } 418 419 propertyMap = msgData.find("BatteryLevel"); 420 if (propertyMap != msgData.end()) 421 { 422 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL", 423 std::get<uint>(propertyMap->second)); 424 determineStatusOfPower(); 425 return; 426 } 427 return; 428 } 429 430 void Chassis::powerSysInputsChangeEvent(sdbusplus::message::message& msg) 431 { 432 debug("Power System Inputs Property Change Event Triggered"); 433 std::string statusInterface; 434 std::map<std::string, std::variant<std::string>> msgData; 435 msg.read(statusInterface, msgData); 436 437 // If the change is to any of the properties we are interested in, then call 438 // determineStatusOfPower(), which looks at all the power-related 439 // interfaces, to see if a power status change is needed 440 auto propertyMap = msgData.find("Status"); 441 if (propertyMap != msgData.end()) 442 { 443 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}", 444 "POWER_SYS_INPUT_STATUS", 445 std::get<std::string>(propertyMap->second)); 446 determineStatusOfPower(); 447 return; 448 } 449 return; 450 } 451 452 void Chassis::startUnit(const std::string& sysdUnit) 453 { 454 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 455 SYSTEMD_INTERFACE, "StartUnit"); 456 457 method.append(sysdUnit); 458 method.append("replace"); 459 460 this->bus.call_noreply(method); 461 462 return; 463 } 464 465 bool Chassis::stateActive(const std::string& target) 466 { 467 std::variant<std::string> currentState; 468 sdbusplus::message::object_path unitTargetPath; 469 470 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 471 SYSTEMD_INTERFACE, "GetUnit"); 472 473 method.append(target); 474 475 try 476 { 477 auto result = this->bus.call(method); 478 result.read(unitTargetPath); 479 } 480 catch (const sdbusplus::exception::exception& e) 481 { 482 error("Error in GetUnit call: {ERROR}", "ERROR", e); 483 return false; 484 } 485 486 method = this->bus.new_method_call( 487 SYSTEMD_SERVICE, 488 static_cast<const std::string&>(unitTargetPath).c_str(), 489 SYSTEMD_PROPERTY_IFACE, "Get"); 490 491 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState"); 492 493 try 494 { 495 auto result = this->bus.call(method); 496 result.read(currentState); 497 } 498 catch (const sdbusplus::exception::exception& e) 499 { 500 error("Error in ActiveState Get: {ERROR}", "ERROR", e); 501 return false; 502 } 503 504 const auto& currentStateStr = std::get<std::string>(currentState); 505 return currentStateStr == ACTIVE_STATE || 506 currentStateStr == ACTIVATING_STATE; 507 } 508 509 int Chassis::sysStateChange(sdbusplus::message::message& msg) 510 { 511 sdbusplus::message::object_path newStateObjPath; 512 std::string newStateUnit{}; 513 std::string newStateResult{}; 514 515 // Read the msg and populate each variable 516 try 517 { 518 // newStateID is a throwaway that is needed in order to read the 519 // parameters that are useful out of the dbus message 520 uint32_t newStateID{}; 521 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 522 } 523 catch (const sdbusplus::exception::exception& e) 524 { 525 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}", 526 "ERROR", e, "REPLY_SIG", msg.get_signature()); 527 return 0; 528 } 529 530 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) && 531 (newStateResult == "done") && 532 (!stateActive(systemdTargetTable[Transition::On]))) 533 { 534 info("Received signal that power OFF is complete"); 535 this->currentPowerState(server::Chassis::PowerState::Off); 536 this->setStateChangeTime(); 537 } 538 else if ((newStateUnit == systemdTargetTable[Transition::On]) && 539 (newStateResult == "done") && 540 (stateActive(systemdTargetTable[Transition::On]))) 541 { 542 info("Received signal that power ON is complete"); 543 this->currentPowerState(server::Chassis::PowerState::On); 544 this->setStateChangeTime(); 545 546 // Remove temporary file which is utilized for scenarios where the 547 // BMC is rebooted while the chassis power is still on. 548 // This file is used to indicate to chassis related systemd services 549 // that the chassis is already on and they should skip running. 550 // Once the chassis state is back to on we can clear this file. 551 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0); 552 size++; // null 553 std::unique_ptr<char[]> chassisFile(new char[size]); 554 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0); 555 if (std::filesystem::exists(chassisFile.get())) 556 { 557 std::filesystem::remove(chassisFile.get()); 558 } 559 } 560 561 return 0; 562 } 563 564 Chassis::Transition Chassis::requestedPowerTransition(Transition value) 565 { 566 567 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}", 568 "REQ_POWER_TRAN", value); 569 startUnit(systemdTargetTable.find(value)->second); 570 return server::Chassis::requestedPowerTransition(value); 571 } 572 573 Chassis::PowerState Chassis::currentPowerState(PowerState value) 574 { 575 PowerState chassisPowerState; 576 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE", 577 value); 578 579 chassisPowerState = server::Chassis::currentPowerState(value); 580 if (chassisPowerState == PowerState::On) 581 { 582 pohTimer.resetRemaining(); 583 } 584 return chassisPowerState; 585 } 586 587 uint32_t Chassis::pohCounter(uint32_t value) 588 { 589 if (value != pohCounter()) 590 { 591 ChassisInherit::pohCounter(value); 592 serializePOH(); 593 } 594 return pohCounter(); 595 } 596 597 void Chassis::pohCallback() 598 { 599 if (ChassisInherit::currentPowerState() == PowerState::On) 600 { 601 pohCounter(pohCounter() + 1); 602 } 603 } 604 605 void Chassis::restorePOHCounter() 606 { 607 uint32_t counter; 608 if (!deserializePOH(POH_COUNTER_PERSIST_PATH, counter)) 609 { 610 // set to default value 611 pohCounter(0); 612 } 613 else 614 { 615 pohCounter(counter); 616 } 617 } 618 619 fs::path Chassis::serializePOH(const fs::path& path) 620 { 621 std::ofstream os(path.c_str(), std::ios::binary); 622 cereal::JSONOutputArchive oarchive(os); 623 oarchive(pohCounter()); 624 return path; 625 } 626 627 bool Chassis::deserializePOH(const fs::path& path, uint32_t& pohCounter) 628 { 629 try 630 { 631 if (fs::exists(path)) 632 { 633 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 634 cereal::JSONInputArchive iarchive(is); 635 iarchive(pohCounter); 636 return true; 637 } 638 return false; 639 } 640 catch (const cereal::Exception& e) 641 { 642 error("deserialize exception: {ERROR}", "ERROR", e); 643 fs::remove(path); 644 return false; 645 } 646 catch (const fs::filesystem_error& e) 647 { 648 return false; 649 } 650 651 return false; 652 } 653 654 void Chassis::startPOHCounter() 655 { 656 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path(); 657 fs::create_directories(dir); 658 659 try 660 { 661 auto event = sdeventplus::Event::get_default(); 662 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 663 event.loop(); 664 } 665 catch (const sdeventplus::SdEventError& e) 666 { 667 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR", 668 e); 669 phosphor::logging::commit<InternalFailure>(); 670 } 671 } 672 673 void Chassis::serializeStateChangeTime() 674 { 675 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH}; 676 std::ofstream os(path.c_str(), std::ios::binary); 677 cereal::JSONOutputArchive oarchive(os); 678 679 oarchive(ChassisInherit::lastStateChangeTime(), 680 ChassisInherit::currentPowerState()); 681 } 682 683 bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state) 684 { 685 fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH}; 686 687 try 688 { 689 if (fs::exists(path)) 690 { 691 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 692 cereal::JSONInputArchive iarchive(is); 693 iarchive(time, state); 694 return true; 695 } 696 } 697 catch (const std::exception& e) 698 { 699 error("deserialize exception: {ERROR}", "ERROR", e); 700 fs::remove(path); 701 } 702 703 return false; 704 } 705 706 void Chassis::restoreChassisStateChangeTime() 707 { 708 uint64_t time; 709 PowerState state; 710 711 if (!deserializeStateChangeTime(time, state)) 712 { 713 ChassisInherit::lastStateChangeTime(0); 714 } 715 else 716 { 717 ChassisInherit::lastStateChangeTime(time); 718 } 719 } 720 721 void Chassis::setStateChangeTime() 722 { 723 using namespace std::chrono; 724 uint64_t lastTime; 725 PowerState lastState; 726 727 auto now = 728 duration_cast<milliseconds>(system_clock::now().time_since_epoch()) 729 .count(); 730 731 // If power is on when the BMC is rebooted, this function will get called 732 // because sysStateChange() runs. Since the power state didn't change 733 // in this case, neither should the state change time, so check that 734 // the power state actually did change here. 735 if (deserializeStateChangeTime(lastTime, lastState)) 736 { 737 if (lastState == ChassisInherit::currentPowerState()) 738 { 739 return; 740 } 741 } 742 743 ChassisInherit::lastStateChangeTime(now); 744 serializeStateChangeTime(); 745 } 746 747 bool Chassis::standbyVoltageRegulatorFault() 748 { 749 bool regulatorFault = false; 750 751 // find standby voltage regulator fault via gpiog 752 753 auto gpioval = utils::getGpioValue("regulator-standby-faulted"); 754 755 if (-1 == gpioval) 756 { 757 error("Failed reading regulator-standby-faulted GPIO"); 758 } 759 760 if (1 == gpioval) 761 { 762 info("Detected standby voltage regulator fault"); 763 regulatorFault = true; 764 } 765 766 return regulatorFault; 767 } 768 769 } // namespace manager 770 } // namespace state 771 } // namespace phosphor 772