2234 lines
76 KiB
Diff
Executable File
2234 lines
76 KiB
Diff
Executable File
From d3f989bc8b50d78389f01e93de838fd23ba8eac1 Mon Sep 17 00:00:00 2001
|
|
From: roly <Rolyli.Li@luxshare-ict.com>
|
|
Date: Fri, 10 Jan 2025 11:29:50 +0800
|
|
Subject: [PATCH] Add luxshare oem redfish update interface
|
|
|
|
---
|
|
include/image_upload.hpp | 152 +-
|
|
include/luxshare/firmware_update.hpp | 67 +
|
|
include/luxshare/upload_update.hpp | 1827 +++++++++++++++++++++++
|
|
meson.build | 23 +-
|
|
redfish-core/include/utils/sw_utils.hpp | 30 +
|
|
src/webserver_main.cpp | 2 +
|
|
6 files changed, 2093 insertions(+), 8 deletions(-)
|
|
create mode 100755 include/luxshare/firmware_update.hpp
|
|
create mode 100755 include/luxshare/upload_update.hpp
|
|
|
|
diff --git a/include/image_upload.hpp b/include/image_upload.hpp
|
|
index 521ff659..e4704fc7 100644
|
|
--- a/include/image_upload.hpp
|
|
+++ b/include/image_upload.hpp
|
|
@@ -10,14 +10,131 @@
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
#include <memory>
|
|
+#include <ranges>
|
|
|
|
namespace crow
|
|
{
|
|
+
|
|
+namespace luxshare_update
|
|
+{
|
|
+
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::string bmcId;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::string biosId;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::string mbCpldId;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::string fcbCpldId;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::string leaf;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::unordered_map<std::string, std::string> firmwareTypeAndId;
|
|
+
|
|
+static const std::unordered_map<std::string, std::string> purposeToTarget = {
|
|
+ {"BMC", "BMC"}, {"Host", "BIOS"},
|
|
+ {"CPLD", "MBCPLD"}, {"FCB", "FCBCPLD"},
|
|
+};
|
|
+
|
|
+inline bool
|
|
+ getTargetFirmwareInfo(const dbus::utility::DBusPropertiesMap& propertys,
|
|
+ std::string& target, std::string& version)
|
|
+{
|
|
+ const std::string* dbusPurpose = nullptr;
|
|
+ const std::string* dbusversion = nullptr;
|
|
+ std::string purpose;
|
|
+ for (const auto& property : propertys)
|
|
+ {
|
|
+ if (property.first == "Purpose")
|
|
+ {
|
|
+ dbusPurpose = std::get_if<std::string>(&property.second);
|
|
+ }
|
|
+
|
|
+ if (property.first == "Version")
|
|
+ {
|
|
+ dbusversion = std::get_if<std::string>(&property.second);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (dbusPurpose == nullptr || dbusversion == nullptr)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("dbusPurpose or version error.");
|
|
+ return false;
|
|
+ }
|
|
+ version = *dbusversion;
|
|
+ // xyz.openbmc_project.Software.Version.VersionPurpose.BMC
|
|
+ // Firmware type after intercepting the last dot.
|
|
+ purpose = dbusPurpose->substr(dbusPurpose->find_last_of('.') + 1);
|
|
+ if (purposeToTarget.find(purpose) == purposeToTarget.end())
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("find purpose error.");
|
|
+ return false;
|
|
+ }
|
|
+ // Convert to the key Redfish requires to display.
|
|
+ target = purposeToTarget.at(purpose);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void updateGlobalFirmwareId(const std::string& target)
|
|
+{
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ bmcId = leaf;
|
|
+ }
|
|
+ else if (target == "BIOS")
|
|
+ {
|
|
+ biosId = leaf;
|
|
+ }
|
|
+ else if (target == "MBCPLD")
|
|
+ {
|
|
+ mbCpldId = leaf;
|
|
+ }
|
|
+ else if (target == "FCBCPLD")
|
|
+ {
|
|
+ fcbCpldId = leaf;
|
|
+ }
|
|
+ // Add to global Map
|
|
+ firmwareTypeAndId[target] = leaf;
|
|
+}
|
|
+
|
|
+inline void deleteLastUpgradedImage(const std::string& target)
|
|
+{
|
|
+ if (firmwareTypeAndId.find(target) != firmwareTypeAndId.end())
|
|
+ {
|
|
+ std::string deletedLeaf = firmwareTypeAndId.at(target);
|
|
+ // Whether the last firmware deletion was successful
|
|
+ // does not affect the result of the current firmware upload,
|
|
+ // so "ec" is not processed.
|
|
+ crow::connections::systemBus->async_method_call(
|
|
+ [](const boost::system::error_code& /*ec*/) {},
|
|
+ "xyz.openbmc_project.Software.BMC.Updater",
|
|
+ "/xyz/openbmc_project/software/" + deletedLeaf,
|
|
+ "xyz.openbmc_project.Object.Delete", "Delete");
|
|
+ }
|
|
+}
|
|
+
|
|
+inline void deleteThisUpgradedImage(const std::string& target)
|
|
+{
|
|
+ // delete leaf file
|
|
+ crow::connections::systemBus->async_method_call(
|
|
+ [](const boost::system::error_code& /*ec*/) {},
|
|
+ "xyz.openbmc_project.Software.BMC.Updater",
|
|
+ "/xyz/openbmc_project/software/" + target,
|
|
+ "xyz.openbmc_project.Object.Delete", "Delete");
|
|
+}
|
|
+
|
|
+} // namespace luxshare_update
|
|
+
|
|
namespace image_upload
|
|
{
|
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
|
|
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
|
+static std::string leaf;
|
|
|
|
inline void
|
|
uploadImageHandler(const crow::Request& req,
|
|
@@ -66,18 +183,45 @@ inline void
|
|
dbus::utility::DBusInteracesMap interfaces;
|
|
m.read(path, interfaces);
|
|
|
|
- if (std::find_if(interfaces.begin(), interfaces.end(),
|
|
- [](const auto& i) {
|
|
+ auto iter = std::find_if(interfaces.begin(), interfaces.end(),
|
|
+ [](const auto& i) {
|
|
return i.first == "xyz.openbmc_project.Software.Version";
|
|
- }) != interfaces.end())
|
|
+ });
|
|
+
|
|
+ if (iter != interfaces.end())
|
|
{
|
|
+ std::string version;
|
|
+ std::string firmwareType;
|
|
+
|
|
timeout.cancel();
|
|
- std::string leaf = path.filename();
|
|
+ leaf = path.filename();
|
|
if (leaf.empty())
|
|
{
|
|
leaf = path.str;
|
|
}
|
|
|
|
+ luxshare_update::leaf = leaf;
|
|
+ // Get target and version
|
|
+ const bool success = luxshare_update::getTargetFirmwareInfo(
|
|
+ iter->second, firmwareType, version);
|
|
+
|
|
+ if (!success)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ asyncResp->res.jsonValue["data"]["description"] =
|
|
+ "The request failed due to an internal service error.";
|
|
+ asyncResp->res.jsonValue["message"] =
|
|
+ "500 Internal Server Error";
|
|
+ asyncResp->res.jsonValue["status"] = "error";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // delete the last upgraded image
|
|
+ luxshare_update::deleteLastUpgradedImage(firmwareType);
|
|
+
|
|
+ luxshare_update::updateGlobalFirmwareId(firmwareType);
|
|
+
|
|
asyncResp->res.jsonValue["data"] = leaf;
|
|
asyncResp->res.jsonValue["message"] = "200 OK";
|
|
asyncResp->res.jsonValue["status"] = "ok";
|
|
diff --git a/include/luxshare/firmware_update.hpp b/include/luxshare/firmware_update.hpp
|
|
new file mode 100755
|
|
index 00000000..e2aa9b80
|
|
--- /dev/null
|
|
+++ b/include/luxshare/firmware_update.hpp
|
|
@@ -0,0 +1,67 @@
|
|
+#pragma once
|
|
+
|
|
+#include "upload_update.hpp"
|
|
+
|
|
+namespace crow
|
|
+{
|
|
+namespace luxshare_update_firmware
|
|
+{
|
|
+
|
|
+inline void requestRoutesOemLuxshareImageUpload(App& app)
|
|
+{
|
|
+ // Add post method to post image
|
|
+ BMCWEB_ROUTE(app,
|
|
+ "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareFile")
|
|
+ .privileges(redfish::privileges::privilegeSetConfigureManager)
|
|
+ .methods(boost::beast::http::verb::post)(
|
|
+ [](const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
|
|
+ crow::luxshare_update::uploadImageHandler(req, asyncResp, "Firmware");
|
|
+ });
|
|
+}
|
|
+
|
|
+inline void requestRoutesOemLuxshareImageUpdate(App& app)
|
|
+{
|
|
+ // Add post method to post update
|
|
+ BMCWEB_ROUTE(
|
|
+ app, "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate")
|
|
+ .privileges(redfish::privileges::privilegeSetConfigureManager)
|
|
+ .methods(boost::beast::http::verb::post)(
|
|
+ [](const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
|
|
+ crow::luxshare_update::handlePostFirmwareUpdate(req, asyncResp);
|
|
+ });
|
|
+
|
|
+ // Add delete method to delete Image
|
|
+ BMCWEB_ROUTE(
|
|
+ app, "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate")
|
|
+ .privileges(redfish::privileges::privilegeSetConfigureManager)
|
|
+ .methods(boost::beast::http::verb::delete_)(
|
|
+ [](const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
|
|
+ crow::luxshare_update::handleDeleteFirmwareUpdate(req, asyncResp);
|
|
+ });
|
|
+}
|
|
+
|
|
+inline void requestRoutesOemLuxshareUpdateStatus(App& app)
|
|
+{
|
|
+ // Add get method to get update status
|
|
+ BMCWEB_ROUTE(
|
|
+ app, "/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareStatus")
|
|
+ .privileges(redfish::privileges::privilegeSetLogin)
|
|
+ .methods(boost::beast::http::verb::get)(
|
|
+ [](const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
|
|
+ crow::luxshare_update::handleFirmwareStatus(req, asyncResp);
|
|
+ });
|
|
+}
|
|
+
|
|
+inline void requestRoutes(App& app)
|
|
+{
|
|
+ requestRoutesOemLuxshareImageUpload(app);
|
|
+ requestRoutesOemLuxshareImageUpdate(app);
|
|
+ requestRoutesOemLuxshareUpdateStatus(app);
|
|
+}
|
|
+
|
|
+} // namespace luxshare_update_firmware
|
|
+} // namespace crow
|
|
diff --git a/include/luxshare/upload_update.hpp b/include/luxshare/upload_update.hpp
|
|
new file mode 100755
|
|
index 00000000..895ecac2
|
|
--- /dev/null
|
|
+++ b/include/luxshare/upload_update.hpp
|
|
@@ -0,0 +1,1827 @@
|
|
+#pragma once
|
|
+#include "image_upload.hpp"
|
|
+#include "multipart_parser.hpp"
|
|
+
|
|
+#include <app.hpp>
|
|
+#include <unordered_set>
|
|
+#include <async_resp.hpp>
|
|
+#include <boost/uuid/uuid.hpp>
|
|
+#include <boost/uuid/uuid_generators.hpp>
|
|
+#include <boost/uuid/uuid_io.hpp>
|
|
+#include <dbus_utility.hpp>
|
|
+#include <error_messages.hpp>
|
|
+#include <nlohmann/json.hpp>
|
|
+#include <query.hpp>
|
|
+#include <registries/privilege_registry.hpp>
|
|
+#include <sdbusplus/bus/match.hpp>
|
|
+#include <sdbusplus/asio/property.hpp>
|
|
+#include <utils/collection.hpp>
|
|
+#include <utils/json_utils.hpp>
|
|
+#include <utils/sw_utils.hpp>
|
|
+#include <xyz/openbmc_project/Software/ApplyOptions/server.hpp>
|
|
+#include <xyz/openbmc_project/Software/UpdateTarget/server.hpp>
|
|
+
|
|
+#include <filesystem>
|
|
+#include <fstream>
|
|
+
|
|
+constexpr auto updateService = "xyz.openbmc_project.Software.BMC.Updater";
|
|
+constexpr auto asyncUpdateService = "xyz.openbmc_project.Software.AsyncUpdate";
|
|
+constexpr auto updateObjPath = "/xyz/openbmc_project/software/";
|
|
+constexpr auto asyncUpdateObjPath = "/xyz/openbmc_project/Software/AsyncUpdate";
|
|
+constexpr auto updateActivationInterface =
|
|
+ "xyz.openbmc_project.Software.Activation";
|
|
+constexpr auto updateApplyOptionsInterface =
|
|
+ "xyz.openbmc_project.Software.ApplyOptions";
|
|
+constexpr auto updateVersionInterface = "xyz.openbmc_project.Software.Version";
|
|
+constexpr auto activationProgressInterface =
|
|
+ "xyz.openbmc_project.Software.ActivationProgress";
|
|
+
|
|
+constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete";
|
|
+constexpr auto deleteProperty = "Delete";
|
|
+constexpr auto updateProperty = "Activation";
|
|
+constexpr auto versionProperty = "Version";
|
|
+constexpr auto progressProperty = "Progress";
|
|
+constexpr auto requsetedUpdateProperty = "RequestedActivation";
|
|
+
|
|
+constexpr auto updateApplyProperty = "RequestedApplyTime";
|
|
+constexpr auto updateClearConfig = "ClearConfig";
|
|
+constexpr auto asyncUpdateMethod = "AddQueue";
|
|
+constexpr auto asyncUpdateStatus = "AsyncUpdateStatus";
|
|
+constexpr auto applytimeService = "xyz.openbmc_project.Settings";
|
|
+constexpr auto applytimeobjPath = "/xyz/openbmc_project/software/apply_time";
|
|
+constexpr auto applytimeInterface = "xyz.openbmc_project.Software.ApplyTime";
|
|
+constexpr auto ready =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.Ready";
|
|
+constexpr auto activating =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.Activating";
|
|
+constexpr auto active =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.Active";
|
|
+constexpr auto addQueue =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.AddQueue";
|
|
+constexpr auto interrupted =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.Interrupted";
|
|
+constexpr auto inVaild =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.Invalid";
|
|
+constexpr auto requestedActive =
|
|
+ "xyz.openbmc_project.Software.Activation.RequestedActivations.Active";
|
|
+
|
|
+constexpr auto immediate =
|
|
+ "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
|
|
+constexpr auto onReset =
|
|
+ "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
|
|
+constexpr auto versionPurposeHost =
|
|
+ "xyz.openbmc_project.Software.Version.VersionPurpose.Host";
|
|
+constexpr auto versionPurposeMBCPLD =
|
|
+ "xyz.openbmc_project.Software.Version.VersionPurpose.CPLD";
|
|
+constexpr auto versionPurposeFCBCPLD =
|
|
+ "xyz.openbmc_project.Software.Version.VersionPurpose.FCB";
|
|
+
|
|
+constexpr auto failed =
|
|
+ "xyz.openbmc_project.Software.Activation.Activations.Failed";
|
|
+constexpr auto bmcUpdateStatusFile = "/tmp/bmc_update_status";
|
|
+constexpr auto biosUpdateStatusFile = "/tmp/bios_update_status";
|
|
+constexpr auto mbcpldUpdateStatusFile = "/tmp/mbcpld_update_status";
|
|
+constexpr auto fcbcpldUpdateStatusFile = "/tmp/fcbcpld_update_status";
|
|
+
|
|
+constexpr auto updateImageFolder = "/tmp/images/";
|
|
+constexpr auto updateTargetInterface =
|
|
+ "xyz.openbmc_project.Software.UpdateTarget";
|
|
+constexpr auto updateTargetSlot = "UpdateTargetSlot";
|
|
+using TargetSlot =
|
|
+ sdbusplus::xyz::openbmc_project::Software::server::UpdateTarget::TargetSlot;
|
|
+using ConfigManagement = sdbusplus::xyz::openbmc_project::Software::server::
|
|
+ ApplyOptions::ConfigManagement;
|
|
+
|
|
+static std::string fancpldLastUpgradeStatus;
|
|
+
|
|
+namespace crow
|
|
+{
|
|
+namespace luxshare_update
|
|
+{
|
|
+
|
|
+static const std::unordered_set<std::string> bmcRefreshStrategy = {
|
|
+ "Keep", "Clear", "PartialKeep", "FactoryReset"};
|
|
+
|
|
+/**
|
|
+ * @brief Converts the target slot to enum
|
|
+ *
|
|
+ */
|
|
+inline TargetSlot convertTargetSlot(const std::optional<std::string>& slot)
|
|
+{
|
|
+ if (slot == "Secondary")
|
|
+ {
|
|
+ return TargetSlot::Secondary;
|
|
+ }
|
|
+ if (slot == "Both")
|
|
+ {
|
|
+ return TargetSlot::Both;
|
|
+ }
|
|
+
|
|
+ return TargetSlot::Primary;
|
|
+}
|
|
+
|
|
+inline ConfigManagement convertConfigStrategy(const std::string& configStrategy)
|
|
+{
|
|
+ if (configStrategy == "Clear")
|
|
+ {
|
|
+ return ConfigManagement::Clear;
|
|
+ }
|
|
+ if (configStrategy == "PartialKeep")
|
|
+ {
|
|
+ return ConfigManagement::PartialKeep;
|
|
+ }
|
|
+ if (configStrategy == "FactoryReset")
|
|
+ {
|
|
+ return ConfigManagement::FactoryReset;
|
|
+ }
|
|
+
|
|
+ return ConfigManagement::Keep;
|
|
+}
|
|
+
|
|
+inline std::string getManifestPurpose(const std::string& folderName)
|
|
+{
|
|
+ std::ifstream ifs(updateImageFolder + folderName + "/MANIFEST");
|
|
+
|
|
+ if (!ifs.is_open())
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error while reading MANIFEST file");
|
|
+ return {};
|
|
+ }
|
|
+ std::string purpose;
|
|
+ while (std::getline(ifs, purpose))
|
|
+ {
|
|
+ auto pos = purpose.find("purpose=");
|
|
+ if (pos != std::string::npos)
|
|
+ {
|
|
+ return purpose.substr(pos + strlen("purpose="));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return {};
|
|
+}
|
|
+
|
|
+inline bool isUploadImageValid(const std::string& target)
|
|
+{
|
|
+ if (std::filesystem::exists(updateImageFolder + leaf + "/MANIFEST"))
|
|
+ {
|
|
+ std::string fwPurpose = getManifestPurpose(leaf);
|
|
+ return (target == "BMC" &&
|
|
+ (fwPurpose.find("BMC") != std::string::npos)) ||
|
|
+ (target == "BIOS" &&
|
|
+ (fwPurpose.find("Host") != std::string::npos)) ||
|
|
+ (target == "MBCPLD" &&
|
|
+ (fwPurpose.find("CPLD") != std::string::npos)) ||
|
|
+ (target == "FCBCPLD" &&
|
|
+ (fwPurpose.find("FCB") != std::string::npos))
|
|
+ ;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+inline std::string getFwId(const std::string& purpose,
|
|
+ const std::string& firmwareID)
|
|
+{
|
|
+ std::string fwId;
|
|
+ std::string fwPurpose;
|
|
+ if (std::filesystem::exists(updateImageFolder + image_upload::leaf +
|
|
+ "/MANIFEST"))
|
|
+ {
|
|
+ fwPurpose = getManifestPurpose(image_upload::leaf);
|
|
+ if (fwPurpose.find(purpose) != std::string::npos)
|
|
+ {
|
|
+ fwId = image_upload::leaf;
|
|
+ return fwId;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (std::filesystem::exists(updateImageFolder + firmwareID + "/MANIFEST"))
|
|
+ {
|
|
+ fwPurpose = getManifestPurpose(firmwareID);
|
|
+ if (fwPurpose.find(purpose) != std::string::npos)
|
|
+ {
|
|
+ fwId = firmwareID;
|
|
+ }
|
|
+ }
|
|
+ return fwId;
|
|
+}
|
|
+
|
|
+inline void getUpdateStatusFileInfo(const std::string& target,
|
|
+ std::string& fwId,
|
|
+ std::string& updateStatusflag,
|
|
+ bool& fileExist)
|
|
+{
|
|
+ std::string fileName;
|
|
+
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ fwId = bmcId;
|
|
+ std::ifstream in(bmcUpdateStatusFile);
|
|
+ in >> updateStatusflag;
|
|
+ fileName = bmcUpdateStatusFile;
|
|
+ }
|
|
+ else if (target == "BIOS")
|
|
+ {
|
|
+ fwId = getFwId("Host", biosId);
|
|
+ std::ifstream in(biosUpdateStatusFile);
|
|
+ in >> updateStatusflag;
|
|
+ fileName = biosUpdateStatusFile;
|
|
+ }
|
|
+ else if (target == "MBCPLD")
|
|
+ {
|
|
+ fwId = getFwId("CPLD", mbCpldId);
|
|
+ std::ifstream in(mbcpldUpdateStatusFile);
|
|
+ in >> updateStatusflag;
|
|
+ fileName = mbcpldUpdateStatusFile;
|
|
+ }
|
|
+ else if (target == "FCBCPLD")
|
|
+ {
|
|
+ fwId = getFwId("FCB", fcbCpldId);
|
|
+ std::ifstream in(fcbcpldUpdateStatusFile);
|
|
+ in >> updateStatusflag;
|
|
+ fileName = fcbcpldUpdateStatusFile;
|
|
+ }
|
|
+
|
|
+ fileExist = std::filesystem::exists(fileName);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * UpdateStatus:
|
|
+ * 0: complete - bios_update_status file exist and value is 0
|
|
+ * 1: fail - bios_update_status file exist and value is not 0 and not null
|
|
+ * 2: incomming - dbus obj exist and status activting
|
|
+ * 3: no task - dbus obj not exist and bios_update_status file not exist
|
|
+ * 4: waiting - dbus obj exist and status ready
|
|
+ * 5: interrupted - dbus obj exist and status interrupted
|
|
+ */
|
|
+
|
|
+inline bool getFirmwareUpgradeStatus(
|
|
+ const boost::asio::yield_context& yield, const std::string& fwId,
|
|
+ const std::string& target, const std::string& updateStatusflag,
|
|
+ const bool& fileExist, std::string& statusString, uint8_t& statusNumber)
|
|
+{
|
|
+ boost::system::error_code ec;
|
|
+ auto response = crow::connections::systemBus
|
|
+ ->yield_method_call<std::variant<std::string>>(
|
|
+ yield, ec, updateService, updateObjPath + fwId,
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ updateActivationInterface, updateProperty);
|
|
+
|
|
+ if (ec && !fileExist)
|
|
+ {
|
|
+ statusNumber = 3;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ const std::string* updateStatus = std::get_if<std::string>(&response);
|
|
+ if (updateStatus == nullptr)
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ if (((ec == boost::system::errc::invalid_argument ||
|
|
+ ec.value() ==
|
|
+ boost::system::linux_error::bad_request_descriptor) &&
|
|
+ updateStatusflag == "0" && fileExist) ||
|
|
+ (*updateStatus == active))
|
|
+ {
|
|
+ statusNumber = 0;
|
|
+ statusString = "Updated";
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if ((ec == boost::system::errc::invalid_argument ||
|
|
+ ec.value() ==
|
|
+ boost::system::linux_error::bad_request_descriptor) &&
|
|
+ updateStatusflag == "0" && fileExist)
|
|
+ {
|
|
+ statusNumber = 0;
|
|
+ statusString = "Updated";
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (*updateStatus == ready)
|
|
+ {
|
|
+ statusNumber = 4;
|
|
+ statusString = "Uploaded";
|
|
+ }
|
|
+
|
|
+ else if (*updateStatus == activating)
|
|
+ {
|
|
+ statusNumber = 2;
|
|
+ statusString = "Updating";
|
|
+ }
|
|
+ else if (*updateStatus == addQueue)
|
|
+ {
|
|
+ statusNumber = 4;
|
|
+ statusString = "Queued";
|
|
+ }
|
|
+ else if (*updateStatus == interrupted)
|
|
+ {
|
|
+ statusNumber = 5;
|
|
+ statusString = "Interrupted";
|
|
+ }
|
|
+ else if (*updateStatus == inVaild || *updateStatus == failed ||
|
|
+ (updateStatusflag != "0" && fileExist))
|
|
+ {
|
|
+ statusNumber = 1;
|
|
+ statusString = "Failed";
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void clearGlobalFirmwareId(const std::string& target)
|
|
+{
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ bmcId.clear();
|
|
+ }
|
|
+ else if (target == "BIOS")
|
|
+ {
|
|
+ biosId.clear();
|
|
+ }
|
|
+ else if (target == "MBCPLD")
|
|
+ {
|
|
+ mbCpldId.clear();
|
|
+ }
|
|
+ else if (target == "FCBCPLD")
|
|
+ {
|
|
+ fcbCpldId.clear();
|
|
+ }
|
|
+}
|
|
+
|
|
+inline void updateGlobalUpdateStatus(const std::string& target,
|
|
+ const std::string& status)
|
|
+{
|
|
+ if (target == "FCBCPLD")
|
|
+ {
|
|
+ fancpldLastUpgradeStatus = status;
|
|
+ }
|
|
+}
|
|
+
|
|
+inline void clearGlobalUpdateStatus(const std::string& target)
|
|
+{
|
|
+ if (target == "FCBCPLD")
|
|
+ {
|
|
+ fancpldLastUpgradeStatus.clear();
|
|
+ }
|
|
+}
|
|
+
|
|
+inline void updateResp(const std::string& firmwareType,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& version)
|
|
+{
|
|
+ deleteLastUpgradedImage(firmwareType);
|
|
+ updateGlobalFirmwareId(firmwareType);
|
|
+ clearGlobalUpdateStatus(firmwareType);
|
|
+
|
|
+ nlohmann::json targetStatus;
|
|
+ // Version only supports the following exception
|
|
+ // returns:Mismatch,Invalid.
|
|
+ if (version != "Mismatch" && version != "Invalid")
|
|
+ {
|
|
+ targetStatus[firmwareType]["UpdateStatus"] = "Uploaded";
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ targetStatus[firmwareType]["UpdateStatus"] = "Failed";
|
|
+ }
|
|
+ targetStatus[firmwareType]["VersionStatus"] = version;
|
|
+ targetStatus[firmwareType]["UpdateProgress"] =
|
|
+ nlohmann::json::value_t::null;
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"].push_back(targetStatus);
|
|
+}
|
|
+
|
|
+inline void getFirmwareUpgradeStatusAsync(
|
|
+ const std::string& firmwareType, const std::string& fwId,
|
|
+ const std::string& version,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const bool& fileExist)
|
|
+{
|
|
+ crow::connections::systemBus->async_method_call(
|
|
+ [firmwareType, version, asyncResp,
|
|
+ fileExist](boost::system::error_code ec1,
|
|
+ std::variant<std::string> statusInfo) {
|
|
+ if (ec1)
|
|
+ {
|
|
+ // fileExist indicates that the firmware has been updated,
|
|
+ // returning ec1 is normal, and the upload can continue.
|
|
+ if (fileExist)
|
|
+ {
|
|
+ updateResp(firmwareType, asyncResp, version);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BMCWEB_LOG_ERROR("Error getting currentState: {}", ec1.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ const std::string* status = std::get_if<std::string>(&statusInfo);
|
|
+ if (status == nullptr)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (*status == addQueue)
|
|
+ {
|
|
+ deleteThisUpgradedImage(leaf);
|
|
+ leaf.clear();
|
|
+ BMCWEB_LOG_ERROR("firmware addqueue Conflict !");
|
|
+ asyncResp->res.result(boost::beast::http::status::conflict);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ updateResp(firmwareType, asyncResp, version);
|
|
+ },
|
|
+ updateService, updateObjPath + fwId, "org.freedesktop.DBus.Properties",
|
|
+ "Get", updateActivationInterface, updateProperty);
|
|
+}
|
|
+
|
|
+inline void
|
|
+ uploadImageHandler(const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target)
|
|
+{
|
|
+ // Only allow one FW update at a time
|
|
+ if (fwUpdateMatcher != nullptr)
|
|
+ {
|
|
+ asyncResp->res.addHeader("Retry-After", "30");
|
|
+ asyncResp->res.result(boost::beast::http::status::service_unavailable);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Make sure that content type is or multipart/form-data
|
|
+ std::string_view contentType = req.getHeaderValue("content-type");
|
|
+ if (!boost::starts_with(contentType, "multipart/form-data"))
|
|
+ {
|
|
+ BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
|
|
+ asyncResp->res.result(boost::beast::http::status::bad_request);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ auto parser = std::make_shared<MultipartParser>();
|
|
+ ParserError ec = parser->parse(req);
|
|
+ if (ec != ParserError::PARSER_SUCCESS)
|
|
+ {
|
|
+ // handle error
|
|
+ BMCWEB_LOG_ERROR("MIME parse failed, ec : {}", static_cast<int>(ec));
|
|
+ asyncResp->res.result(boost::beast::http::status::bad_request);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ bool fwimage = false;
|
|
+ for (const FormPart& formpart : parser->mime_fields)
|
|
+ {
|
|
+ boost::beast::http::fields::const_iterator it =
|
|
+ formpart.fields.find("Content-Disposition");
|
|
+ if (it == formpart.fields.end())
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // The construction parameters of param_list must start with `;`
|
|
+ size_t index = it->value().find(';');
|
|
+ if (index == std::string::npos)
|
|
+ {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (const auto& param :
|
|
+ boost::beast::http::param_list{it->value().substr(index)})
|
|
+ {
|
|
+ if (param.first != "name" || param.second.empty())
|
|
+ {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (param.second == "fwimage")
|
|
+ {
|
|
+ fwimage = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!fwimage)
|
|
+ {
|
|
+ // handle error
|
|
+ BMCWEB_LOG_ERROR("parse fwimage failed");
|
|
+ asyncResp->res.result(boost::beast::http::status::bad_request);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ boost::asio::spawn(
|
|
+ crow::connections::systemBus->get_io_context(),
|
|
+ [asyncResp, parser, target](const boost::asio::yield_context& yield) {
|
|
+ bool commonInterface = target == "Firmware";
|
|
+ if (commonInterface)
|
|
+ {
|
|
+ bool isUpdating = false;
|
|
+ // Get the upgrade status of all firmware in the firmware list.
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ std::string statusString;
|
|
+ uint8_t statusNumber = 0;
|
|
+ std::string updateStatusflag;
|
|
+ std::string tempFwId;
|
|
+ bool fileExist = false;
|
|
+
|
|
+ getUpdateStatusFileInfo(fwType, tempFwId, updateStatusflag,
|
|
+ fileExist);
|
|
+ const bool success = getFirmwareUpgradeStatus(
|
|
+ yield, fwId, fwType, updateStatusflag, fileExist,
|
|
+ statusString, statusNumber);
|
|
+ if (!success)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (statusString == "Updating")
|
|
+ {
|
|
+ isUpdating = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (isUpdating)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Status Conflict !");
|
|
+ asyncResp->res.result(boost::beast::http::status::conflict);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ static boost::asio::steady_timer timeout(
|
|
+ crow::connections::systemBus->get_io_context(),
|
|
+ std::chrono::seconds(5));
|
|
+
|
|
+ timeout.expires_after(std::chrono::seconds(15));
|
|
+
|
|
+ auto timeoutHandler =
|
|
+ [asyncResp, commonInterface](const boost::system::error_code& ec1) {
|
|
+ fwUpdateMatcher = nullptr;
|
|
+ if (ec1 == boost::asio::error::operation_aborted)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+ asyncResp->res.result(boost::beast::http::status::bad_request);
|
|
+ // Fail
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ };
|
|
+
|
|
+ auto callback = [asyncResp, target,
|
|
+ commonInterface](sdbusplus::message::message& m) {
|
|
+ BMCWEB_LOG_DEBUG("Match fired");
|
|
+ sdbusplus::message::object_path path;
|
|
+ dbus::utility::DBusInteracesMap interfaces;
|
|
+ m.read(path, interfaces);
|
|
+
|
|
+ auto iter = std::find_if(interfaces.begin(), interfaces.end(),
|
|
+ [](const auto& i) {
|
|
+ return i.first == "xyz.openbmc_project.Software.Version";
|
|
+ });
|
|
+
|
|
+ if (iter != interfaces.end())
|
|
+ {
|
|
+ std::string version;
|
|
+ std::string firmwareType;
|
|
+ timeout.cancel();
|
|
+
|
|
+ leaf = path.filename();
|
|
+ if (leaf.empty())
|
|
+ {
|
|
+ leaf = path.str;
|
|
+ }
|
|
+
|
|
+ if (commonInterface)
|
|
+ {
|
|
+ // Get target and version
|
|
+ const bool success = getTargetFirmwareInfo(
|
|
+ iter->second, firmwareType, version);
|
|
+ if (!success)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ std::string statusString;
|
|
+ std::string updateStatusflag;
|
|
+ std::string tempFwId;
|
|
+ bool fileExist = false;
|
|
+ std::string firmwareID;
|
|
+ auto it = firmwareTypeAndId.find(firmwareType);
|
|
+
|
|
+ // Find the firmware in the list that has the same
|
|
+ // firmwareType as the current one
|
|
+ if (it != firmwareTypeAndId.end())
|
|
+ {
|
|
+ firmwareID = it->second;
|
|
+ getUpdateStatusFileInfo(firmwareType, tempFwId,
|
|
+ updateStatusflag, fileExist);
|
|
+ getFirmwareUpgradeStatusAsync(firmwareType, firmwareID,
|
|
+ version, asyncResp,
|
|
+ fileExist);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ updateResp(firmwareType, asyncResp, version);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ updateGlobalFirmwareId(target);
|
|
+
|
|
+ clearGlobalUpdateStatus(target);
|
|
+
|
|
+ if (isUploadImageValid(target))
|
|
+ {
|
|
+ // Success
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] =
|
|
+ 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ crow::connections::systemBus->async_method_call(
|
|
+ [](const boost::system::error_code& /*ec*/) {},
|
|
+ updateService, updateObjPath + leaf,
|
|
+ deleteInterface, deleteProperty);
|
|
+ firmwareTypeAndId.erase(target);
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] =
|
|
+ 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ BMCWEB_LOG_DEBUG("ending response");
|
|
+ fwUpdateMatcher = nullptr;
|
|
+ }
|
|
+ };
|
|
+ fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
|
|
+ *crow::connections::systemBus,
|
|
+ "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
|
|
+ "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
|
|
+ callback);
|
|
+
|
|
+ std::string filepath(
|
|
+ "/tmp/images/" +
|
|
+ boost::uuids::to_string(boost::uuids::random_generator()()));
|
|
+ BMCWEB_LOG_DEBUG("Writing file to {}", filepath);
|
|
+ std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
|
|
+ std::ofstream::trunc);
|
|
+ out << parser->mime_fields[0].content;
|
|
+ out.close();
|
|
+ timeout.async_wait(timeoutHandler);
|
|
+ });
|
|
+}
|
|
+
|
|
+inline bool
|
|
+ parseUpgradeStrategy(const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target,
|
|
+ std::tuple<std::string, std::string>& updateStrategy)
|
|
+{
|
|
+ constexpr auto attribute = "action";
|
|
+ std::string action;
|
|
+ constexpr auto targetAttribute = "TargetSlot";
|
|
+ std::optional<std::string> targetSlot{};
|
|
+ if (!redfish::json_util::readJsonPatch(req, asyncResp->res, attribute,
|
|
+ action))
|
|
+ {
|
|
+ BMCWEB_LOG_DEBUG("Missing property action.");
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ if (!redfish::json_util::readJsonPatch(req, asyncResp->res,
|
|
+ targetAttribute, targetSlot))
|
|
+ {
|
|
+ BMCWEB_LOG_DEBUG("Reading property targetSlot error.");
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (targetSlot == std::nullopt)
|
|
+ {
|
|
+ targetSlot = "Primary";
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (targetSlot)
|
|
+ {
|
|
+ updateStrategy = {action, *targetSlot};
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+inline bool isUploadedStatus(const boost::asio::yield_context& yield,
|
|
+ const std::string& target)
|
|
+{
|
|
+ std::string fwId;
|
|
+ bool fileExist = false;
|
|
+ std::string updateStatusflag;
|
|
+ std::string statusString;
|
|
+ uint8_t statusNumber = 1;
|
|
+
|
|
+ getUpdateStatusFileInfo(target, fwId, updateStatusflag, fileExist);
|
|
+
|
|
+ const bool success = getFirmwareUpgradeStatus(yield, fwId, target,
|
|
+ updateStatusflag, fileExist,
|
|
+ statusString, statusNumber);
|
|
+ return (success) && (statusString == "Uploaded");
|
|
+}
|
|
+
|
|
+inline bool isUpgradeCompleted(const boost::asio::yield_context& yield,
|
|
+ const std::string& target)
|
|
+{
|
|
+ std::string fwId;
|
|
+ bool fileExist = false;
|
|
+ std::string updateStatusflag;
|
|
+ std::string statusString;
|
|
+ uint8_t statusNumber = 1;
|
|
+
|
|
+ getUpdateStatusFileInfo(target, fwId, updateStatusflag, fileExist);
|
|
+
|
|
+ const bool success = getFirmwareUpgradeStatus(yield, fwId, target,
|
|
+ updateStatusflag, fileExist,
|
|
+ statusString, statusNumber);
|
|
+ return (success) && (statusString == "Updated" || statusString == "Failed");
|
|
+}
|
|
+
|
|
+inline bool
|
|
+ isPeerNodeUpgrading(const boost::asio::yield_context& yield,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& upgradeFlagProperty,
|
|
+ const bool& commonInterface = false)
|
|
+{
|
|
+ boost::system::error_code ec;
|
|
+ auto property =
|
|
+ crow::connections::systemBus->yield_method_call<std::variant<uint8_t>>(
|
|
+ yield, ec, "xyz.openbmc_project.Settings",
|
|
+ "/xyz/openbmc_project/control/upgrade_flag",
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ "xyz.openbmc_project.Control.MultNodeFw.UpgradeFlag",
|
|
+ upgradeFlagProperty);
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error getting {} property: {}", upgradeFlagProperty,
|
|
+ ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ const uint8_t* upgradeFlag = std::get_if<uint8_t>(&property);
|
|
+ if (upgradeFlag == nullptr)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (*upgradeFlag != 0U)
|
|
+ {
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+inline void activatingImage(const boost::asio::yield_context& yield,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ std::string& fwId,
|
|
+ const bool& commonInterface = false)
|
|
+{
|
|
+ // Activating Image
|
|
+ boost::system::error_code ec;
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, updateService, updateObjPath + fwId,
|
|
+ "org.freedesktop.DBus.Properties", "Set", updateActivationInterface,
|
|
+ requsetedUpdateProperty, std::variant<std::string>(requestedActive));
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error setting RequestedActivation property from {}: {}",
|
|
+ updateObjPath + fwId, ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Success
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+inline void syncUpdateBMCHandler(
|
|
+ const boost::asio::yield_context& yield,
|
|
+
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const bool& commonInterface = false,
|
|
+ const std::tuple<std::string, std::string>& updateStrategy = {"Keep",
|
|
+ "Primary"})
|
|
+{
|
|
+ auto action = std::get<0>(updateStrategy);
|
|
+ auto targetSlot = std::get<1>(updateStrategy);
|
|
+
|
|
+ if (bmcRefreshStrategy.find(action) == bmcRefreshStrategy.end())
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("active property not found..");
|
|
+ asyncResp->res.result(boost::beast::http::status::bad_request);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (action == "FactoryReset")
|
|
+ {
|
|
+ boost::system::error_code ec;
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, "xyz.openbmc_project.Software.BMC.Updater",
|
|
+ "/xyz/openbmc_project/software",
|
|
+ "xyz.openbmc_project.Common.FactoryReset", "Reset");
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error calling Reset method: {}", ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Set Config
|
|
+ boost::system::error_code configEc;
|
|
+ std::string a =
|
|
+ sdbusplus::common::xyz::openbmc_project::software::ApplyOptions::
|
|
+ convertConfigManagementToString(convertConfigStrategy(action));
|
|
+
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, configEc, updateService, updateObjPath + bmcId,
|
|
+ "org.freedesktop.DBus.Properties", "Set", updateApplyOptionsInterface,
|
|
+ updateClearConfig, std::variant<std::string>(a));
|
|
+ if (configEc)
|
|
+ {
|
|
+ std::cout<<std::format("Error setting ClearConfig property from {}: {}",
|
|
+ updateObjPath + bmcId, configEc.message())<<std::endl;
|
|
+ BMCWEB_LOG_ERROR("Error setting ClearConfig property from {}: {}",
|
|
+ updateObjPath + bmcId, configEc.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (targetSlot == "Primary" || targetSlot == "Secondary" ||
|
|
+ targetSlot == "Both")
|
|
+ {
|
|
+ boost::system::error_code ec;
|
|
+ std::string t =
|
|
+ sdbusplus::common::xyz::openbmc_project::software::UpdateTarget::
|
|
+ convertTargetSlotToString(convertTargetSlot(targetSlot));
|
|
+
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, updateService, updateObjPath + bmcId,
|
|
+ "org.freedesktop.DBus.Properties", "Set", updateTargetInterface,
|
|
+ updateTargetSlot, std::variant<std::string>(t));
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error setting UpdateTargetSlot property from {}: {}",
|
|
+ updateObjPath + bmcId, ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bool immediateApply = true;
|
|
+ std::string bmcRunningSlot = redfish::sw_util::getBMCRunningSlot();
|
|
+
|
|
+ if (bmcRunningSlot == "0" && targetSlot == "Secondary")
|
|
+ {
|
|
+ immediateApply = false;
|
|
+ }
|
|
+
|
|
+ if (immediateApply)
|
|
+ {
|
|
+ // Set Apply Time Immediate
|
|
+ // We depend on ApplyTime to trigger BMC reboot after BMC active
|
|
+ // success
|
|
+ boost::system::error_code ec;
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, applytimeService, applytimeobjPath,
|
|
+ "org.freedesktop.DBus.Properties", "Set", applytimeInterface,
|
|
+ updateApplyProperty, std::variant<std::string>(immediate));
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error setting RequestedApplyTime property: {}",
|
|
+ ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Set Apply Time OnReset
|
|
+ // We depend on ApplyTime to trigger BMC reboot after BMC active
|
|
+ // success
|
|
+ boost::system::error_code ec;
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, applytimeService, applytimeobjPath,
|
|
+ "org.freedesktop.DBus.Properties", "Set", applytimeInterface,
|
|
+ updateApplyProperty, std::variant<std::string>(onReset));
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error setting RequestedApplyTime property: {}",
|
|
+ ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Activating Image
|
|
+ activatingImage(yield, asyncResp, bmcId, commonInterface);
|
|
+}
|
|
+
|
|
+inline void syncUpdateImageHandler(
|
|
+ const boost::asio::yield_context& yield,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target, const bool& commonInterface = false,
|
|
+ const std::tuple<std::string, std::string>& updateStrategy = {"Keep",
|
|
+ "Primary"})
|
|
+{
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ boost::system::error_code ec;
|
|
+ auto chassisState =
|
|
+ crow::connections::systemBus
|
|
+ ->yield_method_call<std::variant<std::string>>(
|
|
+ yield, ec, "xyz.openbmc_project.State.Chassis",
|
|
+ "/xyz/openbmc_project/state/chassis0",
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error getting CurrentPowerState property: {}",
|
|
+ ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ const std::string* s = std::get_if<std::string>(&chassisState);
|
|
+ BMCWEB_LOG_DEBUG("Chassis state: {}", *s);
|
|
+ if (s != nullptr)
|
|
+ {
|
|
+ // Verify Chassis State
|
|
+ if (*s == "xyz.openbmc_project.State.Chassis.PowerState.On")
|
|
+ {
|
|
+ auto operatingSystemState =
|
|
+ crow::connections::systemBus
|
|
+ ->yield_method_call<std::variant<bool>>(
|
|
+ yield, ec, "xyz.openbmc_project.Host.Misc.Manager",
|
|
+ "/xyz/openbmc_project/misc/platform_state",
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ "xyz.openbmc_project.State.Host.Misc",
|
|
+ "PostComplete");
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error getting OperatingSystemState property: {}",
|
|
+ ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] =
|
|
+ 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ const bool* value =
|
|
+ std::get_if<bool>(&operatingSystemState);
|
|
+ if (value == nullptr)
|
|
+ {
|
|
+ // illegal value
|
|
+ BMCWEB_LOG_DEBUG(
|
|
+ "Failed to get bios post complete: property value == nullptr");
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] =
|
|
+ 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Check post complete
|
|
+ if (*value)
|
|
+ {
|
|
+ syncUpdateBMCHandler(yield, asyncResp, commonInterface,
|
|
+ updateStrategy);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ BMCWEB_LOG_DEBUG("BIOS post is not yet completed.");
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::service_unavailable);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] =
|
|
+ 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ syncUpdateBMCHandler(yield, asyncResp, commonInterface,
|
|
+ updateStrategy);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+inline void asyncUpdateImageHandler(
|
|
+ const boost::asio::yield_context& yield,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target, const bool& commonInterface = false,
|
|
+ const std::tuple<std::string, std::string>& updateStrategy = {"Keep", ""})
|
|
+{
|
|
+ std::string fwId;
|
|
+ std::string versionPurpose;
|
|
+ if (target == "BIOS")
|
|
+ {
|
|
+ auto action = std::get<0>(updateStrategy);
|
|
+ auto targetSlot = std::get<1>(updateStrategy);
|
|
+ // Set ClearConfig
|
|
+ if (action == "Keep" || action == "Clear")
|
|
+ {
|
|
+ boost::system::error_code ec;
|
|
+ std::string configStrategy =
|
|
+ sdbusplus::common::xyz::openbmc_project::software::
|
|
+ ApplyOptions::convertConfigManagementToString(
|
|
+ convertConfigStrategy(action));
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, updateService, updateObjPath + biosId,
|
|
+ "org.freedesktop.DBus.Properties", "Set",
|
|
+ updateApplyOptionsInterface, updateClearConfig,
|
|
+ std::variant<std::string>(configStrategy));
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error setting ClearConfig property from {}: {}",
|
|
+ updateObjPath + biosId, ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("active property not found..");
|
|
+ asyncResp->res.result(boost::beast::http::status::bad_request);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Set UpdateSlot
|
|
+ if (targetSlot == "Primary" || targetSlot == "Secondary" ||
|
|
+ targetSlot == "Both")
|
|
+ {
|
|
+ boost::system::error_code ec;
|
|
+ std::string t =
|
|
+ sdbusplus::common::xyz::openbmc_project::software::UpdateTarget::
|
|
+ convertTargetSlotToString(convertTargetSlot(targetSlot));
|
|
+
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, updateService, updateObjPath + biosId,
|
|
+ "org.freedesktop.DBus.Properties", "Set", updateTargetInterface,
|
|
+ updateTargetSlot, std::variant<std::string>(t));
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error setting UpdateTargetSlot property from {}: {}",
|
|
+ updateObjPath + bmcId, ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ versionPurpose = versionPurposeHost;
|
|
+ fwId = biosId;
|
|
+ }
|
|
+ else if (target == "MBCPLD")
|
|
+ {
|
|
+ fwId = mbCpldId;
|
|
+ versionPurpose = versionPurposeMBCPLD;
|
|
+ }
|
|
+ else if (target == "FCBCPLD")
|
|
+ {
|
|
+ fwId = fcbCpldId;
|
|
+ versionPurpose = versionPurposeFCBCPLD;
|
|
+ }
|
|
+
|
|
+ if (asyncResp->res.result() != boost::beast::http::status::no_content)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Add Async Queue
|
|
+ boost::system::error_code ec;
|
|
+ crow::connections::systemBus->yield_method_call<void>(
|
|
+ yield, ec, asyncUpdateService, asyncUpdateObjPath,
|
|
+ "xyz.openbmc_project.Software.AsyncUpdate", asyncUpdateMethod,
|
|
+ versionPurpose, fwId);
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error calling AddQueue method: {}", ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ //crow::connections::systemBus->yield_method_call<void>(
|
|
+ // yield, ec, asyncUpdateService, asyncUpdateObjPath,
|
|
+ // "xyz.openbmc_project.Software.AsyncUpdate", "Start");
|
|
+ //if (ec)
|
|
+ //{
|
|
+ // BMCWEB_LOG_ERROR("Error calling Start method: {}", ec.message());
|
|
+ // asyncResp->res.result(
|
|
+ // boost::beast::http::status::internal_server_error);
|
|
+ // if (!commonInterface)
|
|
+ // {
|
|
+ // asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 1;
|
|
+ // }
|
|
+ // return;
|
|
+ //}
|
|
+
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["Status"] = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+inline bool getFirmwareVersion(const boost::asio::yield_context& yield,
|
|
+ const std::string& fwId, std::string& version)
|
|
+{
|
|
+ boost::system::error_code ec;
|
|
+ auto response = crow::connections::systemBus
|
|
+ ->yield_method_call<std::variant<std::string>>(
|
|
+ yield, ec, updateService, updateObjPath + fwId,
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ updateVersionInterface, versionProperty);
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error getting Version property from {} : {}",
|
|
+ updateObjPath + fwId, ec.message());
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ const std::string* versionPtr = std::get_if<std::string>(&response);
|
|
+ if (versionPtr == nullptr)
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ version = *versionPtr;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+inline void getUpdateStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target,
|
|
+ const bool& commonInterface = false)
|
|
+{
|
|
+ boost::asio::spawn(crow::connections::systemBus->get_io_context(),
|
|
+ [asyncResp, target, commonInterface](
|
|
+ const boost::asio::yield_context& yield) {
|
|
+ std::string fwId;
|
|
+ bool fileExist = false;
|
|
+ std::string updateStatusflag;
|
|
+ std::string statusString;
|
|
+ uint8_t statusNumber = 1;
|
|
+
|
|
+ getUpdateStatusFileInfo(target, fwId, updateStatusflag, fileExist);
|
|
+
|
|
+ const bool success =
|
|
+ getFirmwareUpgradeStatus(yield, fwId, target, updateStatusflag,
|
|
+ fileExist, statusString, statusNumber);
|
|
+ if (!success)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!commonInterface)
|
|
+ {
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["UpdateStatus"] =
|
|
+ statusNumber;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nlohmann::json targetStatus;
|
|
+ bool peerNodeUpgrading = false;
|
|
+
|
|
+ // 1: Failed
|
|
+ if (statusNumber == 1)
|
|
+ {
|
|
+ if (target == "FCBCPLD")
|
|
+ {
|
|
+ peerNodeUpgrading = isPeerNodeUpgrading(
|
|
+ yield, asyncResp, "FanCpldUpgradeFlag",
|
|
+ commonInterface);
|
|
+ if (fancpldLastUpgradeStatus != "Failed" &&
|
|
+ peerNodeUpgrading)
|
|
+ {
|
|
+ statusString = "Conflicted";
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (asyncResp->res.result() != boost::beast::http::status::ok)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // 5: Interrupted
|
|
+ if (statusNumber == 5)
|
|
+ {
|
|
+ if (asyncResp->res.result() != boost::beast::http::status::ok)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ updateGlobalUpdateStatus(target, statusString);
|
|
+
|
|
+ targetStatus[target]["UpdateStatus"] = statusString;
|
|
+ targetStatus[target]["UpdateProgress"] =
|
|
+ nlohmann::json::value_t::null;
|
|
+
|
|
+ // 2: Updating
|
|
+ if (statusNumber == 2)
|
|
+ {
|
|
+ boost::system::error_code ec;
|
|
+ auto response =
|
|
+ crow::connections::systemBus
|
|
+ ->yield_method_call<std::variant<uint8_t>>(
|
|
+ yield, ec, updateService, updateObjPath + fwId,
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ activationProgressInterface, progressProperty);
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error getting Progress property from {} : {}",
|
|
+ updateObjPath + fwId, ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ const uint8_t* progress = std::get_if<uint8_t>(&response);
|
|
+ if (progress == nullptr)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ targetStatus[target]["UpdateProgress"] = *progress;
|
|
+ }
|
|
+
|
|
+ std::string version;
|
|
+ boost::system::error_code ec;
|
|
+ // 0: Updated
|
|
+ if (statusNumber == 0 && target != "BMC")
|
|
+ {
|
|
+ auto dbusData =
|
|
+ crow::connections::systemBus
|
|
+ ->yield_method_call<dbus::utility::ManagedObjectType>(
|
|
+ yield, ec, updateService,
|
|
+ "/xyz/openbmc_project/software",
|
|
+ "org.freedesktop.DBus.ObjectManager",
|
|
+ "GetManagedObjects");
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("BMCUpdater GetManagedObjects error: {}",
|
|
+ ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (const auto& object : dbusData)
|
|
+ {
|
|
+ const std::string* dbusPurpose = nullptr;
|
|
+ const std::string* dbusVersion = nullptr;
|
|
+ const std::string* dbusActivation = nullptr;
|
|
+ for (const auto& interface : object.second)
|
|
+ {
|
|
+ if (interface.first == updateVersionInterface)
|
|
+ {
|
|
+ for (const auto& property : interface.second)
|
|
+ {
|
|
+ if (property.first == "Purpose")
|
|
+ {
|
|
+ dbusPurpose = std::get_if<std::string>(
|
|
+ &property.second);
|
|
+ }
|
|
+ if (property.first == "Version")
|
|
+ {
|
|
+ dbusVersion = std::get_if<std::string>(
|
|
+ &property.second);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else if (interface.first == updateActivationInterface)
|
|
+ {
|
|
+ for (const auto& property : interface.second)
|
|
+ {
|
|
+ if (property.first == "Activation")
|
|
+ {
|
|
+ dbusActivation = std::get_if<std::string>(
|
|
+ &property.second);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (dbusPurpose != nullptr && dbusVersion != nullptr &&
|
|
+ dbusActivation != nullptr)
|
|
+ {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (dbusPurpose != nullptr && dbusVersion != nullptr &&
|
|
+ dbusActivation != nullptr)
|
|
+ {
|
|
+ std::string purpose = dbusPurpose->substr(
|
|
+ dbusPurpose->find_last_of('.') + 1);
|
|
+ if (dbusActivation->ends_with("Active") &&
|
|
+ purposeToTarget.find(purpose) !=
|
|
+ purposeToTarget.end() &&
|
|
+ purposeToTarget.at(purpose) == target)
|
|
+ {
|
|
+ version = *dbusVersion;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ auto response =
|
|
+ crow::connections::systemBus
|
|
+ ->yield_method_call<std::variant<std::string>>(
|
|
+ yield, ec, updateService, updateObjPath + fwId,
|
|
+ "org.freedesktop.DBus.Properties", "Get",
|
|
+ updateVersionInterface, versionProperty);
|
|
+ if (ec)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ "Error getting Version property from {} : {}",
|
|
+ updateObjPath + fwId, ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ const std::string* versionPtr =
|
|
+ std::get_if<std::string>(&response);
|
|
+ if (versionPtr == nullptr)
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ version = *versionPtr;
|
|
+ }
|
|
+
|
|
+ if (version.empty())
|
|
+ {
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+ targetStatus[target]["BothUpdateEnable"] = true;
|
|
+ targetStatus[target]["VersionStatus"] = version;
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"].push_back(
|
|
+ targetStatus);
|
|
+ }
|
|
+ });
|
|
+}
|
|
+
|
|
+inline void
|
|
+ syncUpdateStatusHandler(const crow::Request& /*unused*/,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target,
|
|
+ const bool& commonInterface = false)
|
|
+{
|
|
+ getUpdateStatus(asyncResp, target, commonInterface);
|
|
+}
|
|
+
|
|
+inline void asyncUpdateStatusHandler(
|
|
+ const crow::Request& /*unused*/,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& target, const bool& commonInterface = false)
|
|
+{
|
|
+ getUpdateStatus(asyncResp, target, commonInterface);
|
|
+}
|
|
+
|
|
+inline void handlePostFirmwareUpdate(
|
|
+ const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
|
|
+{
|
|
+ std::string target;
|
|
+ std::optional<std::string> action;
|
|
+ std::string targetSlot;
|
|
+
|
|
+ asyncResp->res.result(boost::beast::http::status::no_content);
|
|
+
|
|
+ if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Target",
|
|
+ target, "Action", action))
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("readJsonPatch error");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // If the target is BMC, first clarify the Slot to be upgraded.
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ targetSlot = "Primary";
|
|
+ }
|
|
+ else if (target == "BMC_Secondary")
|
|
+ {
|
|
+ targetSlot = "Secondary";
|
|
+ target = "BMC";
|
|
+ }
|
|
+
|
|
+ if (target == "BIOS")
|
|
+ {
|
|
+ targetSlot = "Primary";
|
|
+ }
|
|
+ else if (target == "BIOS_Secondary")
|
|
+ {
|
|
+ targetSlot = "Secondary";
|
|
+ target = "BIOS";
|
|
+ }
|
|
+
|
|
+ // When the target does not exist, return 404 Not Found.
|
|
+ if (firmwareTypeAndId.find(target) == firmwareTypeAndId.end() &&
|
|
+ target != "All")
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("target property error.");
|
|
+ asyncResp->res.result(boost::beast::http::status::not_found);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (action == std::nullopt)
|
|
+ {
|
|
+ action = "Keep";
|
|
+ }
|
|
+
|
|
+ std::tuple<std::string, std::string> updateStrategy =
|
|
+ std::make_tuple(*action, targetSlot);
|
|
+
|
|
+ boost::asio::spawn(crow::connections::systemBus->get_io_context(),
|
|
+ [asyncResp, target, action, updateStrategy](
|
|
+ const boost::asio::yield_context& yield) mutable {
|
|
+ if (target == "All")
|
|
+ {
|
|
+ // When the upgrade list includes BMC and the target is All,
|
|
+ auto bmcIter = firmwareTypeAndId.find("BMC");
|
|
+ if (bmcIter != firmwareTypeAndId.end())
|
|
+ {
|
|
+ bool isBmcUpdated = isUpgradeCompleted(yield, bmcIter->first);
|
|
+
|
|
+ if (!isBmcUpdated)
|
|
+ {
|
|
+ bool allowedBmcUpgrade = true;
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ if (((fwType != "BMC") &&
|
|
+ (!isUpgradeCompleted(yield, fwType))))
|
|
+ {
|
|
+ allowedBmcUpgrade = false;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!allowedBmcUpgrade)
|
|
+ {
|
|
+ // When the upgrade list includes BMC and other firmware
|
|
+ // in the upgrade, 405 Method Not Allowed is returned.
|
|
+ BMCWEB_LOG_ERROR(
|
|
+ " when targent is All,BMC is not allowed.");
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::method_not_allowed);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // When the upgrade list only contains upgradeable BMC and
|
|
+ // the target is All, the primary and secondary partitions
|
|
+ // are upgraded.
|
|
+ updateStrategy = {*action, "Both"};
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bool existUploadedFirmware = false;
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ if (isUploadedStatus(yield, fwType))
|
|
+ {
|
|
+ existUploadedFirmware = true;
|
|
+ if (fwType == "BMC")
|
|
+ {
|
|
+ syncUpdateImageHandler(yield, asyncResp, fwType, true,
|
|
+ updateStrategy);
|
|
+ }
|
|
+ else if(fwType == "BIOS")
|
|
+ {
|
|
+ updateStrategy = {*action, "Both"};
|
|
+ asyncUpdateImageHandler(yield, asyncResp, fwType, true,
|
|
+ updateStrategy);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ asyncUpdateImageHandler(yield, asyncResp, fwType, true,
|
|
+ updateStrategy);
|
|
+ }
|
|
+
|
|
+ if (asyncResp->res.result() !=
|
|
+ boost::beast::http::status::no_content)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!existUploadedFirmware)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(" Uploaded target not exist.");
|
|
+ asyncResp->res.result(boost::beast::http::status::not_found);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (!isUploadedStatus(yield, target))
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(" Uploaded target not exist.");
|
|
+ asyncResp->res.result(boost::beast::http::status::not_found);
|
|
+ return;
|
|
+ }
|
|
+ if (target == "BMC")
|
|
+ {
|
|
+ syncUpdateImageHandler(yield, asyncResp, target, true,
|
|
+ updateStrategy);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ asyncUpdateImageHandler(yield, asyncResp, target, true,
|
|
+ updateStrategy);
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+}
|
|
+
|
|
+inline void
|
|
+ deleteSpecifiedFirmware(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
|
|
+ const std::string& fwId, const std::string& target)
|
|
+{
|
|
+ crow::connections::systemBus->async_method_call(
|
|
+ [asyncResp, target](const boost::system::error_code& ec) {
|
|
+ // After the upgrade fails, the corresponding dbus path may
|
|
+ // have been deleted and no longer exists. In this case,
|
|
+ // calling the Delete method will return an
|
|
+ // "Invalid request descriptor" error code.
|
|
+ // The corresponding Linux code of this error code is
|
|
+ // "EBADR".
|
|
+ if (ec && ec.value() != EBADR)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Error calling Delete method: {}", ec.message());
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::internal_server_error);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ firmwareTypeAndId.erase(target);
|
|
+ clearGlobalFirmwareId(target);
|
|
+ clearGlobalUpdateStatus(target);
|
|
+ },
|
|
+ updateService, updateObjPath + fwId, deleteInterface, deleteProperty);
|
|
+}
|
|
+
|
|
+inline bool isNotSupportedDeletion(const boost::asio::yield_context& yield,
|
|
+ const std::string& target)
|
|
+{
|
|
+ std::string updateStatusflag;
|
|
+ std::string tempFwId;
|
|
+ bool fileExist = false;
|
|
+ std::string statusString;
|
|
+ uint8_t statusNumber = 0;
|
|
+ std::string version;
|
|
+
|
|
+ getUpdateStatusFileInfo(target, tempFwId, updateStatusflag, fileExist);
|
|
+ const bool success = getFirmwareUpgradeStatus(yield, tempFwId, target,
|
|
+ updateStatusflag, fileExist,
|
|
+ statusString, statusNumber);
|
|
+ if (success)
|
|
+ {
|
|
+ if (statusString == "Updating" || statusString == "Queued" ||
|
|
+ statusString == "Updated")
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+inline void handleDeleteFirmwareUpdate(
|
|
+ const crow::Request& req,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
|
|
+{
|
|
+ std::string target;
|
|
+ auto fwTypeAndStatus =
|
|
+ std::make_shared<std::unordered_map<std::string, std::string>>();
|
|
+
|
|
+ asyncResp->res.result(boost::beast::http::status::no_content);
|
|
+
|
|
+ if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Target",
|
|
+ target))
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("readJsonPatch error");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (firmwareTypeAndId.find(target) == firmwareTypeAndId.end() &&
|
|
+ target != "All")
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("target property error.");
|
|
+ asyncResp->res.result(boost::beast::http::status::not_found);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ boost::asio::spawn(
|
|
+ crow::connections::systemBus->get_io_context(),
|
|
+ [asyncResp, target](const boost::asio::yield_context& yield) {
|
|
+ if (target == "All")
|
|
+ {
|
|
+ // Get the upgrade status of all firmware in the firmware list.
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ if (isNotSupportedDeletion(yield, fwType))
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Status Conflict !");
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::method_not_allowed);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ deleteSpecifiedFirmware(asyncResp, fwId, fwType);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (isNotSupportedDeletion(yield, target))
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR("Status Conflict !");
|
|
+ asyncResp->res.result(
|
|
+ boost::beast::http::status::method_not_allowed);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ deleteSpecifiedFirmware(asyncResp, firmwareTypeAndId.at(target),
|
|
+ target);
|
|
+ }
|
|
+ });
|
|
+}
|
|
+
|
|
+inline bool NeedDelete(const std::string& target)
|
|
+{
|
|
+ std::string updateStatusflag;
|
|
+ std::string tempFwId;
|
|
+ bool fileExist = false;
|
|
+
|
|
+ getUpdateStatusFileInfo(target, tempFwId, updateStatusflag, fileExist);
|
|
+
|
|
+ if(tempFwId.empty())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+inline void
|
|
+ handleFirmwareStatus(const crow::Request& /*unused*/,
|
|
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
|
|
+{
|
|
+ std::string target;
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"] = nlohmann::json::array();
|
|
+
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ if(NeedDelete(fwType))
|
|
+ {
|
|
+ target = fwType;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(!target.empty())
|
|
+ {
|
|
+ firmwareTypeAndId.erase(target);
|
|
+ }
|
|
+
|
|
+ for (const auto& [fwType, fwId] : firmwareTypeAndId)
|
|
+ {
|
|
+ getUpdateStatus(asyncResp, fwType, true);
|
|
+ }
|
|
+}
|
|
+} // namespace luxshare_update
|
|
+} // namespace crow
|
|
diff --git a/meson.build b/meson.build
|
|
index 3afb44e1..6ddf2be9 100644
|
|
--- a/meson.build
|
|
+++ b/meson.build
|
|
@@ -238,6 +238,7 @@ endif
|
|
|
|
add_project_arguments(
|
|
cxx.get_supported_arguments([
|
|
+ '-DBOOST_ASIO_DISABLE_CONCEPTS',
|
|
'-DBOOST_ALL_NO_LIB',
|
|
'-DBOOST_ALLOW_DEPRECATED_HEADERS',
|
|
'-DBOOST_ASIO_DISABLE_THREADS',
|
|
@@ -245,11 +246,9 @@ cxx.get_supported_arguments([
|
|
'-DBOOST_ASIO_SEPARATE_COMPILATION',
|
|
'-DBOOST_BEAST_SEPARATE_COMPILATION',
|
|
'-DBOOST_EXCEPTION_DISABLE',
|
|
- '-DBOOST_NO_EXCEPTIONS',
|
|
'-DBOOST_URL_NO_SOURCE_LOCATION',
|
|
'-DJSON_NOEXCEPTION',
|
|
'-DOPENSSL_NO_FILENAMES',
|
|
- '-DSDBUSPLUS_DISABLE_BOOST_COROUTINES',
|
|
]),
|
|
language : 'cpp')
|
|
|
|
@@ -307,12 +306,28 @@ if not nlohmann_json.found()
|
|
endif
|
|
bmcweb_dependencies += nlohmann_json
|
|
|
|
-boost = dependency('boost',version : '>=1.82.0', required : false, include_type: 'system')
|
|
+boost = dependency(
|
|
+ 'boost',
|
|
+ modules: [
|
|
+ 'coroutine',
|
|
+ 'context',
|
|
+ ],
|
|
+ version : '>=1.82.0',
|
|
+ required : false,
|
|
+ include_type: 'system'
|
|
+)
|
|
+
|
|
if not boost.found()
|
|
boost = subproject('boost', required: true).get_variable('boost_dep')
|
|
boost = boost.as_system('system')
|
|
endif
|
|
-bmcweb_dependencies += boost
|
|
+
|
|
+bmcweb_dependencies += [boost]
|
|
+boost_coroutine = cxx.find_library('boost_coroutine', required: true)
|
|
+bmcweb_dependencies += [boost_coroutine]
|
|
+
|
|
+pdi_dep = dependency('phosphor-dbus-interfaces')
|
|
+bmcweb_dependencies += pdi_dep
|
|
|
|
if get_option('tests').enabled()
|
|
gtest = dependency('gtest', main: true, disabler: true, required : false)
|
|
diff --git a/redfish-core/include/utils/sw_utils.hpp b/redfish-core/include/utils/sw_utils.hpp
|
|
index cffc637f..0d6e2ba3 100644
|
|
--- a/redfish-core/include/utils/sw_utils.hpp
|
|
+++ b/redfish-core/include/utils/sw_utils.hpp
|
|
@@ -29,6 +29,8 @@ constexpr const char* biosPurpose =
|
|
constexpr const char* bmcPurpose =
|
|
"xyz.openbmc_project.Software.Version.VersionPurpose.BMC";
|
|
|
|
+constexpr auto bmcRuningSlot = "/run/media/slot";
|
|
+
|
|
/**
|
|
* @brief Populate the running software version and image links
|
|
*
|
|
@@ -376,5 +378,33 @@ inline void
|
|
});
|
|
}
|
|
|
|
+inline std::string getBMCRunningSlot()
|
|
+{
|
|
+ std::string slot;
|
|
+ std::ifstream in(bmcRuningSlot);
|
|
+ in >> slot;
|
|
+
|
|
+ return slot;
|
|
+}
|
|
+
|
|
+inline void
|
|
+ getSoftwareActiveSlot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
|
|
+{
|
|
+ const auto& activeSlotStr = getBMCRunningSlot();
|
|
+ uint8_t activeSlot = 0;
|
|
+
|
|
+ try
|
|
+ {
|
|
+ activeSlot = static_cast<uint8_t>(std::stoi(activeSlotStr));
|
|
+ }
|
|
+ catch (const std::exception&)
|
|
+ {
|
|
+ BMCWEB_LOG_ERROR(" bmc slot convert error");
|
|
+ messages::internalError(asyncResp->res);
|
|
+ return;
|
|
+ }
|
|
+ asyncResp->res.jsonValue["Oem"]["Luxshare"]["ActiveSlot"] = activeSlot;
|
|
+}
|
|
+
|
|
} // namespace sw_util
|
|
} // namespace redfish
|
|
diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
|
|
index 0008ad52..f4f43254 100644
|
|
--- a/src/webserver_main.cpp
|
|
+++ b/src/webserver_main.cpp
|
|
@@ -29,6 +29,7 @@
|
|
#include <sdbusplus/server.hpp>
|
|
|
|
#include <luxshare/sel.hpp>
|
|
+#include <luxshare/firmware_update.hpp>
|
|
|
|
#include <exception>
|
|
#include <memory>
|
|
@@ -120,6 +121,7 @@ static int run()
|
|
|
|
#ifdef BMCWEB_ENABLE_LUXSHARE_API
|
|
crow::luxshare_sel_api::requestRoutes(app);
|
|
+ crow::luxshare_update_firmware::requestRoutes(app);
|
|
#endif
|
|
|
|
if (bmcwebInsecureDisableXssPrevention != 0)
|
|
--
|
|
2.25.1
|
|
|