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