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