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