Files
OpenBMC/meta-luxshare/recipes-phosphor/ipmi/phosphor-ipmi-host/0001-Add-power-cycle-trigger-async-firmware-update.patch
T

264 lines
9.2 KiB
Diff
Raw Normal View History

2026-04-23 17:07:55 +08:00
From 57dc7fbdd1dceb8c82512dae754bf4a8370a4010 Mon Sep 17 00:00:00 2001
From: roly <Rolyli.Li@luxshare-ict.com>
Date: Fri, 7 Feb 2025 14:42:30 +0800
Subject: [PATCH] Add power cycle trigger async firmware update
---
chassishandler.cpp | 216 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 216 insertions(+)
diff --git a/chassishandler.cpp b/chassishandler.cpp
index 89d3e00..8e68ac5 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -8,6 +8,7 @@
#include <mapper.h>
#include <netinet/in.h>
+#include <boost/asio.hpp>
#include <ipmid/api.hpp>
#include <ipmid/types.hpp>
#include <ipmid/utils.hpp>
@@ -813,6 +814,196 @@ ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout,
return ipmi::responseSuccess();
}
+//------------------------------------------------
+// Is power on (check power state before async update)
+//------------------------------------------------
+bool isPowerOn()
+{
+ std::variant<std::string> msg;
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ try
+ {
+ auto method = bus.new_method_call("xyz.openbmc_project.State.Chassis",
+ "/xyz/openbmc_project/state/chassis0",
+ "org.freedesktop.DBus.Properties",
+ "Get");
+ method.append("xyz.openbmc_project.State.Chassis");
+ method.append("CurrentPowerState");
+ auto reply = bus.call(method);
+ reply.read(msg);
+ return std::get<std::string>(msg).find("Off") != std::string::npos
+ ? false
+ : true;
+ }
+ catch (const std::exception& /*ex*/)
+ {
+ log<level::ERR>("Can not get power status");
+ return true;
+ }
+}
+
+//------------------------------------------------
+// Async update start (return status or no value))
+//------------------------------------------------
+std::string asyncUpdateStart()
+{
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ std::string asyncStatus{};
+ auto method = bus.new_method_call(
+ "xyz.openbmc_project.Software.AsyncUpdate",
+ "/xyz/openbmc_project/Software/AsyncUpdate",
+ "xyz.openbmc_project.Software.AsyncUpdate", "Start");
+ try
+ {
+ auto reply = bus.call(method);
+ reply.read(asyncStatus);
+ return asyncStatus;
+ }
+ catch (const sdbusplus::exception::SdBusError& /*ex*/)
+ {
+ log<level::ERR>("Error in starting async update");
+ return "";
+ }
+}
+
+//------------------------------------------------
+// Get async update status (return status or no value)
+//------------------------------------------------
+std::string asyncUpdateStatus()
+{
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ try
+ {
+ auto status = ipmi::getDbusProperty(
+ bus, "xyz.openbmc_project.Software.AsyncUpdate",
+ "/xyz/openbmc_project/Software/AsyncUpdate",
+ "xyz.openbmc_project.Software.AsyncUpdate", "AsyncUpdateStatus");
+ return std::get<std::string>(status);
+ }
+ catch (const sdbusplus::exception::SdBusError& /*ex*/)
+ {
+ log<level::ERR>("Can not get async update status");
+ return "";
+ }
+}
+
+int initiateHostStateTransition(ipmi::Context::ptr& ctx,
+ State::Host::Transition transition);
+int initiateChassisStateTransition(ipmi::Context::ptr& ctx,
+ State::Chassis::Transition transition);
+//---------------------------------------------------------------------------
+// if power cycle wait for power on
+//---------------------------------------------------------------------------
+void waitForUpdateDone()
+{
+ static boost::asio::steady_timer timer(*getIoContext());
+ timer.expires_after(boost::asio::chrono::seconds(15));
+ timer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec)
+ {
+ return;
+ }
+ if (asyncUpdateStatus() != "Idle")
+ {
+ waitForUpdateDone();
+ return;
+ }
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ try
+ {
+ auto method =
+ bus.new_method_call("xyz.openbmc_project.State.Host",
+ "/xyz/openbmc_project/state/host0",
+ "org.freedesktop.DBus.Properties", "Set");
+
+ method.append("xyz.openbmc_project.State.Host");
+ method.append("RequestedHostTransition");
+ method.append(std::variant<std::string>{
+ "xyz.openbmc_project.State.Host.Transition.On"});
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& /*ex*/)
+ {
+ log<level::ERR>("Can not power On after async update");
+ }
+ });
+}
+//---------------------------------------------------------------------------
+// wait for power off to start update
+//---------------------------------------------------------------------------
+void waitForStartUpdate(bool doPowerOn)
+{
+ static boost::asio::steady_timer timer(*getIoContext());
+ timer.expires_after(boost::asio::chrono::seconds(15));
+ timer.async_wait(
+ [doPowerOn](const boost::system::error_code& ec) {
+ if (ec)
+ {
+ return;
+ }
+ if (isPowerOn()) // power status is not "off"
+ {
+ waitForStartUpdate(doPowerOn);
+ return;
+ }
+ try
+ {
+ asyncUpdateStart();
+ if (doPowerOn)
+ {
+ waitForUpdateDone();
+ }
+ }
+ catch (const sdbusplus::exception::SdBusError& /*ex*/)
+ {
+ log<level::ERR>("Can not start update");
+ }
+ });
+}
+
+enum class PowerControlAsyncStatus
+{
+ active,
+ running,
+ idle
+};
+
+//---------------------------------------------------------------------------
+// hook host power control to trigger async update
+// return true if async-update is not in progress
+//---------------------------------------------------------------------------
+PowerControlAsyncStatus
+ processPowerControlHost(ipmi::Context::ptr& ctx,
+ State::Host::Transition transition)
+{
+ std::string asyncStatus = asyncUpdateStatus();
+ auto doPowerOn = isPowerOn();
+ switch (transition)
+ {
+ case State::Host::Transition::Reboot:
+ if (asyncStatus == "Waiting")
+ {
+ initiateChassisStateTransition(ctx,
+ State::Chassis::Transition::Off);
+ waitForStartUpdate(doPowerOn);
+ return PowerControlAsyncStatus::active;
+ }
+ else if (asyncStatus == "Running")
+ {
+ return PowerControlAsyncStatus::running;
+ }
+ break;
+ case State::Host::Transition::On:
+ if (asyncStatus == "Running")
+ {
+ return PowerControlAsyncStatus::running;
+ }
+ break;
+ default:
+ break;
+ }
+ return PowerControlAsyncStatus::idle;
+}
//------------------------------------------
// Calls into Host State Manager Dbus object
//------------------------------------------
@@ -1363,9 +1554,18 @@ ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx,
uint8_t chassisControl)
{
int rc = 0;
+ PowerControlAsyncStatus asyncUpdateStatus = PowerControlAsyncStatus::idle;
switch (chassisControl)
{
case CMD_POWER_ON:
+ asyncUpdateStatus =
+ processPowerControlHost(ctx, State::Host::Transition::On);
+ if (asyncUpdateStatus != PowerControlAsyncStatus::idle)
+ {
+ log<level::ERR>(
+ "Power-on opertion is not allowed during the upgrade process");
+ return ipmi::responseCommandNotAvailable();
+ }
rc = initiateHostStateTransition(ctx, State::Host::Transition::On);
break;
case CMD_POWER_OFF:
@@ -1377,6 +1577,22 @@ ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx,
ctx, State::Host::Transition::ForceWarmReboot);
break;
case CMD_POWER_CYCLE:
+ asyncUpdateStatus =
+ processPowerControlHost(ctx, State::Host::Transition::Reboot);
+ if (asyncUpdateStatus == PowerControlAsyncStatus::active)
+ {
+ log<level::INFO>(
+ "Power-cycle operation will activate the async-update, let the async-update flow to do the power-cycle");
+ rc = ipmi::ccSuccess;
+ break;
+ }
+ else if (asyncUpdateStatus == PowerControlAsyncStatus::running)
+ {
+ log<level::INFO>(
+ "Power-cycle operation is not allowed during the upgrade");
+ rc = ipmi::ccCommandNotAvailable;
+ break;
+ }
rc = initiateHostStateTransition(ctx,
State::Host::Transition::Reboot);
break;
--
2.25.1