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