| #include "device.hpp" | 
 |  | 
 | #include "common/pldm/pldm_package_util.hpp" | 
 | #include "software.hpp" | 
 | #include "software_manager.hpp" | 
 |  | 
 | #include <phosphor-logging/lg2.hpp> | 
 | #include <sdbusplus/asio/object_server.hpp> | 
 | #include <sdbusplus/async/context.hpp> | 
 | #include <sdbusplus/bus.hpp> | 
 | #include <xyz/openbmc_project/Association/Definitions/server.hpp> | 
 | #include <xyz/openbmc_project/Software/ActivationProgress/aserver.hpp> | 
 | #include <xyz/openbmc_project/State/Host/client.hpp> | 
 |  | 
 | #include <utility> | 
 |  | 
 | PHOSPHOR_LOG2_USING; | 
 |  | 
 | using namespace phosphor::software::device; | 
 |  | 
 | using SoftwareActivationProgress = | 
 |     sdbusplus::aserver::xyz::openbmc_project::software::ActivationProgress< | 
 |         phosphor::software::Software>; | 
 |  | 
 | using SoftwareActivationProgressProperties = sdbusplus::common::xyz:: | 
 |     openbmc_project::software::ActivationProgress::properties_t; | 
 |  | 
 | const auto applyTimeImmediate = sdbusplus::common::xyz::openbmc_project:: | 
 |     software::ApplyTime::RequestedApplyTimes::Immediate; | 
 |  | 
 | const auto ActivationInvalid = ActivationInterface::Activations::Invalid; | 
 | const auto ActivationFailed = ActivationInterface::Activations::Failed; | 
 |  | 
 | Device::Device(sdbusplus::async::context& ctx, const SoftwareConfig& config, | 
 |                manager::SoftwareManager* parent, | 
 |                std::set<RequestedApplyTimes> allowedApplyTimes = | 
 |                    {RequestedApplyTimes::Immediate, | 
 |                     RequestedApplyTimes::OnReset}) : | 
 |     allowedApplyTimes(std::move(allowedApplyTimes)), config(config), | 
 |     parent(parent), ctx(ctx) | 
 | {} | 
 |  | 
 | sdbusplus::async::task<bool> Device::getImageInfo( | 
 |     std::unique_ptr<void, std::function<void(void*)>>& pldmPackage, | 
 |     size_t pldmPackageSize, uint8_t** matchingComponentImage, | 
 |     size_t* componentImageSize, std::string& componentVersion) | 
 |  | 
 | { | 
 |     std::shared_ptr<PackageParser> packageParser = | 
 |         pldm_package_util::parsePLDMPackage( | 
 |             static_cast<uint8_t*>(pldmPackage.get()), pldmPackageSize); | 
 |  | 
 |     if (packageParser == nullptr) | 
 |     { | 
 |         error("could not parse PLDM package"); | 
 |         co_return false; | 
 |     } | 
 |  | 
 |     uint32_t componentOffset = 0; | 
 |     const int status = pldm_package_util::extractMatchingComponentImage( | 
 |         packageParser, config.compatibleHardware, config.vendorIANA, | 
 |         &componentOffset, componentImageSize, componentVersion); | 
 |  | 
 |     if (status != 0) | 
 |     { | 
 |         error("could not extract matching component image"); | 
 |         co_return false; | 
 |     } | 
 |  | 
 |     *matchingComponentImage = | 
 |         static_cast<uint8_t*>(pldmPackage.get()) + componentOffset; | 
 |  | 
 |     co_return true; | 
 | } | 
 |  | 
 | sdbusplus::async::task<bool> Device::startUpdateAsync( | 
 |     sdbusplus::message::unix_fd image, RequestedApplyTimes applyTime, | 
 |     std::unique_ptr<Software> softwarePendingIn) | 
 | { | 
 |     debug("starting the async update with memfd {FD}", "FD", image.fd); | 
 |  | 
 |     size_t pldm_pkg_size = 0; | 
 |     auto pldm_pkg = pldm_package_util::mmapImagePackage(image, &pldm_pkg_size); | 
 |  | 
 |     if (pldm_pkg == nullptr) | 
 |     { | 
 |         softwarePendingIn->setActivation(ActivationInvalid); | 
 |         co_return false; | 
 |     } | 
 |  | 
 |     uint8_t* componentImage; | 
 |     size_t componentImageSize = 0; | 
 |     std::string componentVersion; | 
 |  | 
 |     if (!co_await getImageInfo(pldm_pkg, pldm_pkg_size, &componentImage, | 
 |                                &componentImageSize, componentVersion)) | 
 |     { | 
 |         error("could not extract matching component image"); | 
 |         softwarePendingIn->setActivation(ActivationInvalid); | 
 |         co_return false; | 
 |     } | 
 |  | 
 |     std::unique_ptr<Software> softwarePendingOld = std::move(softwarePending); | 
 |  | 
 |     softwarePending = std::move(softwarePendingIn); | 
 |     softwarePendingIn = nullptr; | 
 |  | 
 |     const bool success = co_await continueUpdateWithMappedPackage( | 
 |         componentImage, componentImageSize, componentVersion, applyTime); | 
 |  | 
 |     if (!success) | 
 |     { | 
 |         softwarePending->setActivation(ActivationFailed); | 
 |         error("Failed to update the software for {SWID}", "SWID", | 
 |               softwareCurrent->swid); | 
 |  | 
 |         softwarePending = std::move(softwarePendingOld); | 
 |  | 
 |         co_return false; | 
 |     } | 
 |  | 
 |     if (applyTime == RequestedApplyTimes::Immediate) | 
 |     { | 
 |         softwareCurrent = std::move(softwarePending); | 
 |  | 
 |         // In case an immediate update is triggered after an update for | 
 |         // onReset. | 
 |         softwarePending = nullptr; | 
 |  | 
 |         debug("Successfully updated to software version {SWID}", "SWID", | 
 |               softwareCurrent->swid); | 
 |     } | 
 |  | 
 |     co_return true; | 
 | } | 
 |  | 
 | std::string Device::getEMConfigType() const | 
 | { | 
 |     return config.configType; | 
 | } | 
 |  | 
 | sdbusplus::async::task<bool> Device::resetDevice() | 
 | { | 
 |     debug("Default implementation for device reset"); | 
 |  | 
 |     co_return true; | 
 | } | 
 |  | 
 | bool Device::setUpdateProgress(uint8_t progress) const | 
 | { | 
 |     if (!softwarePending || !softwarePending->softwareActivationProgress) | 
 |     { | 
 |         return false; | 
 |     } | 
 |  | 
 |     softwarePending->softwareActivationProgress->progress(progress); | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | sdbusplus::async::task<bool> Device::continueUpdateWithMappedPackage( | 
 |     const uint8_t* matchingComponentImage, size_t componentImageSize, | 
 |     const std::string& componentVersion, RequestedApplyTimes applyTime) | 
 | { | 
 |     softwarePending->setActivation(ActivationInterface::Activations::Ready); | 
 |  | 
 |     softwarePending->setVersion(componentVersion, | 
 |                                 softwareCurrent->getPurpose().value_or( | 
 |                                     SoftwareVersion::VersionPurpose::Unknown)); | 
 |  | 
 |     std::string objPath = softwarePending->objectPath; | 
 |  | 
 |     softwarePending->softwareActivationProgress = | 
 |         std::make_unique<SoftwareActivationProgress>( | 
 |             ctx, objPath.c_str(), SoftwareActivationProgressProperties{0}); | 
 |  | 
 |     softwarePending->softwareActivationProgress->emit_added(); | 
 |  | 
 |     softwarePending->setActivationBlocksTransition(true); | 
 |  | 
 |     softwarePending->setActivation( | 
 |         ActivationInterface::Activations::Activating); | 
 |  | 
 |     bool success = | 
 |         co_await updateDevice(matchingComponentImage, componentImageSize); | 
 |  | 
 |     if (success) | 
 |     { | 
 |         softwarePending->setActivation( | 
 |             ActivationInterface::Activations::Active); | 
 |     } | 
 |  | 
 |     softwarePending->setActivationBlocksTransition(false); | 
 |  | 
 |     softwarePending->softwareActivationProgress = nullptr; | 
 |  | 
 |     if (!success) | 
 |     { | 
 |         // do not apply the update, it has failed. | 
 |         // We can delete the new software version. | 
 |  | 
 |         co_return false; | 
 |     } | 
 |  | 
 |     if (applyTime == applyTimeImmediate) | 
 |     { | 
 |         co_await resetDevice(); | 
 |  | 
 |         co_await softwarePending->createInventoryAssociations(true); | 
 |  | 
 |         softwarePending->enableUpdate(allowedApplyTimes); | 
 |     } | 
 |     else | 
 |     { | 
 |         co_await softwarePending->createInventoryAssociations(false); | 
 |     } | 
 |  | 
 |     co_return true; | 
 | } |