fw-update: Add StartUpdate D-Bus API support for firmware updates

This commit adds support for the StartUpdate D-Bus API to enable FW
updates through the xyz.openbmc_project.Software.Update interface. The
user would need to enable `fw-update-pkg-inotify` meson option to
switch to inotify mechanism.

The existing inotify mechanism has critical limitations:
- Race condition between a BMC code updater and PLDM updater when
  accessing images from /tmp/images directory
- Lack of a standard D-Bus interface for firmware operations
- No proper error handling or status reporting

This implementation follows the upstream design for device & component
level inventory & update, based on the proposed code-update design
document [1]. The FW Update is triggered at the top-level i.e. all valid
targets will be updated with a single StartUpdate call.

Key changes:
- Add Update class implementing the Software.Update D-Bus interface
- Add startUpdate method that accepts file descriptors and processes
  firmware packages via streams
- Refactor UpdateManager to support both file path and stream-based
  package processing

Tests:
- Successful FW Update with both inotify and startUpdate flows on
  gb200nvl-obmc platform with multiple components (GPU + SMA)
- FW Update failure case by modifying the package size. The Redfish task
  immediately fails with Exception after creation.

[1]: https://gerrit.openbmc.org/c/openbmc/docs/+/76645

Change-Id: Ic2ca74431316161de844f6a3966f612760f5c298
Signed-off-by: P Arun Kumar Reddy <arunpapannagari23@gmail.com>
diff --git a/fw-update/update_manager.hpp b/fw-update/update_manager.hpp
index 012c83e..5aa80ec 100644
--- a/fw-update/update_manager.hpp
+++ b/fw-update/update_manager.hpp
@@ -4,15 +4,23 @@
 #include "common/types.hpp"
 #include "device_updater.hpp"
 #include "fw-update/activation.hpp"
+#include "fw-update/update.hpp"
+#ifdef FW_UPDATE_INOTIFY_ENABLED
+#include "fw-update/watch.hpp"
+#endif
 #include "package_parser.hpp"
 #include "requester/handler.hpp"
-#include "watch.hpp"
 
 #include <libpldm/base.h>
 
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/Software/Activation/server.hpp>
+
 #include <chrono>
 #include <filesystem>
 #include <fstream>
+#include <sstream>
 #include <tuple>
 #include <unordered_map>
 
@@ -48,11 +56,17 @@
         const ComponentInfoMap& componentInfoMap) :
         event(event), handler(handler), instanceIdDb(instanceIdDb),
         descriptorMap(descriptorMap), componentInfoMap(componentInfoMap),
+#ifdef FW_UPDATE_INOTIFY_ENABLED
         watch(event.get(),
               [this](std::string& packageFilePath) {
                   return this->processPackage(
                       std::filesystem::path(packageFilePath));
               }),
+#else
+        updater(std::make_unique<Update>(pldm::utils::DBusHandler::getBus(),
+                                         "/xyz/openbmc_project/software/pldm",
+                                         this)),
+#endif
         totalNumComponentUpdates(0), compUpdateCompletedCount(0)
     {}
 
@@ -71,6 +85,25 @@
 
     int processPackage(const std::filesystem::path& packageFilePath);
 
+    /** @brief Process the firmware update package
+     *
+     *  @param[in] packageStream - Stream of the firmware update package
+     *  @param[in] packageSize - Size of the firmware update package
+     *
+     *  @return Object path of the created Software object
+     */
+    void processStream(std::istream& packageStream, uintmax_t packageSize);
+
+    /** @brief Defers processing the package stream
+     *
+     *  @param[in] packageStream - Stream of the firmware update package
+     *  @param[in] packageSize - Size of the firmware update package
+     *
+     *  @return Object path of the created Software object as a string
+     */
+    std::string processStreamDefer(std::istream& packageStream,
+                                   uintmax_t packageSize);
+
     void updateDeviceCompletion(mctp_eid_t eid, bool status);
 
     void updateActivationProgress();
@@ -91,20 +124,31 @@
         const DescriptorMap& descriptorMap,
         TotalComponentUpdates& totalNumComponentUpdates);
 
+    /** @brief Generate a unique software ID based on current timestamp
+     *
+     *  @return String representation of the current timestamp in seconds
+     */
+    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;
+
   private:
     /** @brief Device identifiers of the managed FDs */
     const DescriptorMap& descriptorMap;
     /** @brief Component information needed for the update of the managed FDs */
     const ComponentInfoMap& componentInfoMap;
+#ifdef FW_UPDATE_INOTIFY_ENABLED
     Watch watch;
+#else
+    std::unique_ptr<Update> updater;
+#endif
 
-    std::unique_ptr<Activation> activation;
     std::unique_ptr<ActivationProgress> activationProgress;
     std::string objPath;
 
@@ -128,6 +172,7 @@
      */
     size_t compUpdateCompletedCount;
     decltype(std::chrono::steady_clock::now()) startTime;
+    std::unique_ptr<sdeventplus::source::Defer> updateDeferHandler;
 };
 
 } // namespace fw_update