fw_update: target specific startUpdate implementation

Description:
This commit implements the `startUpdate` handling of the target specific
update flow [1], which introduced `ItemUpdateManager` for handling
target specific updates.

Motivation:
We have introduced the `FirmwareInventory`/`FirmwareInventoryManager`
classes to manage D-Bus interfaces for firmware update, After that, we
would like to implement the `StartUpdate` method for `FirmwareInventory`
properly for the update process implementation, which
`AggregateUpdateManager` will help route the command to the specific
update sessions.

Test results:
- Successful FD item Firmware update performed on Yosemite4, by manually
  triggering the `StartUpdate` method call on the
  `xyz.openbmc_project.Software.Update` interface.
- Successful FD item Firmware update performed on Yosemite4, by using
  Redfish API to trigger the update.
  (main stream version, no patch needed)
- Successful firmware update performed on Yosemite4 via the dir inotify
  based flow.
- Successful firmware update performed on Yosemite4 via the multipart
  image flow.
- Successful firmware update performed on Yosemite4 after a previous
  failed update attempt.
-

[1]: https://github.com/openbmc/docs/blob/master/designs/code-update.md

Change-Id: I724f0987eb882a1b4e6bce8c209b319c927689e9
Signed-off-by: Unive Tien <unive.tien.wiwynn@gmail.com>
diff --git a/common/utils.cpp b/common/utils.cpp
index b7de564..d0bd39e 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -2,9 +2,12 @@
 
 #include "common/start_lifetime_as.hpp"
 
+#include <fcntl.h>
 #include <libpldm/pdr.h>
 #include <libpldm/pldm_types.h>
 #include <linux/mctp.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
 
 #include <xyz/openbmc_project/BIOSConfig/Manager/client.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -15,6 +18,7 @@
 
 #include <algorithm>
 #include <cctype>
+#include <cstdlib>
 #include <ctime>
 #include <fstream>
 #include <iostream>
@@ -22,6 +26,7 @@
 #include <memory>
 #include <stdexcept>
 #include <string>
+#include <system_error>
 #include <vector>
 
 PHOSPHOR_LOG2_USING;
@@ -971,5 +976,179 @@
     return random() % 10000;
 }
 
+MMapHandler::MMapHandler(int fd, std::optional<size_t> size) :
+    fd(fd), ownsFd(false) // External fd, not owned by MMapHandler
+{
+    struct stat sb;
+    if (fstat(fd, &sb) == -1)
+    {
+        error("Failed to get the actual file size");
+        data = nullptr;
+        throw std::runtime_error("Failed to get the actual file size");
+    }
+
+    if (size.has_value())
+    {
+        this->size = size.value();
+        if (this->size > static_cast<size_t>(sb.st_size))
+        {
+            error("Requested size {REQSIZE} exceeds file size {FILESIZE}",
+                  "REQSIZE", this->size, "FILESIZE", sb.st_size);
+            data = nullptr;
+            throw std::invalid_argument("Requested size exceeds file size");
+        }
+    }
+    else
+    {
+        this->size = static_cast<size_t>(sb.st_size);
+    }
+
+    data = static_cast<char*>(
+        mmap(nullptr, this->size, PROT_READ, MAP_PRIVATE, fd, 0));
+    if (data == MAP_FAILED)
+    {
+        int savedErrno = errno;
+        data = nullptr;
+        error("mmap failed: size {SIZE}, error: {ERROR}", "SIZE", this->size,
+              "ERROR", savedErrno);
+        throw std::system_error(savedErrno, std::system_category(),
+                                "mmap failed");
+    }
+}
+
+MMapHandler::MMapHandler(const std::filesystem::path& path,
+                         std::optional<size_t> size) :
+    ownsFd(true) // MMapHandler owns this fd and will close it
+{
+    fd = open(path.c_str(), O_RDONLY);
+    if (fd < 0)
+    {
+        int savedErrno = errno;
+        error("Failed to open file: {PATH}, error: {ERROR}", "PATH",
+              path.c_str(), "ERROR", savedErrno);
+        throw std::system_error(savedErrno, std::system_category(),
+                                "Failed to open file: " + path.string());
+    }
+
+    try
+    {
+        struct stat sb;
+        if (fstat(fd, &sb) == -1)
+        {
+            int savedErrno = errno;
+            close(fd);
+            fd = -1;
+            error("Failed to fstat file: {ERROR}", "ERROR", savedErrno);
+            throw std::system_error(savedErrno, std::system_category(),
+                                    "Failed to fstat file");
+        }
+
+        if (size.has_value())
+        {
+            this->size = size.value();
+            if (this->size > static_cast<size_t>(sb.st_size))
+            {
+                close(fd);
+                fd = -1;
+                error("Requested size {REQSIZE} exceeds file size {FILESIZE}",
+                      "REQSIZE", this->size, "FILESIZE", sb.st_size);
+                throw std::invalid_argument("Requested size exceeds file size");
+            }
+        }
+        else
+        {
+            this->size = static_cast<size_t>(sb.st_size);
+        }
+
+        data = static_cast<char*>(
+            mmap(nullptr, this->size, PROT_READ, MAP_PRIVATE, fd, 0));
+        if (data == MAP_FAILED)
+        {
+            int savedErrno = errno;
+            close(fd);
+            fd = -1;
+            data = nullptr;
+            error("mmap failed: size {SIZE}, error: {ERROR}", "SIZE",
+                  this->size, "ERROR", savedErrno);
+            throw std::system_error(savedErrno, std::system_category(),
+                                    "mmap failed");
+        }
+    }
+    catch (...)
+    {
+        if (fd >= 0)
+        {
+            close(fd);
+            fd = -1;
+        }
+        throw;
+    }
+}
+
+MMapHandler::~MMapHandler()
+{
+    if (data)
+    {
+        munmap(data, size);
+    }
+
+    // Only close fd if we own it (opened via path constructor)
+    if (ownsFd && fd >= 0)
+    {
+        close(fd);
+    }
+}
+
+char* MMapHandler::getData()
+{
+    return data;
+}
+
+const char* MMapHandler::getData() const
+{
+    return data;
+}
+
+std::span<const uint8_t> MMapHandler::getBytes() const
+{
+    if (!data)
+    {
+        return {};
+    }
+    return {reinterpret_cast<const uint8_t*>(data), size};
+}
+
+std::span<uint8_t> MMapHandler::getBytes()
+{
+    if (!data)
+    {
+        return {};
+    }
+    return {reinterpret_cast<uint8_t*>(data), size};
+}
+
+std::span<const char> MMapHandler::getChars() const
+{
+    if (!data)
+    {
+        return {};
+    }
+    return {data, size};
+}
+
+std::span<char> MMapHandler::getChars()
+{
+    if (!data)
+    {
+        return {};
+    }
+    return {data, size};
+}
+
+size_t MMapHandler::getSize() const
+{
+    return size;
+}
+
 } // namespace utils
 } // namespace pldm
