| #include "tlbmc/host_state/power_control.h" |
| |
| #include <array> |
| #include <chrono> // NOLINT |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/functional/any_invocable.h" |
| #include "absl/log/log.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/synchronization/mutex.h" |
| #include "absl/time/time.h" |
| #include "absl/types/span.h" |
| #include "boost/asio/steady_timer.hpp" // NOLINT |
| #include "g3/macros.h" |
| #include "thread/thread.h" |
| #include "time/clock.h" |
| #include "gbmc_sel_defs.h" |
| #include "gbmc_sel_pub.h" |
| #include <nlohmann/json.hpp> |
| #include "tlbmc/collector/gpio_collector.h" |
| #include "gpio_config.pb.h" |
| #include "power_control.pb.h" |
| #include "resource.pb.h" |
| |
| namespace milotic_tlbmc { |
| |
| namespace { |
| struct PowerStateSelInfo { |
| absl::string_view message_suffix; |
| absl::string_view state_value; |
| }; |
| |
| PowerStateSelInfo GetPowerStateSelInfo(PowerState state) { |
| switch (state) { |
| case PowerState::POWER_STATE_ON: |
| return {.message_suffix = "ON", .state_value = "On"}; |
| case PowerState::POWER_STATE_OFF: |
| return {.message_suffix = "OFF", .state_value = "Off"}; |
| case PowerState::POWER_STATE_WAIT_FOR_POWER_OK: |
| return {.message_suffix = "Wait for Power OK", |
| .state_value = "WaitForPowerOk"}; |
| case PowerState::POWER_STATE_TRANSITION_TO_OFF: |
| return {.message_suffix = "Transition to Off", |
| .state_value = "TransitionToOff"}; |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF: |
| return {.message_suffix = "Graceful Transition to Off", |
| .state_value = "GracefulTransitionToOff"}; |
| case PowerState::POWER_STATE_CYCLE_OFF: |
| return {.message_suffix = "Cycle Off", .state_value = "CycleOff"}; |
| case PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF: |
| return {.message_suffix = "Transition to Cycle Off", |
| .state_value = "TransitionToCycleOff"}; |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF: |
| return {.message_suffix = "Graceful Transition to Cycle Off", |
| .state_value = "GracefulTransitionToCycleOff"}; |
| case PowerState::POWER_STATE_CHECK_FOR_WARM_RESET: |
| return {.message_suffix = "Check for Warm Reset", |
| .state_value = "CheckForWarmReset"}; |
| default: |
| return {.message_suffix = "Unknown", .state_value = "Unknown"}; |
| } |
| } |
| } // namespace |
| |
| constexpr auto kDefaultTimerMap = |
| std::to_array<std::pair<absl::string_view, uint32_t>>( |
| {{"ForceOffPulseMs", 15000}, |
| {"GracefulPowerOffS", 300}, |
| {"PowerCycleMs", 5000}, |
| {"PowerOKWatchdogMs", 8000}, |
| {"PowerPulseMs", 200}, |
| {"ResetPulseMs", 500}, |
| {"WarmResetCheckMs", 500}}); |
| |
| absl::StatusOr<std::unique_ptr<PowerControlGpio>> PowerControlGpio::Create( |
| const Params& params, |
| gbmc_sel_framework::JournalSenderFunc journal_sender) { |
| PowerControlConfig power_control_config = params.power_control_config; |
| GpioCollector* gpio_collector = params.gpio_collector; |
| HostState host_state; |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| bool power_ok, |
| gpio_collector->GetValue(power_control_config.power_ok_gpio_name())); |
| LOG(INFO) << "Power OK: " << power_ok << " " |
| << power_control_config.power_ok_gpio_name(); |
| if (power_ok) { |
| host_state.set_power_state(PowerState::POWER_STATE_ON); |
| } else { |
| host_state.set_power_state(PowerState::POWER_STATE_OFF); |
| } |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| bool post_complete, |
| gpio_collector->GetValue(power_control_config.post_complete_gpio_name())); |
| LOG(INFO) << "Post Complete: " << post_complete << " " |
| << power_control_config.post_complete_gpio_name(); |
| if (post_complete) { |
| host_state.set_os_state(OSState::OS_STATE_STANDBY); |
| } else { |
| host_state.set_os_state(OSState::OS_STATE_INACTIVE); |
| } |
| |
| std::shared_ptr<boost::asio::io_context> io_context = |
| std::make_shared<boost::asio::io_context>(); |
| boost::asio::executor_work_guard<boost::asio::io_context::executor_type> |
| work_guard(boost::asio::make_work_guard(*io_context)); |
| |
| boost::asio::steady_timer power_cycle_timer(*io_context); |
| boost::asio::steady_timer power_ok_watchdog_timer(*io_context); |
| boost::asio::steady_timer graceful_power_off_timer(*io_context); |
| boost::asio::steady_timer warm_reset_check_timer(*io_context); |
| |
| auto thread_manager = |
| std::make_unique<PowerControlThreadManager>(params.clock); |
| thread_manager->threads.push_back( |
| params.thread_factory->New([io_context]() { io_context->run(); })); |
| thread_manager->work_guards.push_back(std::move(work_guard)); |
| thread_manager->io_contexts.push_back(std::move(io_context)); |
| |
| // Update the timer map with default values if not already present. |
| for (const auto& [key, value] : kDefaultTimerMap) { |
| if (!power_control_config.timer_map().contains(key)) { |
| power_control_config.mutable_timer_map()->insert( |
| {std::string(key), value}); |
| } |
| } |
| |
| return absl::WrapUnique(new PowerControlGpio( |
| std::move(host_state), power_control_config, gpio_collector, |
| std::move(thread_manager), std::move(power_cycle_timer), |
| std::move(power_ok_watchdog_timer), std::move(graceful_power_off_timer), |
| std::move(warm_reset_check_timer), params.clock, |
| std::move(journal_sender))); |
| } |
| |
| PowerControlGpio::PowerControlGpio( |
| HostState host_state, PowerControlConfig power_control_config, |
| GpioCollector* gpio_collector, |
| std::unique_ptr<PowerControlThreadManager>&& thread_manager, |
| boost::asio::steady_timer&& power_cycle_timer, |
| boost::asio::steady_timer&& power_ok_watchdog_timer, |
| boost::asio::steady_timer&& graceful_power_off_timer, |
| boost::asio::steady_timer&& warm_reset_check_timer, ecclesia::Clock* clock, |
| gbmc_sel_framework::JournalSenderFunc journal_sender) |
| : host_state_(std::move(host_state)), |
| power_control_config_(std::move(power_control_config)), |
| gpio_collector_(gpio_collector), |
| thread_manager_(std::move(thread_manager)), |
| power_cycle_timer_(std::move(power_cycle_timer)), |
| power_ok_watchdog_timer_(std::move(power_ok_watchdog_timer)), |
| graceful_power_off_timer_(std::move(graceful_power_off_timer)), |
| warm_reset_check_timer_(std::move(warm_reset_check_timer)), |
| clock_(clock), |
| journal_sender_(std::move(journal_sender)) { |
| if (clock_ != nullptr) { |
| last_state_change_time_ = clock_->Now(); |
| } |
| } |
| |
| void PowerControlGpio::PowerOkHandler(GpioEventType event) { |
| LOG(INFO) << "Power OK Handler: " << event << " " |
| << power_control_config_.power_ok_gpio_name(); |
| absl::MutexLock lock(host_state_mutex_); |
| absl::MutexLock power_state_callback_lock(power_state_callback_mutex_); |
| switch (event) { |
| // assuming asserted now |
| case GpioEventType::GPIO_EVENT_RISING: |
| for (const auto& cb : host_power_off_to_on_callbacks_) { |
| cb(/*setup_run=*/false); |
| } |
| switch (host_state_.power_state()) { |
| case PowerState::POWER_STATE_OFF: |
| case PowerState::POWER_STATE_CYCLE_OFF: |
| SetPowerState(PowerState::POWER_STATE_ON); |
| break; |
| case PowerState::POWER_STATE_WAIT_FOR_POWER_OK: |
| SetPowerState(PowerState::POWER_STATE_ON); |
| power_ok_watchdog_timer_.cancel(); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| break; |
| // assuming deasserted now |
| case GpioEventType::GPIO_EVENT_FALLING: |
| for (const auto& cb : host_power_on_to_off_callbacks_) { |
| cb(/*setup_run=*/false); |
| } |
| switch (host_state_.power_state()) { |
| case PowerState::POWER_STATE_CHECK_FOR_WARM_RESET: |
| warm_reset_check_timer_.cancel(); |
| SetPowerState(PowerState::POWER_STATE_OFF); |
| break; |
| case PowerState::POWER_STATE_ON: |
| case PowerState::POWER_STATE_TRANSITION_TO_OFF: |
| SetPowerState(PowerState::POWER_STATE_OFF); |
| break; |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF: |
| SetPowerState(PowerState::POWER_STATE_CYCLE_OFF); |
| graceful_power_off_timer_.cancel(); |
| StartPowerCycleTimer(); |
| break; |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF: |
| SetPowerState(PowerState::POWER_STATE_OFF); |
| graceful_power_off_timer_.cancel(); |
| break; |
| case PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF: |
| LOG(INFO) << "Enter POWER_STATE_CYCLE_OFF"; |
| SetPowerState(PowerState::POWER_STATE_CYCLE_OFF); |
| StartPowerCycleTimer(); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| void PowerControlGpio::PostCompleteHandler(GpioEventType event) { |
| LOG(INFO) << "Post Complete Handler: " << event << " " |
| << power_control_config_.post_complete_gpio_name(); |
| absl::MutexLock lock(host_state_mutex_); |
| absl::MutexLock os_state_callback_lock(os_state_callback_mutex_); |
| switch (event) { |
| // assuming asserted now |
| case GpioEventType::GPIO_EVENT_RISING: |
| host_state_.set_os_state(OSState::OS_STATE_STANDBY); |
| PublishSel("Host OS State: Standby (Post Complete Assert)", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_OS_STATE=Standby"}); |
| for (const auto& cb : host_os_inactive_to_standby_callbacks_) { |
| cb(/*setup_run=*/false); |
| } |
| break; |
| // assuming deasserted now |
| case GpioEventType::GPIO_EVENT_FALLING: |
| host_state_.set_os_state(OSState::OS_STATE_INACTIVE); |
| PublishSel("Host OS State: Inactive (Post Complete Deassert)", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_OS_STATE=Inactive"}); |
| for (const auto& cb : host_os_standby_to_inactive_callbacks_) { |
| cb(/*setup_run=*/false); |
| } |
| if (host_state_.power_state() == PowerState::POWER_STATE_ON) { |
| SetPowerState(PowerState::POWER_STATE_CHECK_FOR_WARM_RESET); |
| StartWarmResetCheckTimer(); |
| }; |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| void PowerControlGpio::StartPowerCycleTimer() { |
| power_cycle_timer_.expires_after(std::chrono::milliseconds( |
| power_control_config_.timer_map().at("PowerCycleMs"))); |
| power_cycle_timer_.async_wait([this](const boost::system::error_code ec) { |
| if (ec) { |
| if (ec != boost::asio::error::operation_aborted) { |
| LOG(ERROR) << "Power-cycle async_wait failed: " << ec.message(); |
| } |
| return; |
| } |
| LOG(INFO) << "Power-cycle timer expired"; |
| PowerCycleTimerExpired(); |
| }); |
| } |
| |
| void PowerControlGpio::PowerCycleTimerExpired() { |
| absl::MutexLock lock(host_state_mutex_); |
| if (host_state_.power_state() == PowerState::POWER_STATE_CYCLE_OFF) { |
| LOG(INFO) << "Enter POWER_STATE_WAIT_FOR_POWER_OK"; |
| SetPowerState(PowerState::POWER_STATE_WAIT_FOR_POWER_OK); |
| StartPowerOKWatchdogTimer(); |
| PowerOn(); |
| } |
| } |
| |
| void PowerControlGpio::StartPowerOKWatchdogTimer() { |
| power_ok_watchdog_timer_.expires_after(std::chrono::milliseconds( |
| power_control_config_.timer_map().at("PowerOKWatchdogMs"))); |
| power_ok_watchdog_timer_.async_wait([this]( |
| const boost::system::error_code ec) { |
| if (ec) { |
| if (ec != boost::asio::error::operation_aborted) { |
| LOG(ERROR) << "Power-OK watchdog async_wait failed: " << ec.message(); |
| } |
| return; |
| } |
| PowerOkWatchdogTimerExpired(); |
| }); |
| } |
| |
| void PowerControlGpio::PowerOkWatchdogTimerExpired() { |
| absl::MutexLock lock(host_state_mutex_); |
| if (host_state_.power_state() == PowerState::POWER_STATE_WAIT_FOR_POWER_OK) { |
| PublishSel("Power OK Watchdog Expired", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_WARNING, |
| {"GBMC_POWER_CONTROL_ERROR_TYPE=PowerOkWatchdogTimeout"}); |
| SetPowerState(PowerState::POWER_STATE_OFF); |
| } |
| } |
| |
| void PowerControlGpio::StartGracefulPowerOffTimer() { |
| graceful_power_off_timer_.expires_after(std::chrono::seconds( |
| power_control_config_.timer_map().at("GracefulPowerOffS"))); |
| graceful_power_off_timer_.async_wait([this]( |
| const boost::system::error_code ec) { |
| if (ec) { |
| if (ec != boost::asio::error::operation_aborted) { |
| LOG(ERROR) << "Graceful power-off async_wait failed: " << ec.message(); |
| } |
| return; |
| } |
| GracefulPowerOffTimerExpired(); |
| }); |
| } |
| |
| void PowerControlGpio::GracefulPowerOffTimerExpired() { |
| absl::MutexLock lock(host_state_mutex_); |
| switch (host_state_.power_state()) { |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF: |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF: |
| PublishSel("Graceful Power Off Timed Out", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_WARNING, |
| {"GBMC_POWER_CONTROL_ERROR_TYPE=GracefulPowerOffTimeout"}); |
| SetPowerState(PowerState::POWER_STATE_ON); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| void PowerControlGpio::StartWarmResetCheckTimer() { |
| warm_reset_check_timer_.expires_after(std::chrono::milliseconds( |
| power_control_config_.timer_map().at("WarmResetCheckMs"))); |
| warm_reset_check_timer_.async_wait([this]( |
| const boost::system::error_code ec) { |
| if (ec) { |
| if (ec != boost::asio::error::operation_aborted) { |
| LOG(ERROR) << "Warm reset check async_wait failed: " << ec.message(); |
| } |
| return; |
| } |
| WarmResetCheckTimerExpired(); |
| }); |
| } |
| |
| void PowerControlGpio::WarmResetCheckTimerExpired() { |
| absl::MutexLock lock(host_state_mutex_); |
| if (host_state_.power_state() == |
| PowerState::POWER_STATE_CHECK_FOR_WARM_RESET) { |
| SetPowerState(PowerState::POWER_STATE_ON); |
| } |
| } |
| |
| void PowerControlGpio::PowerOn() { |
| absl::Status status = gpio_collector_->SetActiveForDuration( |
| power_control_config_.power_out_gpio_name(), |
| absl::Milliseconds(power_control_config_.timer_map().at("PowerPulseMs"))); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to press power-on GPIO: " << status; |
| } |
| } |
| |
| void PowerControlGpio::ForcePowerOff() { |
| absl::Status status = gpio_collector_->SetActiveForDuration( |
| power_control_config_.power_out_gpio_name(), |
| absl::Milliseconds( |
| power_control_config_.timer_map().at("ForceOffPulseMs"))); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to press force-power-off GPIO: " << status; |
| } |
| } |
| |
| void PowerControlGpio::GracefulPowerOff() { |
| absl::Status status = gpio_collector_->SetActiveForDuration( |
| power_control_config_.power_out_gpio_name(), |
| absl::Milliseconds(power_control_config_.timer_map().at("PowerPulseMs"))); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to press graceful-power-off GPIO: " << status; |
| } |
| } |
| |
| void PowerControlGpio::Reset() { |
| absl::Status status = gpio_collector_->SetActiveForDuration( |
| power_control_config_.reset_out_gpio_name(), |
| absl::Milliseconds(power_control_config_.timer_map().at("ResetPulseMs"))); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to press reset GPIO: " << status; |
| } |
| } |
| |
| void PowerControlGpio::PowerOnRequest() { |
| absl::MutexLock lock(host_state_mutex_); |
| PublishSel("Power On Request", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_POWER_CONTROL_REQUEST_TYPE=PowerOn"}); |
| if (host_state_.power_state() == PowerState::POWER_STATE_OFF) { |
| SetPowerState(PowerState::POWER_STATE_WAIT_FOR_POWER_OK); |
| StartPowerOKWatchdogTimer(); |
| PowerOn(); |
| } |
| } |
| |
| void PowerControlGpio::PowerOffRequest() { |
| absl::MutexLock lock(host_state_mutex_); |
| PublishSel("Power Off Request", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_POWER_CONTROL_REQUEST_TYPE=PowerOff"}); |
| switch (host_state_.power_state()) { |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF: |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF: |
| SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_OFF); |
| graceful_power_off_timer_.cancel(); |
| ForcePowerOff(); |
| break; |
| case PowerState::POWER_STATE_ON: |
| SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_OFF); |
| ForcePowerOff(); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| void PowerControlGpio::PowerCycleRequest() { |
| absl::MutexLock lock(host_state_mutex_); |
| PublishSel("Power Cycle Request", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_POWER_CONTROL_REQUEST_TYPE=PowerCycle"}); |
| switch (host_state_.power_state()) { |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF: |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF: |
| SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF); |
| graceful_power_off_timer_.cancel(); |
| ForcePowerOff(); |
| break; |
| case PowerState::POWER_STATE_ON: |
| LOG(INFO) << "Enter TRANSITION_TO_CYCLE_OFF"; |
| SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF); |
| ForcePowerOff(); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| void PowerControlGpio::ResetRequest() { |
| absl::MutexLock lock(host_state_mutex_); |
| PublishSel("Reset Request", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_POWER_CONTROL_REQUEST_TYPE=Reset"}); |
| switch (host_state_.power_state()) { |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF: |
| case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF: |
| SetPowerState(PowerState::POWER_STATE_ON); |
| graceful_power_off_timer_.cancel(); |
| Reset(); |
| break; |
| case PowerState::POWER_STATE_ON: |
| Reset(); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| void PowerControlGpio::GracefulPowerOffRequest() { |
| absl::MutexLock lock(host_state_mutex_); |
| PublishSel("Graceful Power Off Request", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_POWER_CONTROL_REQUEST_TYPE=GracefulPowerOff"}); |
| if (host_state_.power_state() == PowerState::POWER_STATE_ON) { |
| SetPowerState(PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF); |
| StartGracefulPowerOffTimer(); |
| GracefulPowerOff(); |
| } |
| } |
| |
| void PowerControlGpio::GracefulPowerCycleRequest() { |
| absl::MutexLock lock(host_state_mutex_); |
| PublishSel("Graceful Power Cycle Request", |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {"GBMC_POWER_CONTROL_REQUEST_TYPE=GracefulPowerCycle"}); |
| if (host_state_.power_state() == PowerState::POWER_STATE_ON) { |
| SetPowerState(PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF); |
| StartGracefulPowerOffTimer(); |
| GracefulPowerOff(); |
| } |
| } |
| |
| void PowerControlGpio::SystemResetRequest(ResetType reset_type, |
| absl::Duration delay) { |
| LOG(INFO) << "System reset request: " << reset_type << " " << delay; |
| switch (reset_type) { |
| case ResetType::RESET_TYPE_FORCE_OFF: |
| PowerOffRequest(); |
| break; |
| case ResetType::RESET_TYPE_FORCE_ON: |
| case ResetType::RESET_TYPE_ON: |
| PowerOnRequest(); |
| break; |
| case ResetType::RESET_TYPE_FORCE_RESTART: |
| ResetRequest(); |
| break; |
| case ResetType::RESET_TYPE_GRACEFUL_RESTART: |
| GracefulPowerCycleRequest(); |
| break; |
| case ResetType::RESET_TYPE_GRACEFUL_SHUTDOWN: |
| GracefulPowerOffRequest(); |
| break; |
| case ResetType::RESET_TYPE_POWER_CYCLE: |
| PowerCycleRequest(); |
| break; |
| default: |
| // Do nothing |
| break; |
| } |
| } |
| |
| nlohmann::json PowerControlGpio::ToJson() { |
| nlohmann::json json; |
| absl::MutexLock lock(host_state_mutex_); |
| json["OSState"] = OSState_Name(host_state_.os_state()); |
| json["PowerState"] = PowerState_Name(host_state_.power_state()); |
| json["LastResetTime"] = absl::FormatTime( |
| absl::RFC3339_sec, last_state_change_time_, absl::UTCTimeZone()); |
| return json; |
| } |
| |
| PowerControlGpio::~PowerControlGpio() { |
| gpio_collector_ = nullptr; |
| power_cycle_timer_.cancel(); |
| power_ok_watchdog_timer_.cancel(); |
| graceful_power_off_timer_.cancel(); |
| warm_reset_check_timer_.cancel(); |
| } |
| |
| void PowerControlGpio::SetPowerState(PowerState state) { |
| if (host_state_.power_state() != state) { |
| host_state_.set_power_state(state); |
| if (clock_ != nullptr) { |
| last_state_change_time_ = clock_->Now(); |
| } |
| PowerStateSelInfo sel_info = GetPowerStateSelInfo(state); |
| PublishSel(absl::StrCat("Host Power State: ", sel_info.message_suffix), |
| gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO, |
| {absl::StrCat("GBMC_POWER_STATE=", sel_info.state_value)}); |
| } |
| } |
| |
| void PowerControlGpio::PublishSel( |
| absl::string_view message, gbmc_sel_framework::EventSeverity severity, |
| absl::Span<const absl::string_view> additional_data) { |
| gbmc_sel_framework::EventSourceType source_type = GetEventSourceType(); |
| gbmc_sel_framework::EventSourceComponent component = |
| gbmc_sel_framework::EventSourceComponent::EVENT_SOURCE_COMPONENT_CPU; |
| gbmc_sel_framework::EventAction action = |
| gbmc_sel_framework::EventAction::EVENT_ACTION_NULL; |
| |
| std::vector<absl::string_view> additional_data_views; |
| additional_data_views.reserve(additional_data.size() + 1); |
| additional_data_views.emplace_back("GBMC_SYSTEM_EVENT_TYPE=CPUEvent"); |
| for (const auto& data : additional_data) { |
| additional_data_views.push_back(data); |
| } |
| |
| absl::Status status; |
| if (journal_sender_ != nullptr) { |
| status = gbmc_sel_framework::PublishGBMCSystemEvent( |
| source_type, component, severity, action, message, |
| additional_data_views, [this](const struct iovec* iov, int iovcnt) { |
| return journal_sender_(iov, iovcnt); |
| }); |
| } else { |
| status = gbmc_sel_framework::PublishGBMCSystemEvent( |
| source_type, component, severity, action, message, |
| additional_data_views); |
| } |
| if (!status.ok()) { |
| LOG(INFO) << "Failed to publish power control SEL event: " << status; |
| } |
| } |
| |
| gbmc_sel_framework::EventSourceType PowerControlGpio::GetEventSourceType() |
| const { |
| if (power_control_config_.host_id() == "system2") { |
| return gbmc_sel_framework::EventSourceType::EVENT_SOURCE_HPM1; |
| } |
| return gbmc_sel_framework::EventSourceType::EVENT_SOURCE_HPM0; |
| } |
| |
| void PowerControlGpio::Start() { |
| absl::MutexLock lock(host_state_mutex_); |
| absl::MutexLock power_state_callback_lock(power_state_callback_mutex_); |
| absl::MutexLock os_state_callback_lock(os_state_callback_mutex_); |
| if (host_state_.power_state() == PowerState::POWER_STATE_ON) { |
| for (const auto& cb : host_power_off_to_on_callbacks_) { |
| cb(/*setup_run=*/true); |
| } |
| } else if (host_state_.power_state() == PowerState::POWER_STATE_OFF || |
| host_state_.power_state() == PowerState::POWER_STATE_CYCLE_OFF || |
| host_state_.power_state() == |
| PowerState::POWER_STATE_WAIT_FOR_POWER_OK) { |
| for (const auto& cb : host_power_on_to_off_callbacks_) { |
| cb(/*setup_run=*/true); |
| } |
| } |
| |
| if (host_state_.os_state() == OSState::OS_STATE_STANDBY) { |
| for (const auto& cb : host_os_inactive_to_standby_callbacks_) { |
| cb(/*setup_run=*/true); |
| } |
| } else if (host_state_.os_state() == OSState::OS_STATE_INACTIVE) { |
| for (const auto& cb : host_os_standby_to_inactive_callbacks_) { |
| cb(/*setup_run=*/true); |
| } |
| } |
| } |
| |
| void PowerControlGpio::RegisterHostPowerOnToOffCallback( |
| absl::AnyInvocable<void(bool) const> callback) { |
| absl::MutexLock lock(power_state_callback_mutex_); |
| host_power_on_to_off_callbacks_.push_back(std::move(callback)); |
| } |
| |
| void PowerControlGpio::RegisterHostPowerOffToOnCallback( |
| absl::AnyInvocable<void(bool) const> callback) { |
| absl::MutexLock lock(power_state_callback_mutex_); |
| host_power_off_to_on_callbacks_.push_back(std::move(callback)); |
| } |
| |
| void PowerControlGpio::RegisterHostOSInactiveToStandbyCallback( |
| absl::AnyInvocable<void(bool) const> callback) { |
| absl::MutexLock lock(os_state_callback_mutex_); |
| host_os_inactive_to_standby_callbacks_.push_back(std::move(callback)); |
| } |
| |
| void PowerControlGpio::RegisterHostOSStandbyToInactiveCallback( |
| absl::AnyInvocable<void(bool) const> callback) { |
| absl::MutexLock lock(os_state_callback_mutex_); |
| host_os_standby_to_inactive_callbacks_.push_back(std::move(callback)); |
| } |
| |
| absl::Time PowerControlGpio::GetLastStateChangeTime() { |
| absl::MutexLock lock(host_state_mutex_); |
| return last_state_change_time_; |
| } |
| |
| PowerControlThreadManager::~PowerControlThreadManager() { |
| // Stop the scheduler first. |
| task_scheduler->Stop(); |
| |
| // Finally join all threads. |
| for (const std::shared_ptr<boost::asio::io_context>& io_context : |
| io_contexts) { |
| io_context->stop(); |
| } |
| |
| for (const std::unique_ptr<ecclesia::ThreadInterface>& thread : threads) { |
| thread->Join(); |
| } |
| } |
| |
| } // namespace milotic_tlbmc |