Files
OpenBMC/meta-luxshare/recipes-phosphor/interfaces/bmcweb/0001-Add-luxshare-oem-redfish-update-interface.patch
T
2026-04-23 17:07:55 +08:00

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