diff --git a/common/utils.hpp b/common/utils.hpp
index b0b14e0..7003ad5 100644
--- a/common/utils.hpp
+++ b/common/utils.hpp
@@ -21,6 +21,7 @@
 #include <cstdint>
 #include <filesystem>
 #include <map>
+#include <span>
 #include <string>
 #include <variant>
 #include <vector>
@@ -751,5 +752,116 @@
  */
 void setBiosAttr(const PendingAttributesList& biosAttrList);
 
+/** @brief RAII class to handle mmap and munmap */
+class MMapHandler
+{
+  public:
+    /** @brief Constructor to handle mmap with file descriptor
+     *
+     *  @param[in] fd - file descriptor to be mapped
+     *  @param[in] size - size to be mapped (optional)
+     *
+     *  @note The file descriptor is not owned by MMapHandler and will not be
+     *        closed in the destructor when constructed with this method.
+     */
+    MMapHandler(int fd, std::optional<size_t> size = std::nullopt);
+
+    /** @brief Constructor to handle mmap with file path
+     *
+     *  Opens the file, memory maps it, and manages the file descriptor
+     * lifecycle.
+     *
+     *  @param[in] path - filesystem path to the file to be opened and mapped
+     *  @param[in] size - size to be mapped (optional)
+     *
+     *  @note The file descriptor is owned by MMapHandler and will be
+     *        automatically closed in the destructor.
+     */
+    explicit MMapHandler(const std::filesystem::path& path,
+                         std::optional<size_t> size = std::nullopt);
+
+    /** @brief Copy operations (deleted)
+     *
+     *  Copying is not allowed because the mapped memory region is owned
+     *  by a single MMapHandler instance to prevent double munmap.
+     */
+    MMapHandler(const MMapHandler&) = delete;
+    MMapHandler& operator=(const MMapHandler&) = delete;
+
+    /** @brief Move operations (deleted)
+     *
+     *  Transfer of ownership is not allowed at the moment
+     *  to keep the implementation simple and avoid complexities around
+     *  ensuring only one instance manages the mapped memory region.
+     */
+    MMapHandler(MMapHandler&& other) = delete;
+    MMapHandler& operator=(MMapHandler&& other) = delete;
+
+    /** @brief Destructor to handle munmap and optionally close file descriptor
+     *
+     *  Unmaps the memory and closes the file descriptor if it was opened
+     *  by MMapHandler (i.e., constructed with file path).
+     */
+    ~MMapHandler();
+
+    /** @brief Get the data pointer and size of the mapped data
+     *
+     *  @return char* - pointer to the mapped data
+     */
+    char* getData();
+
+    /** @brief Get the const data pointer and size of the mapped data
+     *
+     *  @return const char* - const pointer to the mapped data
+     */
+    const char* getData() const;
+
+    /** @brief Get the size of the mapped data
+     *
+     *  @return size_t - size of the mapped data
+     */
+    size_t getSize() const;
+
+    /** @brief Get a read-only view of the mapped data as bytes
+     *
+     *  @return std::span<const uint8_t> - read-only byte view of mapped data
+     *
+     *  @note The caller is responsible for parsing and interpreting the bytes,
+     *        including handling endianness, alignment, and data validation.
+     */
+    std::span<const uint8_t> getBytes() const;
+
+    /** @brief Get a mutable view of the mapped data as bytes
+     *
+     *  @return std::span<uint8_t> - mutable byte view of mapped data
+     *
+     *  @note The caller is responsible for writing valid data,
+     *        including handling endianness, alignment, and data validation.
+     */
+    std::span<uint8_t> getBytes();
+
+    /** @brief Get a read-only view of the mapped data as chars
+     *
+     *  @return std::span<const char> - read-only char view of mapped data
+     *
+     *  @note The caller is responsible for parsing and interpreting the chars.
+     */
+    std::span<const char> getChars() const;
+
+    /** @brief Get a mutable view of the mapped data as chars
+     *
+     *  @return std::span<char> - mutable char view of mapped data
+     *
+     *  @note The caller is responsible for writing valid data.
+     */
+    std::span<char> getChars();
+
+  private:
+    int fd;
+    size_t size;
+    char* data = nullptr;
+    bool ownsFd = false;
+};
+
 } // namespace utils
 } // namespace pldm
