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 CHASSIS_BLACKOUT_TGT_FMT = "obmc-chassis-blackout@{}.target"; 48 constexpr auto CHASSIS_STATE_POWERCYCLE_TGT_FMT = 49 "obmc-chassis-powercycle@{}.target"; 50 constexpr auto AUTO_POWER_RESTORE_SVC_FMT = 51 "phosphor-discover-system-state@{}.service"; 52 constexpr auto ACTIVE_STATE = "active"; 53 constexpr auto ACTIVATING_STATE = "activating"; 54 55 // Details at https://upower.freedesktop.org/docs/Device.html 56 constexpr uint TYPE_UPS = 3; 57 constexpr uint STATE_FULLY_CHARGED = 4; 58 constexpr uint BATTERY_LVL_FULL = 8; 59 60 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 61 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 62 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 63 64 constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; 65 constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit"; 66 67 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 68 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 69 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 70 constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device"; 71 constexpr auto POWERSYSINPUTS_INTERFACE = 72 "xyz.openbmc_project.State.Decorator.PowerSystemInputs"; 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", UPOWER_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 POWERSYSINPUTS_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(MAPPER_BUSNAME, MAPPER_PATH, 234 MAPPER_INTERFACE, "GetSubTree"); 235 236 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE})); 237 238 std::map<std::string, std::map<std::string, std::vector<std::string>>> 239 mapperResponse; 240 241 try 242 { 243 auto mapperResponseMsg = bus.call(mapper); 244 mapperResponseMsg.read(mapperResponse); 245 } 246 catch (const sdbusplus::exception_t& e) 247 { 248 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e); 249 throw; 250 } 251 252 if (mapperResponse.empty()) 253 { 254 debug("No UPower devices found in system"); 255 } 256 257 // Iterate through all returned Upower interfaces and look for UPS's 258 for (const auto& [path, services] : mapperResponse) 259 { 260 for (const auto& serviceIter : services) 261 { 262 const std::string& service = serviceIter.first; 263 264 try 265 { 266 auto method = bus.new_method_call(service.c_str(), path.c_str(), 267 PROPERTY_INTERFACE, "GetAll"); 268 method.append(UPOWER_INTERFACE); 269 270 auto response = bus.call(method); 271 using Property = std::string; 272 using Value = std::variant<bool, uint>; 273 using PropertyMap = std::map<Property, Value>; 274 PropertyMap properties; 275 response.read(properties); 276 277 if (std::get<uint>(properties["Type"]) != TYPE_UPS) 278 { 279 info("UPower device {OBJ_PATH} is not a UPS device", 280 "OBJ_PATH", path); 281 continue; 282 } 283 284 if (std::get<bool>(properties["IsPresent"]) != true) 285 { 286 // There is a UPS detected but it is not officially 287 // "present" yet. Monitor it for state change. 288 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH", 289 path); 290 continue; 291 } 292 293 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED) 294 { 295 info("UPS is fully charged"); 296 } 297 else 298 { 299 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE", 300 std::get<uint>(properties["State"])); 301 server::Chassis::currentPowerStatus( 302 PowerStatus::UninterruptiblePowerSupply); 303 return false; 304 } 305 306 if (std::get<uint>(properties["BatteryLevel"]) == 307 BATTERY_LVL_FULL) 308 { 309 info("UPS Battery Level is Full"); 310 // Only one UPS per system, we've found it and it's all 311 // good so exit function 312 return true; 313 } 314 else 315 { 316 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}", 317 "UPS_BAT_LEVEL", 318 std::get<uint>(properties["BatteryLevel"])); 319 server::Chassis::currentPowerStatus( 320 PowerStatus::UninterruptiblePowerSupply); 321 return false; 322 } 323 } 324 catch (const sdbusplus::exception_t& e) 325 { 326 error("Error reading UPS property, error: {ERROR}, " 327 "service: {SERVICE} path: {PATH}", 328 "ERROR", e, "SERVICE", service, "PATH", path); 329 throw; 330 } 331 } 332 } 333 return true; 334 } 335 336 bool Chassis::determineStatusOfPSUPower() 337 { 338 // Find all implementations of the PowerSystemInputs interface 339 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 340 MAPPER_INTERFACE, "GetSubTree"); 341 342 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE})); 343 344 std::map<std::string, std::map<std::string, std::vector<std::string>>> 345 mapperResponse; 346 347 try 348 { 349 auto mapperResponseMsg = bus.call(mapper); 350 mapperResponseMsg.read(mapperResponse); 351 } 352 catch (const sdbusplus::exception_t& e) 353 { 354 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}", 355 "ERROR", e); 356 throw; 357 } 358 359 for (const auto& [path, services] : mapperResponse) 360 { 361 for (const auto& serviceIter : services) 362 { 363 const std::string& service = serviceIter.first; 364 365 try 366 { 367 auto method = bus.new_method_call(service.c_str(), path.c_str(), 368 PROPERTY_INTERFACE, "GetAll"); 369 method.append(POWERSYSINPUTS_INTERFACE); 370 371 auto response = bus.call(method); 372 using Property = std::string; 373 using Value = std::variant<std::string>; 374 using PropertyMap = std::map<Property, Value>; 375 PropertyMap properties; 376 response.read(properties); 377 378 auto statusStr = std::get<std::string>(properties["Status"]); 379 auto status = 380 decoratorServer::PowerSystemInputs::convertStatusFromString( 381 statusStr); 382 383 if (status == decoratorServer::PowerSystemInputs::Status::Fault) 384 { 385 info("Power System Inputs status is in Fault state"); 386 server::Chassis::currentPowerStatus(PowerStatus::BrownOut); 387 return false; 388 } 389 } 390 catch (const sdbusplus::exception_t& e) 391 { 392 error( 393 "Error reading Power System Inputs property, error: {ERROR}, " 394 "service: {SERVICE} path: {PATH}", 395 "ERROR", e, "SERVICE", service, "PATH", path); 396 throw; 397 } 398 } 399 } 400 return true; 401 } 402 403 void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg) 404 { 405 debug("UPS Property Change Event Triggered"); 406 std::string statusInterface; 407 std::map<std::string, std::variant<uint, bool>> msgData; 408 msg.read(statusInterface, msgData); 409 410 // If the change is to any of the properties we are interested in, then call 411 // determineStatusOfPower(), which looks at all the power-related 412 // interfaces, to see if a power status change is needed 413 auto propertyMap = msgData.find("IsPresent"); 414 if (propertyMap != msgData.end()) 415 { 416 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO", 417 std::get<bool>(propertyMap->second)); 418 determineStatusOfPower(); 419 return; 420 } 421 422 propertyMap = msgData.find("State"); 423 if (propertyMap != msgData.end()) 424 { 425 info("UPS State changed to {UPS_STATE}", "UPS_STATE", 426 std::get<uint>(propertyMap->second)); 427 determineStatusOfPower(); 428 return; 429 } 430 431 propertyMap = msgData.find("BatteryLevel"); 432 if (propertyMap != msgData.end()) 433 { 434 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL", 435 std::get<uint>(propertyMap->second)); 436 determineStatusOfPower(); 437 return; 438 } 439 return; 440 } 441 442 void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg) 443 { 444 debug("Power System Inputs Property Change Event Triggered"); 445 std::string statusInterface; 446 std::map<std::string, std::variant<std::string>> msgData; 447 msg.read(statusInterface, msgData); 448 449 // If the change is to any of the properties we are interested in, then call 450 // determineStatusOfPower(), which looks at all the power-related 451 // interfaces, to see if a power status change is needed 452 auto propertyMap = msgData.find("Status"); 453 if (propertyMap != msgData.end()) 454 { 455 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}", 456 "POWER_SYS_INPUT_STATUS", 457 std::get<std::string>(propertyMap->second)); 458 determineStatusOfPower(); 459 return; 460 } 461 return; 462 } 463 464 void Chassis::startUnit(const std::string& sysdUnit) 465 { 466 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 467 SYSTEMD_INTERFACE, "StartUnit"); 468 469 method.append(sysdUnit); 470 method.append("replace"); 471 472 this->bus.call_noreply(method); 473 474 return; 475 } 476 477 void Chassis::restartUnit(const std::string& sysdUnit) 478 { 479 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 480 SYSTEMD_INTERFACE, "RestartUnit"); 481 482 method.append(sysdUnit); 483 method.append("replace"); 484 485 this->bus.call_noreply(method); 486 487 return; 488 } 489 490 bool Chassis::stateActive(const std::string& target) 491 { 492 std::variant<std::string> currentState; 493 sdbusplus::message::object_path unitTargetPath; 494 495 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 496 SYSTEMD_INTERFACE, "GetUnit"); 497 498 method.append(target); 499 500 try 501 { 502 auto result = this->bus.call(method); 503 result.read(unitTargetPath); 504 } 505 catch (const sdbusplus::exception_t& e) 506 { 507 error("Error in GetUnit call: {ERROR}", "ERROR", e); 508 return false; 509 } 510 511 method = this->bus.new_method_call( 512 SYSTEMD_SERVICE, 513 static_cast<const std::string&>(unitTargetPath).c_str(), 514 SYSTEMD_PROPERTY_IFACE, "Get"); 515 516 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState"); 517 518 try 519 { 520 auto result = this->bus.call(method); 521 result.read(currentState); 522 } 523 catch (const sdbusplus::exception_t& e) 524 { 525 error("Error in ActiveState Get: {ERROR}", "ERROR", e); 526 return false; 527 } 528 529 const auto& currentStateStr = std::get<std::string>(currentState); 530 return currentStateStr == ACTIVE_STATE || 531 currentStateStr == ACTIVATING_STATE; 532 } 533 534 int Chassis::sysStateChange(sdbusplus::message_t& msg) 535 { 536 sdbusplus::message::object_path newStateObjPath; 537 std::string newStateUnit{}; 538 std::string newStateResult{}; 539 540 // Read the msg and populate each variable 541 try 542 { 543 // newStateID is a throwaway that is needed in order to read the 544 // parameters that are useful out of the dbus message 545 uint32_t newStateID{}; 546 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 547 } 548 catch (const sdbusplus::exception_t& e) 549 { 550 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}", 551 "ERROR", e, "REPLY_SIG", msg.get_signature()); 552 return 0; 553 } 554 555 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) && 556 (newStateResult == "done") && 557 (!stateActive(systemdTargetTable[Transition::On]))) 558 { 559 info("Received signal that power OFF is complete"); 560 this->currentPowerState(server::Chassis::PowerState::Off); 561 this->setStateChangeTime(); 562 } 563 else if ((newStateUnit == systemdTargetTable[Transition::On]) && 564 (newStateResult == "done") && 565 (stateActive(systemdTargetTable[Transition::On]))) 566 { 567 info("Received signal that power ON is complete"); 568 this->currentPowerState(server::Chassis::PowerState::On); 569 this->setStateChangeTime(); 570 571 // Remove temporary file which is utilized for scenarios where the 572 // BMC is rebooted while the chassis power is still on. 573 // This file is used to indicate to chassis related systemd services 574 // that the chassis is already on and they should skip running. 575 // Once the chassis state is back to on we can clear this file. 576 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0); 577 size++; // null 578 std::unique_ptr<char[]> chassisFile(new char[size]); 579 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0); 580 if (std::filesystem::exists(chassisFile.get())) 581 { 582 std::filesystem::remove(chassisFile.get()); 583 } 584 } 585 586 return 0; 587 } 588 589 Chassis::Transition Chassis::requestedPowerTransition(Transition value) 590 { 591 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}", 592 "REQ_POWER_TRAN", value); 593 startUnit(systemdTargetTable.find(value)->second); 594 return server::Chassis::requestedPowerTransition(value); 595 } 596 597 Chassis::PowerState Chassis::currentPowerState(PowerState value) 598 { 599 PowerState chassisPowerState; 600 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE", 601 value); 602 603 chassisPowerState = server::Chassis::currentPowerState(value); 604 if (chassisPowerState == PowerState::On) 605 { 606 pohTimer.resetRemaining(); 607 } 608 return chassisPowerState; 609 } 610 611 uint32_t Chassis::pohCounter(uint32_t value) 612 { 613 if (value != pohCounter()) 614 { 615 ChassisInherit::pohCounter(value); 616 serializePOH(); 617 } 618 return pohCounter(); 619 } 620 621 void Chassis::pohCallback() 622 { 623 if (ChassisInherit::currentPowerState() == PowerState::On) 624 { 625 pohCounter(pohCounter() + 1); 626 } 627 } 628 629 void Chassis::restorePOHCounter() 630 { 631 uint32_t counter; 632 if (!deserializePOH(counter)) 633 { 634 // set to default value 635 pohCounter(0); 636 } 637 else 638 { 639 pohCounter(counter); 640 } 641 } 642 643 fs::path Chassis::serializePOH() 644 { 645 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)}; 646 std::ofstream os(path.c_str(), std::ios::binary); 647 cereal::JSONOutputArchive oarchive(os); 648 oarchive(pohCounter()); 649 return path; 650 } 651 652 bool Chassis::deserializePOH(uint32_t& pohCounter) 653 { 654 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)}; 655 try 656 { 657 if (fs::exists(path)) 658 { 659 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 660 cereal::JSONInputArchive iarchive(is); 661 iarchive(pohCounter); 662 return true; 663 } 664 return false; 665 } 666 catch (const cereal::Exception& e) 667 { 668 error("deserialize exception: {ERROR}", "ERROR", e); 669 fs::remove(path); 670 return false; 671 } 672 catch (const fs::filesystem_error& e) 673 { 674 return false; 675 } 676 677 return false; 678 } 679 680 void Chassis::startPOHCounter() 681 { 682 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path(); 683 fs::create_directories(dir); 684 685 try 686 { 687 auto event = sdeventplus::Event::get_default(); 688 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 689 event.loop(); 690 } 691 catch (const sdeventplus::SdEventError& e) 692 { 693 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR", 694 e); 695 phosphor::logging::commit<InternalFailure>(); 696 } 697 } 698 699 void Chassis::serializeStateChangeTime() 700 { 701 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)}; 702 std::ofstream os(path.c_str(), std::ios::binary); 703 cereal::JSONOutputArchive oarchive(os); 704 705 oarchive(ChassisInherit::lastStateChangeTime(), 706 ChassisInherit::currentPowerState()); 707 } 708 709 bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state) 710 { 711 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)}; 712 713 try 714 { 715 if (fs::exists(path)) 716 { 717 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 718 cereal::JSONInputArchive iarchive(is); 719 iarchive(time, state); 720 return true; 721 } 722 } 723 catch (const std::exception& e) 724 { 725 error("deserialize exception: {ERROR}", "ERROR", e); 726 fs::remove(path); 727 } 728 729 return false; 730 } 731 732 void Chassis::restoreChassisStateChangeTime() 733 { 734 uint64_t time; 735 PowerState state; 736 737 if (!deserializeStateChangeTime(time, state)) 738 { 739 ChassisInherit::lastStateChangeTime(0); 740 } 741 else 742 { 743 ChassisInherit::lastStateChangeTime(time); 744 } 745 } 746 747 void Chassis::setStateChangeTime() 748 { 749 using namespace std::chrono; 750 uint64_t lastTime; 751 PowerState lastState; 752 753 auto now = 754 duration_cast<milliseconds>(system_clock::now().time_since_epoch()) 755 .count(); 756 757 // If power is on when the BMC is rebooted, this function will get called 758 // because sysStateChange() runs. Since the power state didn't change 759 // in this case, neither should the state change time, so check that 760 // the power state actually did change here. 761 if (deserializeStateChangeTime(lastTime, lastState)) 762 { 763 if (lastState == ChassisInherit::currentPowerState()) 764 { 765 return; 766 } 767 } 768 769 ChassisInherit::lastStateChangeTime(now); 770 serializeStateChangeTime(); 771 } 772 773 bool Chassis::standbyVoltageRegulatorFault() 774 { 775 bool regulatorFault = false; 776 777 // find standby voltage regulator fault via gpiog 778 779 auto gpioval = utils::getGpioValue("regulator-standby-faulted"); 780 781 if (-1 == gpioval) 782 { 783 error("Failed reading regulator-standby-faulted GPIO"); 784 } 785 786 if (1 == gpioval) 787 { 788 info("Detected standby voltage regulator fault"); 789 regulatorFault = true; 790 } 791 792 return regulatorFault; 793 } 794 795 } // namespace manager 796 } // namespace state 797 } // namespace phosphor 798