264 lines
9.2 KiB
Diff
264 lines
9.2 KiB
Diff
|
|
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
|
||
|
|
|