diff --git a/fw-update/activation.cpp b/fw-update/activation.cpp
index abafd96..b5790c9 100644
--- a/fw-update/activation.cpp
+++ b/fw-update/activation.cpp
@@ -12,6 +12,10 @@
 {
     if (value == ActivationIntf::Activations::Activating)
     {
+        if (ActivationIntf::activation() != ActivationIntf::Activations::Ready)
+        {
+            return ActivationIntf::activation();
+        }
         deleteImpl.reset();
         updateManager->activatePackage();
     }
@@ -29,7 +33,7 @@
 
 void Delete::delete_()
 {
-    updateManager->clearActivationInfo();
+    updateManager->resetActivationState();
 }
 } // namespace fw_update
 } // namespace pldm
diff --git a/fw-update/activation.hpp b/fw-update/activation.hpp
index f072124..cb56d24 100644
--- a/fw-update/activation.hpp
+++ b/fw-update/activation.hpp
@@ -13,7 +13,7 @@
 namespace fw_update
 {
 
-class UpdateManager;
+class UpdateManagerBase;
 
 using ActivationIntf = sdbusplus::server::object_t<
     sdbusplus::xyz::openbmc_project::Software::server::Activation>;
@@ -57,7 +57,7 @@
      *  @param[in] updateManager - Reference to FW update manager
      */
     Delete(sdbusplus::bus_t& bus, const std::string& objPath,
-           UpdateManager* updateManager) :
+           UpdateManagerBase* updateManager) :
         DeleteIntf(bus, objPath.c_str(), action::emit_interface_added),
         updateManager(updateManager)
     {}
@@ -66,7 +66,7 @@
     void delete_() override;
 
   private:
-    UpdateManager* updateManager;
+    UpdateManagerBase* updateManager;
 };
 
 /** @class Activation
@@ -84,13 +84,16 @@
      *  @param[in] updateManager - Reference to FW update manager
      */
     Activation(sdbusplus::bus_t& bus, std::string objPath,
-               Activations activationStatus, UpdateManager* updateManager) :
+               Activations activationStatus, UpdateManagerBase* updateManager) :
         ActivationIntf(bus, objPath.c_str(),
                        ActivationIntf::action::defer_emit),
         bus(bus), objPath(objPath), updateManager(updateManager)
     {
         activation(activationStatus);
-        deleteImpl = std::make_unique<Delete>(bus, objPath, updateManager);
+        if (!deleteImpl)
+        {
+            deleteImpl = std::make_unique<Delete>(bus, objPath, updateManager);
+        }
         emit_object_added();
     }
 
@@ -122,7 +125,7 @@
   private:
     sdbusplus::bus_t& bus;
     const std::string objPath;
-    UpdateManager* updateManager;
+    UpdateManagerBase* updateManager;
     std::unique_ptr<Delete> deleteImpl;
 };
 
diff --git a/fw-update/aggregate_update_manager.cpp b/fw-update/aggregate_update_manager.cpp
index 3bdaacb..8a2cc39 100644
--- a/fw-update/aggregate_update_manager.cpp
+++ b/fw-update/aggregate_update_manager.cpp
@@ -21,7 +21,8 @@
     {
         response =
             updateManager->handleRequest(eid, command, request, reqMsgLen);
-        if (responseMsg->payload[0] != PLDM_FWUP_COMMAND_NOT_EXPECTED)
+        auto relayedResponseMsg = new (response.data()) pldm_msg;
+        if (relayedResponseMsg->payload[0] != PLDM_FWUP_COMMAND_NOT_EXPECTED)
         {
             return response;
         }
@@ -29,6 +30,24 @@
     return response;
 }
 
