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