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