+void AggregateUpdateManager::createUpdateManager(
+    const SoftwareIdentifier& softwareIdentifier,
+    const Descriptors& descriptors, const ComponentInfo& componentInfo,
+    const std::string& updateObjPath, const std::string& generatedId)
+{
+    auto eid = softwareIdentifier.first;
+
+    descriptorMap[softwareIdentifier] =
+        std::make_unique<Descriptors>(descriptors);
+    componentInfoMap[softwareIdentifier] =
+        std::make_unique<ComponentInfo>(componentInfo);
+
+    updateManagers[softwareIdentifier] = std::make_unique<ItemUpdateManager>(
+        eid, event, handler, instanceIdDb, updateObjPath, generatedId,
+        *descriptorMap[softwareIdentifier],
+        *componentInfoMap[softwareIdentifier]);
+}
+
 void AggregateUpdateManager::eraseUpdateManager(
     const SoftwareIdentifier& softwareIdentifier)
 {
diff --git a/fw-update/aggregate_update_manager.hpp b/fw-update/aggregate_update_manager.hpp
index eb15dc9..c9acc6c 100644
--- a/fw-update/aggregate_update_manager.hpp
+++ b/fw-update/aggregate_update_manager.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "item_update_manager.hpp"
 #include "update_manager.hpp"
 
 namespace pldm::fw_update
@@ -63,11 +64,12 @@
      * @param[in] componentInfo - The component information associated with the
      * software identifier
      * @param[in] updateObjPath - The D-Bus object path for the update manager
+     * @param[in] generatedId - The software hash identifier
      */
-    void createUpdateManager(const SoftwareIdentifier& softwareIdentifier,
-                             const Descriptors& descriptors,
-                             const ComponentInfo& componentInfo,
-                             const std::string& updateObjPath);
+    void createUpdateManager(
+        const SoftwareIdentifier& softwareIdentifier,
+        const Descriptors& descriptors, const ComponentInfo& componentInfo,
+        const std::string& updateObjPath, const std::string& generatedId);
 
     /**
      * @brief Erase an existing UpdateManager instance associated with a
@@ -99,7 +101,8 @@
     /**
      * @brief Map of UpdateManager instances keyed by software identifier
      */
-    std::map<SoftwareIdentifier, std::unique_ptr<UpdateManager>> updateManagers;
+    std::map<SoftwareIdentifier, std::unique_ptr<ItemUpdateManager>>
+        updateManagers;
 
     /**
      * @brief Map of descriptor maps keyed by software identifier
diff --git a/fw-update/device_updater.cpp b/fw-update/device_updater.cpp
index 7c02190..39fbd2d 100644
--- a/fw-update/device_updater.cpp
+++ b/fw-update/device_updater.cpp
@@ -94,7 +94,7 @@
     mctp_eid_t eid, std::istream& package,
     const FirmwareDeviceIDRecord& fwDeviceIDRecord,
     const ComponentImageInfos& compImageInfos, const ComponentInfo& compInfo,
-    uint32_t maxTransferSize, UpdateManager* updateManager) :
+    uint32_t maxTransferSize, UpdateManagerBase* updateManager) :
     eid(eid), package(package), fwDeviceIDRecord(fwDeviceIDRecord),
     compImageInfos(compImageInfos), compInfo(compInfo),
     maxTransferSize(maxTransferSize), updateManager(updateManager),
diff --git a/fw-update/device_updater.hpp b/fw-update/device_updater.hpp
index a4c2d03..a0621c3 100644
--- a/fw-update/device_updater.hpp
+++ b/fw-update/device_updater.hpp
@@ -21,7 +21,7 @@
  */
 using ComponentUpdateStatusMap = std::map<size_t, bool>;
 
-class UpdateManager;
+class UpdateManagerBase;
 
 /** @class UpdateProgress
  *
@@ -141,7 +141,7 @@
                            const ComponentImageInfos& compImageInfos,
                            const ComponentInfo& compInfo,
                            uint32_t maxTransferSize,
-                           UpdateManager* updateManager);
+                           UpdateManagerBase* updateManager);
 
     /** @brief Get the progress of updating this device as percentage
      *
@@ -309,7 +309,7 @@
     uint32_t maxTransferSize;
 
     /** @brief To update the status of fw update of the FD */
-    UpdateManager* updateManager;
+    UpdateManagerBase* updateManager;
 
     /** @brief Component index is used to track the current component being
      *         updated if multiple components are applicable for the FD.
diff --git a/fw-update/firmware_inventory.cpp b/fw-update/firmware_inventory.cpp
index ffbb4e8..8ac3fb9 100644
--- a/fw-update/firmware_inventory.cpp
+++ b/fw-update/firmware_inventory.cpp
@@ -4,18 +4,22 @@
 {
 
 FirmwareInventory::FirmwareInventory(
-    SoftwareIdentifier /*softwareIdentifier*/, const std::string& softwarePath,
-    const std::string& softwareVersion, const std::string& associatedEndpoint,
-    SoftwareVersionPurpose purpose) :
-    softwarePath(softwarePath),
+    SoftwareIdentifier softwareIdentifier, const std::string& softwarePath,
+    const std::string& generatedId, const std::string& softwareVersion,
+    const std::string& associatedEndpoint, SoftwareVersionPurpose purpose) :
+    softwareIdentifier(softwareIdentifier),
+    softwarePath(std::format("{}_{}", softwarePath, generatedId)),
     association(this->bus, this->softwarePath.c_str()),
     version(this->bus, this->softwarePath.c_str(),
-            SoftwareVersion::action::defer_emit)
+            SoftwareVersion::action::defer_emit),
+    activation(this->bus, this->softwarePath.c_str(),
+               SoftwareActivation::action::defer_emit)
 {
     this->association.associations(
         {{"running", "ran_on", associatedEndpoint.c_str()}});
     this->version.version(softwareVersion.c_str());
     this->version.purpose(purpose);
+    this->activation.activation(SoftwareActivation::Activations::Active);
     this->version.emit_added();
 }
 
diff --git a/fw-update/firmware_inventory.hpp b/fw-update/firmware_inventory.hpp
index 3ef9bfb..ef7b5e3 100644
--- a/fw-update/firmware_inventory.hpp
+++ b/fw-update/firmware_inventory.hpp
@@ -4,14 +4,18 @@
 #include "common/utils.hpp"
 
 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/Software/Activation/server.hpp>
 #include <xyz/openbmc_project/Software/Version/server.hpp>
 
 class FirmwareInventoryTest;
+class FirmwareInventoryTestInstance;
 
 namespace pldm::fw_update
 {
 
 class FirmwareInventory;
+using SoftwareActivation = sdbusplus::server::object_t<
+    sdbusplus::xyz::openbmc_project::Software::server::Activation>;
 using SoftwareVersion = sdbusplus::server::object_t<
     sdbusplus::xyz::openbmc_project::Software::server::Version>;
 using SoftwareAssociationDefinitions = sdbusplus::server::object_t<
@@ -23,12 +27,12 @@
 {
   public:
     friend class ::FirmwareInventoryTest;
+    friend class ::FirmwareInventoryTestInstance;
     FirmwareInventory() = delete;
     FirmwareInventory(const FirmwareInventory&) = delete;
     FirmwareInventory(FirmwareInventory&&) = delete;
     FirmwareInventory& operator=(const FirmwareInventory&) = delete;
     FirmwareInventory& operator=(FirmwareInventory&&) = delete;
-    ~FirmwareInventory() = default;
 
     /**
      * @brief Constructor
@@ -36,6 +40,7 @@
      *                                 component identifier
      * @param[in] softwarePath - D-Bus object path for the firmware inventory
      * entry
+     * @param[in] generatedId - Software hash identifier
      * @param[in] softwareVersion - Active version of the firmware
      * @param[in] associatedEndpoint - D-Bus object path of the endpoint
      * associated with the firmware
@@ -47,8 +52,8 @@
      * future use and currently not used in the implementation.
      */
     explicit FirmwareInventory(
-        SoftwareIdentifier /*softwareIdentifier*/,
-        const std::string& softwarePath, const std::string& softwareVersion,
+        SoftwareIdentifier softwareIdentifier, const std::string& softwarePath,
+        const std::string& generatedId, const std::string& softwareVersion,
         const std::string& associatedEndpoint,
         SoftwareVersionPurpose purpose = SoftwareVersionPurpose::Unknown);
 
@@ -59,6 +64,11 @@
     sdbusplus::bus_t& bus = utils::DBusHandler::getBus();
 
     /**
+     * @brief Software identifier containing EID and component identifier
+     */
+    SoftwareIdentifier softwareIdentifier;
+
+    /**
      * @brief The D-Bus object path for the firmware inventory entry, obtained
      * by
      */
@@ -74,6 +84,12 @@
      * @brief Software version object that represents the firmware version
      */
     SoftwareVersion version;
+
+    /**
+     * @brief Software activation object that represents the activation state
+     *        of the firmware
+     */
+    SoftwareActivation activation;
 };
 
 } // namespace pldm::fw_update
