blob: ef580a5e2a9f1b02c718c4a2bff48410c5a19d83 [file]
#pragma once
#include "update_manager.hpp"
#include <xyz/openbmc_project/Software/ApplyTime/server.hpp>
#include <xyz/openbmc_project/Software/Update/server.hpp>
#include <memory>
#include <optional>
#include <span>
#include <spanstream>
#include <variant>
namespace pldm::fw_update
{
namespace software = sdbusplus::xyz::openbmc_project::Software::server;
using ItemUpdateIntf = sdbusplus::server::object_t<
sdbusplus::xyz::openbmc_project::Software::server::Update>;
using ApplyTimeIntf =
sdbusplus::xyz::openbmc_project::Software::server::ApplyTime;
class ItemUpdateManager : public UpdateManagerBase, public ItemUpdateIntf
{
public:
ItemUpdateManager() = delete;
ItemUpdateManager(const ItemUpdateManager&) = delete;
ItemUpdateManager(ItemUpdateManager&&) = delete;
ItemUpdateManager& operator=(const ItemUpdateManager&) = delete;
ItemUpdateManager& operator=(ItemUpdateManager&&) = delete;
~ItemUpdateManager() = default;
/**
* @brief Constructor for ItemUpdateManager
*
* @param[in] eid The MCTP EID
* @param[in] event The event object
* @param[in] handler The request handler
* @param[in] instanceIdDb The instance ID database
* @param[in] objPath The D-Bus object path
* @param[in] generatedId The software hash identifier
* @param[in] descriptors The descriptors for the device
* @param[in] componentInfo The component information for the device
*/
explicit ItemUpdateManager(
mctp_eid_t eid, Event& event,
pldm::requester::Handler<pldm::requester::Request>& handler,
InstanceIdDb& instanceIdDb, const std::string& objPath,
const std::string& generatedId, const Descriptors& descriptors,
const ComponentInfo& componentInfo,
const ConditionPaths& conditionPathPair = ConditionPaths{},
const std::string& conditionArg = std::string{},
std::function<void()> taskCompletionCallback = nullptr) :
UpdateManagerBase(event, handler, instanceIdDb),
ItemUpdateIntf(pldm::utils::DBusHandler::getBus(),
std::format("{}_{}", objPath, generatedId).c_str()),
eid(eid), objPath(objPath), descriptors(descriptors),
componentInfo(componentInfo), preConditionPath(conditionPathPair.first),
postConditionPath(conditionPathPair.second),
baseConditionArg(conditionArg), conditionArg(conditionArg),
taskCompletionCallback(std::move(taskCompletionCallback))
{}
/**
* @brief Handle PLDM requests for the item-based update manager
*
* @param[in] eid - Remote MCTP Endpoint ID
* @param[in] command - PLDM command code
* @param[in] request - PLDM request message
* @param[in] reqMsgLen - PLDM request message length
* @return PLDM response message
*/
Response handleRequest(mctp_eid_t eid, uint8_t command,
const pldm_msg* request, size_t reqMsgLen);
/**
* @brief Update the device completion status
*
* @param[in] eid - The MCTP EID of the device
* @param[in] status - The completion status (true for success, false for
* failure)
*/
void updateDeviceCompletion(mctp_eid_t eid, bool status) override;
/**
* @brief Update the activation progress status
*/
void updateActivationProgress() override;
/**
* @brief Activate the firmware update package
*/
void activatePackage() override;
/**
* @brief Clear the activation information
*/
void resetActivationState() override;
/**
* @brief Start the update process
*
* D-Bus method implementation for starting the update process
*
* @param[in] image The image file descriptor
* @param[in] applyTime The requested apply time (Immediate, OnReset,
* OnStart, etc.) This will be passed to post-condition services to allow
* conditional handling (e.g., skip reset if not
* immediate)
*/
virtual sdbusplus::object_path startUpdate(
sdbusplus::message::unix_fd image,
ApplyTimeIntf::RequestedApplyTimes applyTime =
ApplyTimeIntf::RequestedApplyTimes::Immediate) override;
/**
* @brief Associate the firmware update package with the target device
*
* This function associates the firmware update package with the specified
* target device by matching the device ID records and descriptors.
*
* @param[in] fwDeviceIDRecords The firmware device ID records
* @param[in] descriptors The descriptors to match against
*
* @return The offset of the device ID record if found, std::nullopt
* otherwise.
*/
std::optional<DeviceIDRecordOffset> associatePkgToDevice(
const FirmwareDeviceIDRecords& fwDeviceIDRecords,
const Descriptors& descriptors);
private:
mctp_eid_t eid;
std::string objPath;
std::string objPathWithSwId;
/**
* @brief The descriptors to match against
*/
const Descriptors& descriptors;
/**
* @brief The component information of the target device
*/
const ComponentInfo& componentInfo;
/**
* @brief The package data for the firmware update
*/
std::unique_ptr<pldm::utils::MMapHandler> packageMap;
/**
* @brief The package data stream for the firmware update
*/
std::unique_ptr<std::ispanstream> packageDataStream;
/**
* @brief Process the firmware update package
*
* @return true on success, false on failure
*/
bool processPackage();
/**
* @brief Send the defer request of the firmware update package
*
* @param[in] fd - The firmware update package file descriptor
* @return The D-Bus object path of the firmware update package
*/
std::string processFd(int fd);
void startFirmwareUpdate();
/**
* @brief Common completion path for an update attempt
*
* Reports the final activation state, releases the update resources and
* notifies the completion callback. This is the single completion path for
* both a finished firmware update and a failing pre/post update condition,
* so it tolerates being called before the firmware update flow started.
*
* @param[in] status - true when the update succeeded
*/
void completeUpdate(bool status);
/**
* @brief Common teardown path for cleaning up update resources
*
* Resets deviceUpdater, packageDataStream, packageMap, dupFd and
* marks updateInProgress as false. Should be called from all cleanup
* paths to ensure consistent resource management.
*/
void teardownUpdate();
std::unique_ptr<Activation> inProgressActivation;
std::unique_ptr<ActivationProgress> activationProgress;
std::unique_ptr<PackageParser> parser;
std::unique_ptr<DeviceUpdater> deviceUpdater;
/**
* @brief Start time of the firmware update flow
*
* Only set once the update has been activated, so that a completion before
* that point does not report a bogus duration.
*/
std::optional<decltype(std::chrono::steady_clock::now())> startTime;
/**
* @brief The defer handler for processing package
*/
std::unique_ptr<sdeventplus::source::Defer> deferHandler;
/**
* @brief RAII wrapper for the duplicated package file descriptor
*/
std::unique_ptr<pldm::utils::CustomFD> dupFd;
/**
* @brief Liveness token for callbacks that can outlive this manager
*
* A condition callback is owned by the SystemdInterface singleton, which
* lives as long as the daemon, while this manager is erased as soon as its
* MCTP endpoint goes away. Callbacks therefore capture a weak reference to
* this token and return early once it has expired, both while the StartUnit
* reply is still pending and after the callback has been registered for the
* JobRemoved signal.
*/
std::shared_ptr<std::monostate> aliveToken =
std::make_shared<std::monostate>();
std::string preConditionPath;
std::string postConditionPath;
/**
* @brief Named arguments known before an update is requested
*/
std::string baseConditionArg;
/**
* @brief All named arguments passed to a parameterized condition service
*
* The base arguments plus the fields only known once an update has been
* requested. Ignored for a condition service that is not parameterized.
*/
std::string conditionArg;
ApplyTimeIntf::RequestedApplyTimes applyTime =
ApplyTimeIntf::RequestedApplyTimes::Immediate;
/**
* @brief Convert applyTime enum to string representation
* @return String representation of applyTime
*/
std::string applyTimeToString() const;
std::function<void()> taskCompletionCallback;
bool updateInProgress = false;
/** @brief The last progress that was calculated. Used to avoid spamming
* dbus
*
*/
uint8_t lastProgress;
};
} // namespace pldm::fw_update