diff --git a/fw-update/firmware_inventory_manager.cpp b/fw-update/firmware_inventory_manager.cpp
index b4274eb..d093d3e 100644
--- a/fw-update/firmware_inventory_manager.cpp
+++ b/fw-update/firmware_inventory_manager.cpp
@@ -20,7 +20,7 @@
 void FirmwareInventoryManager::createFirmwareEntry(
     const SoftwareIdentifier& softwareIdentifier,
     const SoftwareName& softwareName, const std::string& activeVersion,
-    const Descriptors& /*descriptors*/, const ComponentInfo& /*componentInfo*/)
+    const Descriptors& descriptors, const ComponentInfo& componentInfo)
 {
     struct timespec ts;
     clock_gettime(CLOCK_REALTIME, &ts);
@@ -40,14 +40,18 @@
             .value_or(std::filesystem::path{
                 "/xyz/openbmc_project/inventory/system/board/PLDM_Device"});
     const auto boardName = boardPath.filename().string();
-    const auto softwarePath =
-        std::format("{}/{}_{}_{}", SoftwareVersion::namespace_path, boardName,
-                    softwareName, utils::generateSwId());
 
-    softwareMap.insert_or_assign(
-        softwareIdentifier,
-        std::make_unique<FirmwareInventory>(softwareIdentifier, softwarePath,
-                                            activeVersion, boardPath));
+    const auto softwarePath = std::format(
+        "{}/{}_{}", SoftwareVersion::namespace_path, boardName, softwareName);
+    const auto generatedId = std::to_string(utils::generateSwId());
+
+    updateManager.createUpdateManager(softwareIdentifier, descriptors,
+                                      componentInfo, softwarePath, generatedId);
+
+    softwareMap.insert_or_assign(softwareIdentifier,
+                                 std::make_unique<FirmwareInventory>(
+                                     softwareIdentifier, softwarePath,
+                                     generatedId, activeVersion, boardPath));
 }
 
 void FirmwareInventoryManager::deleteFirmwareEntry(const pldm::eid& eid)
diff --git a/fw-update/item_update_manager.cpp b/fw-update/item_update_manager.cpp
new file mode 100644
index 0000000..850b78f
--- /dev/null
+++ b/fw-update/item_update_manager.cpp
@@ -0,0 +1,266 @@
+#include "item_update_manager.hpp"
+
+#include "activation.hpp"
+#include "common/utils.hpp"
+#include "package_parser.hpp"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+#include <span>
+#include <spanstream>
+#include <string>
+#include <system_error>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm::fw_update
+{
+
+bool ItemUpdateManager::processPackage()
+{
+    inProgressActivation = std::make_unique<Activation>(
+        pldm::utils::DBusHandler::getBus(), objPathWithSwId,
+        software::Activation::Activations::NotReady, this);
+
+    if (packageMap->getSize() < sizeof(pldm_package_header_information))
+    {
+        error(
+            "PLDM fw update package length {SIZE} less than the length of the package header information '{PACKAGE_HEADER_INFO_SIZE}'.",
+            "SIZE", packageMap->getSize(), "PACKAGE_HEADER_INFO_SIZE",
+            sizeof(pldm_package_header_information));
+        inProgressActivation->activation(
+            software::Activation::Activations::Invalid);
+        packageMap.reset();
+        return false;
+    }
+
+    auto buffer = std::vector<uint8_t>(packageMap->getBytes().begin(),
+                                       packageMap->getBytes().end());
+    parser = parsePkgHeader(buffer);
+    if (parser == nullptr)
+    {
+        error("Invalid PLDM package header information");
+        inProgressActivation->activation(
+            software::Activation::Activations::Invalid);
+        packageMap.reset();
+        return false;
+    }
+    try
+    {
+        parser->parse(buffer, buffer.size());
+    }
+    catch (const std::exception& e)
+    {
+        error("Invalid PLDM package header, error - {ERROR}", "ERROR", e);
+        inProgressActivation->activation(
+            software::Activation::Activations::Invalid);
+        parser.reset();
+        packageMap.reset();
+        return false;
+    }
+
+    auto deviceIdRecordOffset =
+        associatePkgToDevice(parser->getFwDeviceIDRecords(), descriptors);
+    if (!deviceIdRecordOffset)
+    {
+        error("Failed to associate package to device");
+        inProgressActivation->activation(
+            software::Activation::Activations::Invalid);
+        packageMap.reset();
+        return false;
+    }
+
+    const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords();
+    const auto& compImageInfos = parser->getComponentImageInfos();
+    static constexpr uint32_t MAXIMUM_TRANSFER_SIZE = 4096;
+
+    auto packageSpan = packageMap->getChars();
+    packageDataStream =
+        std::make_unique<std::ispanstream>(packageSpan, std::ios::binary);
+    deviceUpdater = std::make_unique<DeviceUpdater>(
+        eid, *packageDataStream, fwDeviceIDRecords[*deviceIdRecordOffset],
+        compImageInfos, componentInfo, MAXIMUM_TRANSFER_SIZE, this);
+    inProgressActivation->activation(software::Activation::Activations::Ready);
+    activationProgress = std::make_unique<ActivationProgress>(
+        pldm::utils::DBusHandler::getBus(), objPathWithSwId);
+    inProgressActivation->activation(
+        software::Activation::Activations::Activating);
+
+    return true;
+}
+
+std::string ItemUpdateManager::processFd(int fd)
+{
+    objPathWithSwId = std::format("{}_{}", objPath, utils::generateSwId());
+    auto rawDupFd = dup(fd);
+    if (rawDupFd < 0)
+    {
+        error("Failed to duplicate package file descriptor");
+        throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
+    }
+    this->dupFd = std::make_unique<pldm::utils::CustomFD>(rawDupFd);
+    deferHandler = std::make_unique<
+        sdeventplus::source::Defer>(event, [this](sdeventplus::source::
+                                                      EventBase&) {
+        try
+        {
+            packageMap =
+                std::make_unique<pldm::utils::MMapHandler>((*this->dupFd)());
+        }
+        catch (const std::exception& e)
+        {
+            error("Failed to mmap package file, error - {ERROR}", "ERROR", e);
+            updateInProgress = false;
+            this->dupFd.reset();
+            throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
+        }
+        if (!processPackage())
+        {
+            error("Failed to process firmware update package");
+            updateInProgress = false;
+            packageMap.reset();
+            this->dupFd.reset();
+            throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
+        }
+    });
+    return objPathWithSwId;
+}
+
+std::optional<DeviceIDRecordOffset> ItemUpdateManager::associatePkgToDevice(
+    const FirmwareDeviceIDRecords& fwDeviceIDRecords,
+    const Descriptors& descriptors)
+{
+    for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index)
+    {
+        const auto& deviceIDDescriptors =
+            std::get<Descriptors>(fwDeviceIDRecords[index]);
+        if (std::includes(descriptors.begin(), descriptors.end(),
+                          deviceIDDescriptors.begin(),
+                          deviceIDDescriptors.end()))
+        {
+            return index;
+        }
+    }
+    return std::nullopt;
+}
+
+void ItemUpdateManager::updateDeviceCompletion(mctp_eid_t /*eid*/, bool status)
+{
+    activationProgress->progress(100);
+    packageMap.reset();
+    dupFd.reset();
+
+    auto endTime = std::chrono::steady_clock::now();
+    auto dur =
+        std::chrono::duration<double, std::milli>(endTime - startTime).count();
+    info("Firmware update time: {DURATION}ms", "DURATION", dur);
+    activationProgress.reset();
+    inProgressActivation->activation(
+        status ? software::Activation::Activations::Active
+               : software::Activation::Activations::Failed);
+    deviceUpdater.reset();
+    packageDataStream.reset();
+    packageMap.reset();
+    dupFd.reset();
+    updateInProgress = false;
+    return;
+}
+
+Response ItemUpdateManager::handleRequest(mctp_eid_t /*eid*/, uint8_t command,
+                                          const pldm_msg* request,
+                                          size_t reqMsgLen)
+{
+    Response response(sizeof(pldm_msg), 0);
+    if (deviceUpdater)
+    {
+        if (command == PLDM_REQUEST_FIRMWARE_DATA)
+        {
+            return deviceUpdater->requestFwData(request, reqMsgLen);
+        }
+        else if (command == PLDM_TRANSFER_COMPLETE)
+        {
+            return deviceUpdater->transferComplete(request, reqMsgLen);
+        }
+        else if (command == PLDM_VERIFY_COMPLETE)
+        {
+            return deviceUpdater->verifyComplete(request, reqMsgLen);
+        }
+        else if (command == PLDM_APPLY_COMPLETE)
+        {
+            return deviceUpdater->applyComplete(request, reqMsgLen);
+        }
+        else
+        {
+            auto ptr = new (response.data()) pldm_msg;
+            auto rc = encode_cc_only_resp(
+                request->hdr.instance_id, request->hdr.type,
+                request->hdr.command, PLDM_ERROR_INVALID_DATA, ptr);
+            assert(rc == PLDM_SUCCESS);
+        }
+    }
+    else
+    {
+        auto ptr = new (response.data()) pldm_msg;
+        auto rc = encode_cc_only_resp(request->hdr.instance_id,
+                                      request->hdr.type, +request->hdr.command,
+                                      PLDM_FWUP_COMMAND_NOT_EXPECTED, ptr);
+        assert(rc == PLDM_SUCCESS);
+    }
+
+    return response;
+}
+
+void ItemUpdateManager::activatePackage()
+{
+    startTime = std::chrono::steady_clock::now();
+    deviceUpdater->startFwUpdateFlow();
+}
+
+void ItemUpdateManager::resetActivationState()
+{
+    inProgressActivation.reset();
+    activationProgress.reset();
+    dupFd.reset();
+    updateInProgress = false;
+}
+
+void ItemUpdateManager::updateActivationProgress()
+{
+    if (deviceUpdater)
+    {
+        auto progress = deviceUpdater->getProgress();
+        if (progress != lastProgress)
+        {
+            activationProgress->progress(progress);
+            lastProgress = progress;
+        }
+    }
+}
+
+sdbusplus::message::object_path ItemUpdateManager::startUpdate(
+    sdbusplus::message::unix_fd image,
+    ApplyTimeIntf::RequestedApplyTimes /*applyTime*/)
+{
+    if (updateInProgress)
+    {
+        error("Update already in progress");
+        throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
+    }
+    if (image.fd < 0)
+    {
+        error("Invalid package file descriptor");
+        throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
+    }
+    updateInProgress = true;
+
+    return processFd(image.fd);
+}
+
+} // namespace pldm::fw_update
diff --git a/fw-update/item_update_manager.hpp b/fw-update/item_update_manager.hpp
new file mode 100644
index 0000000..04542dd
--- /dev/null
+++ b/fw-update/item_update_manager.hpp
@@ -0,0 +1,186 @@
+#pragma once
+
+#include "update_manager.hpp"
+
+#include <xyz/openbmc_project/Software/ApplyTime/server.hpp>
+#include <xyz/openbmc_project/Software/Update/server.hpp>
+
+#include <span>
+#include <spanstream>
+
+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) :
+        UpdateManagerBase(event, handler, instanceIdDb),
+        ItemUpdateIntf(pldm::utils::DBusHandler::getBus(),
+                       std::format("{}_{}", objPath, generatedId).c_str()),
+        eid(eid), objPath(objPath), descriptors(descriptors),
+        componentInfo(componentInfo)
+    {}
+
+    /**
+     * @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
+     */
+    virtual sdbusplus::message::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);
+
+    std::unique_ptr<Activation> inProgressActivation;
+    std::unique_ptr<ActivationProgress> activationProgress;
+    std::unique_ptr<PackageParser> parser;
+    std::unique_ptr<DeviceUpdater> deviceUpdater;
+    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;
+
+    bool updateInProgress = false;
+
+    /** @brief The last progress that was calculated. Used to avoid spamming
+     * dbus
+     *
+     */
+    uint8_t lastProgress;
+};
+
+} // namespace pldm::fw_update
diff --git a/fw-update/test/firmware_inventory_manager_test.cpp b/fw-update/test/firmware_inventory_manager_test.cpp
index c7effaa..d279b35 100644
--- a/fw-update/test/firmware_inventory_manager_test.cpp
+++ b/fw-update/test/firmware_inventory_manager_test.cpp
@@ -11,7 +11,7 @@
 using namespace pldm::fw_update;
 
 // Helper class for testing: inherits FirmwareInventory and exposes protected
-class FirmwareInventoryTest : public pldm::fw_update::FirmwareInventory
+class FirmwareInventoryTestInstance : public pldm::fw_update::FirmwareInventory
 {
   public:
     using FirmwareInventory::FirmwareInventory;
@@ -95,7 +95,7 @@
         inventoryManager.getSoftwareMap().find(softwareIdentifier);
     ASSERT_NE(inventoryIt, inventoryManager.getSoftwareMap().end());
     const auto* inventory =
-        static_cast<FirmwareInventoryTest*>(inventoryIt->second.get());
+        static_cast<FirmwareInventoryTestInstance*>(inventoryIt->second.get());
     ASSERT_NE(inventory, nullptr);
     EXPECT_NE(inventory->getSoftwarePath().find(
                   "/xyz/openbmc_project/software/PLDM_Device_TestDevice_"),
diff --git a/fw-update/test/firmware_inventory_test.cpp b/fw-update/test/firmware_inventory_test.cpp
index 47d8396..beb17ec 100644
--- a/fw-update/test/firmware_inventory_test.cpp
+++ b/fw-update/test/firmware_inventory_test.cpp
@@ -1,10 +1,13 @@
+#include "fw-update/aggregate_update_manager.hpp"
 #include "fw-update/firmware_inventory.hpp"
+#include "test/test_instance_id.hpp"
 
 #include <string>
 
 #include <gtest/gtest.h>
 
 using namespace pldm::fw_update;
+using namespace std::chrono;
 
 class FirmwareInventoryTest : public FirmwareInventory
 {
@@ -28,19 +31,32 @@
 {
     SoftwareIdentifier softwareIdentifier{1, 100};
     std::string expectedSoftwarePath =
-        "/xyz/openbmc_project/software/PLDM_Device_TestDevice_1234";
+        "/xyz/openbmc_project/software/PLDM_Device_TestDevice";
+    std::string expectedSoftwareHash = "1234";
     std::string expectedSoftwareVersion = "2.3.4";
     std::string expectedEndpointPath =
         "/xyz/openbmc_project/inventory/system/board/PLDM_Device";
     Descriptors firmwareDescriptors;
+    DescriptorMap firmwareDescriptorMap{};
     ComponentInfo firmwareComponentInfo;
+    ComponentInfoMap firmwareComponentInfoMap{};
     SoftwareVersionPurpose expectedPurpose = SoftwareVersionPurpose::Unknown;
 
-    FirmwareInventoryTest inventory(softwareIdentifier, expectedSoftwarePath,
-                                    expectedSoftwareVersion,
-                                    expectedEndpointPath, expectedPurpose);
+    Event event(sdeventplus::Event::get_default());
+    TestInstanceIdDb instanceIdDb;
+    requester::Handler<requester::Request> handler(
+        nullptr, event, instanceIdDb, false, seconds(1), 2, milliseconds(100));
 
-    EXPECT_EQ(inventory.getSoftwarePath(), expectedSoftwarePath);
+    AggregateUpdateManager updateManager(
+        event, handler, instanceIdDb, firmwareDescriptorMap,
+        firmwareComponentInfoMap);
+
+    FirmwareInventoryTest inventory(
+        softwareIdentifier, expectedSoftwarePath, expectedSoftwareHash,
+        expectedSoftwareVersion, expectedEndpointPath, expectedPurpose);
+
+    EXPECT_EQ(inventory.getSoftwarePath(),
+              std::format("{}_{}", expectedSoftwarePath, expectedSoftwareHash));
     auto associationTuples = inventory.getAssociation().associations();
     ASSERT_FALSE(associationTuples.empty());
     EXPECT_EQ(std::get<2>(associationTuples[0]), expectedEndpointPath);
diff --git a/fw-update/test/meson.build b/fw-update/test/meson.build
index fd510c6..92ba092 100644
--- a/fw-update/test/meson.build
+++ b/fw-update/test/meson.build
@@ -1,6 +1,4 @@
-fw_update_test_src = declare_dependency(
-    sources: fw_update_sources,
-)
+fw_update_test_src = declare_dependency(sources: fw_update_sources)
 
 tests = [
     'inventory_manager_test',
diff --git a/fw-update/update.cpp b/fw-update/update.cpp
index 9113056..f113445 100644
--- a/fw-update/update.cpp
+++ b/fw-update/update.cpp
@@ -23,7 +23,7 @@
         }
         else
         {
-            updateManager->clearActivationInfo();
+            updateManager->resetActivationState();
         }
     }
 
diff --git a/fw-update/update_manager.cpp b/fw-update/update_manager.cpp
index 3cf5fde..fae8034 100644
--- a/fw-update/update_manager.cpp
+++ b/fw-update/update_manager.cpp
@@ -55,7 +55,7 @@
         }
         else
         {
-            clearActivationInfo();
+            resetActivationState();
         }
     }
 
@@ -311,7 +311,7 @@
     }
 }
 
-void UpdateManager::clearActivationInfo()
+void UpdateManager::resetActivationState()
 {
     activation.reset();
     activationProgress.reset();
diff --git a/fw-update/update_manager.hpp b/fw-update/update_manager.hpp
index 2b896ab..d6a8924 100644
--- a/fw-update/update_manager.hpp
+++ b/fw-update/update_manager.hpp
@@ -37,7 +37,44 @@
 using DeviceUpdaterInfos = std::vector<DeviceUpdaterInfo>;
 using TotalComponentUpdates = size_t;
 
-class UpdateManager
+/**
+ * @brief The base class of the UpdateManager and the
+ *        ItemBaseUpdateManager
+ */
+class UpdateManagerBase
+{
+  public:
+    virtual ~UpdateManagerBase() = default;
+
+    UpdateManagerBase() = delete;
+    UpdateManagerBase(const UpdateManagerBase&) = delete;
+    UpdateManagerBase(UpdateManagerBase&&) = delete;
+    UpdateManagerBase& operator=(const UpdateManagerBase&) = delete;
+    UpdateManagerBase& operator=(UpdateManagerBase&&) = delete;
+    /** @brief Constructor
+     *
+     *  @param[in] event - PLDM daemon's main event loop
+     *  @param[in] handler - PLDM request handler
+     *  @param[in] instanceIdDb - Managing instance ID for PLDM requests
+     */
+    UpdateManagerBase(
+        Event& event,
+        pldm::requester::Handler<pldm::requester::Request>& handler,
+        InstanceIdDb& instanceIdDb) :
+        event(event), handler(handler), instanceIdDb(instanceIdDb)
+    {}
+
+    virtual void updateDeviceCompletion(mctp_eid_t eid, bool status) = 0;
+    virtual void updateActivationProgress() = 0;
+    virtual void activatePackage() = 0;
+    virtual void resetActivationState() = 0;
+
+    Event& event;               //!< reference to PLDM daemon's main event loop
+    pldm::requester::Handler<pldm::requester::Request>& handler;
+    InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
+};
+
+class UpdateManager : public UpdateManagerBase
 {
   public:
     UpdateManager() = delete;
@@ -52,7 +89,7 @@
         pldm::requester::Handler<pldm::requester::Request>& handler,
         InstanceIdDb& instanceIdDb, const DescriptorMap& descriptorMap,
         const ComponentInfoMap& componentInfoMap) :
-        event(event), handler(handler), instanceIdDb(instanceIdDb),
+        UpdateManagerBase(event, handler, instanceIdDb),
         descriptorMap(descriptorMap), componentInfoMap(componentInfoMap),
 #ifdef FW_UPDATE_INOTIFY_ENABLED
         watch(event.get(),
@@ -102,17 +139,17 @@
     std::string processStreamDefer(std::istream& packageStream,
                                    uintmax_t packageSize);
 
-    void updateDeviceCompletion(mctp_eid_t eid, bool status);
+    void updateDeviceCompletion(mctp_eid_t eid, bool status) override;
 
-    void updateActivationProgress();
+    void updateActivationProgress() override;
 
     /** @brief Callback function that will be invoked when the
      *         RequestedActivation will be set to active in the Activation
      *         interface
      */
-    void activatePackage();
+    void activatePackage() override;
 
-    void clearActivationInfo();
+    void resetActivationState() override;
 
     /** @brief
      *
@@ -129,10 +166,6 @@
     static std::string getSwId();
 
     const std::string swRootPath{"/xyz/openbmc_project/software/"};
-    Event& event; //!< reference to PLDM daemon's main event loop
-    /** @brief PLDM request handler */
-    pldm::requester::Handler<pldm::requester::Request>& handler;
-    InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
 
     std::unique_ptr<Activation> activation;
 
diff --git a/meson.build b/meson.build
index 8e2282f..cd6f800 100644
--- a/meson.build
+++ b/meson.build
@@ -261,6 +261,7 @@
     'fw-update/firmware_inventory.cpp',
     'fw-update/firmware_inventory_manager.cpp',
     'fw-update/inventory_manager.cpp',
+    'fw-update/item_update_manager.cpp',
     'fw-update/package_parser.cpp',
     'fw-update/update.cpp',
     'fw-update/update_manager.cpp',