fw-update: Implement firmware UpdateManager
The UpdateManager parses the PLDM package and co-ordinates with
the DeviceUpdater to update all the PLDM enabled firmware devices.
Tested: Completed firmware update using PLDM for an FD
Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: Ia87675e0a88cb1f72ad82934e539739db193b9f6
diff --git a/fw-update/update_manager.cpp b/fw-update/update_manager.cpp
new file mode 100644
index 0000000..42e2156
--- /dev/null
+++ b/fw-update/update_manager.cpp
@@ -0,0 +1,296 @@
+#include "update_manager.hpp"
+
+#include "activation.hpp"
+#include "common/utils.hpp"
+#include "package_parser.hpp"
+
+#include <cassert>
+#include <cmath>
+#include <filesystem>
+#include <fstream>
+#include <string>
+
+namespace pldm
+{
+
+namespace fw_update
+{
+
+namespace fs = std::filesystem;
+namespace software = sdbusplus::xyz::openbmc_project::Software::server;
+
+int UpdateManager::processPackage(const std::filesystem::path& packageFilePath)
+{
+ // If no devices discovered, take no action on the package.
+ if (!descriptorMap.size())
+ {
+ return 0;
+ }
+
+ namespace software = sdbusplus::xyz::openbmc_project::Software::server;
+ // If a firmware activation of a package is in progress, don't proceed with
+ // package processing
+ if (activation)
+ {
+
+ if (activation->activation() ==
+ software::Activation::Activations::Activating)
+ {
+ std::cerr
+ << "Activation of PLDM FW update package already in progress"
+ << ", PACKAGE_VERSION=" << parser->pkgVersion << "\n";
+ std::filesystem::remove(packageFilePath);
+ return -1;
+ }
+ else
+ {
+ clearActivationInfo();
+ }
+ }
+
+ package.open(packageFilePath,
+ std::ios::binary | std::ios::in | std::ios::ate);
+ if (!package.good())
+ {
+ std::cerr << "Opening the PLDM FW update package failed, ERR="
+ << unsigned(errno) << ", PACKAGEFILE=" << packageFilePath
+ << "\n";
+ package.close();
+ std::filesystem::remove(packageFilePath);
+ return -1;
+ }
+
+ uintmax_t packageSize = package.tellg();
+ if (packageSize < sizeof(pldm_package_header_information))
+ {
+ std::cerr << "PLDM FW update package length less than the length of "
+ "the package header information, PACKAGESIZE="
+ << packageSize << "\n";
+ package.close();
+ std::filesystem::remove(packageFilePath);
+ return -1;
+ }
+
+ package.seekg(0);
+ std::vector<uint8_t> packageHeader(sizeof(pldm_package_header_information));
+ package.read(reinterpret_cast<char*>(packageHeader.data()),
+ sizeof(pldm_package_header_information));
+
+ auto pkgHeaderInfo =
+ reinterpret_cast<const pldm_package_header_information*>(
+ packageHeader.data());
+ auto pkgHeaderInfoSize = sizeof(pldm_package_header_information) +
+ pkgHeaderInfo->package_version_string_length;
+ packageHeader.clear();
+ packageHeader.resize(pkgHeaderInfoSize);
+ package.seekg(0);
+ package.read(reinterpret_cast<char*>(packageHeader.data()),
+ pkgHeaderInfoSize);
+
+ parser = parsePkgHeader(packageHeader);
+ if (parser == nullptr)
+ {
+ std::cerr << "Invalid PLDM package header information"
+ << "\n";
+ package.close();
+ std::filesystem::remove(packageFilePath);
+ return -1;
+ }
+
+ // Populate object path with the hash of the package version
+ size_t versionHash = std::hash<std::string>{}(parser->pkgVersion);
+ objPath = swRootPath + std::to_string(versionHash);
+
+ package.seekg(0);
+ packageHeader.resize(parser->pkgHeaderSize);
+ package.read(reinterpret_cast<char*>(packageHeader.data()),
+ parser->pkgHeaderSize);
+ try
+ {
+ parser->parse(packageHeader, packageSize);
+ }
+ catch (const std::exception& e)
+ {
+ std::cerr << "Invalid PLDM package header"
+ << "\n";
+ activation = std::make_unique<Activation>(
+ pldm::utils::DBusHandler::getBus(), objPath,
+ software::Activation::Activations::Invalid, this);
+ package.close();
+ parser.reset();
+ return -1;
+ }
+
+ auto deviceUpdaterInfos =
+ associatePkgToDevices(parser->getFwDeviceIDRecords(), descriptorMap,
+ totalNumComponentUpdates);
+ if (!deviceUpdaterInfos.size())
+ {
+ std::cerr
+ << "No matching devices found with the PLDM firmware update package"
+ << "\n";
+ activation = std::make_unique<Activation>(
+ pldm::utils::DBusHandler::getBus(), objPath,
+ software::Activation::Activations::Invalid, this);
+ package.close();
+ parser.reset();
+ return 0;
+ }
+
+ const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords();
+ const auto& compImageInfos = parser->getComponentImageInfos();
+
+ for (const auto& deviceUpdaterInfo : deviceUpdaterInfos)
+ {
+ const auto& fwDeviceIDRecord =
+ fwDeviceIDRecords[deviceUpdaterInfo.second];
+ auto search = componentInfoMap.find(deviceUpdaterInfo.first);
+ deviceUpdaterMap.emplace(deviceUpdaterInfo.first,
+ std::make_unique<DeviceUpdater>(
+ deviceUpdaterInfo.first, event, requester,
+ handler, package, fwDeviceIDRecord,
+ compImageInfos, search->second,
+ MAXIMUM_TRANSFER_SIZE, this));
+ }
+
+ fwPackageFilePath = packageFilePath;
+ activation = std::make_unique<Activation>(
+ pldm::utils::DBusHandler::getBus(), objPath,
+ software::Activation::Activations::Ready, this);
+ activationProgress = std::make_unique<ActivationProgress>(
+ pldm::utils::DBusHandler::getBus(), objPath);
+
+ return 0;
+}
+
+DeviceUpdaterInfos UpdateManager::associatePkgToDevices(
+ const FirmwareDeviceIDRecords& fwDeviceIDRecords,
+ const DescriptorMap& descriptorMap,
+ TotalComponentUpdates& totalNumComponentUpdates)
+{
+ DeviceUpdaterInfos deviceUpdaterInfos;
+ for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index)
+ {
+ const auto& deviceIDDescriptors =
+ std::get<Descriptors>(fwDeviceIDRecords[index]);
+ for (const auto& [eid, descriptors] : descriptorMap)
+ {
+ if (std::includes(descriptors.begin(), descriptors.end(),
+ deviceIDDescriptors.begin(),
+ deviceIDDescriptors.end()))
+ {
+ deviceUpdaterInfos.emplace_back(std::make_pair(eid, index));
+ const auto& applicableComponents =
+ std::get<ApplicableComponents>(fwDeviceIDRecords[index]);
+ totalNumComponentUpdates += applicableComponents.size();
+ }
+ }
+ }
+ return deviceUpdaterInfos;
+}
+
+void UpdateManager::updateDeviceCompletion(mctp_eid_t eid, bool status)
+{
+ deviceUpdateCompletionMap.emplace(eid, status);
+ if (deviceUpdateCompletionMap.size() == deviceUpdaterMap.size())
+ {
+ for (const auto& [eid, status] : deviceUpdateCompletionMap)
+ {
+ if (!status)
+ {
+ activation->activation(
+ software::Activation::Activations::Failed);
+ return;
+ }
+ }
+
+ auto endTime = std::chrono::steady_clock::now();
+ std::cerr << "Firmware update time: "
+ << std::chrono::duration<double, std::milli>(endTime -
+ startTime)
+ .count()
+ << " ms\n";
+ activation->activation(software::Activation::Activations::Active);
+ }
+ return;
+}
+
+Response UpdateManager::handleRequest(mctp_eid_t eid, uint8_t command,
+ const pldm_msg* request, size_t reqMsgLen)
+{
+ Response response(sizeof(pldm_msg), 0);
+ if (deviceUpdaterMap.contains(eid))
+ {
+ auto search = deviceUpdaterMap.find(eid);
+ if (command == PLDM_REQUEST_FIRMWARE_DATA)
+ {
+ return search->second->requestFwData(request, reqMsgLen);
+ }
+ else if (command == PLDM_TRANSFER_COMPLETE)
+ {
+ return search->second->transferComplete(request, reqMsgLen);
+ }
+ else if (command == PLDM_VERIFY_COMPLETE)
+ {
+ return search->second->verifyComplete(request, reqMsgLen);
+ }
+ else if (command == PLDM_APPLY_COMPLETE)
+ {
+ return search->second->applyComplete(request, reqMsgLen);
+ }
+ else
+ {
+ auto ptr = reinterpret_cast<pldm_msg*>(response.data());
+ 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 = reinterpret_cast<pldm_msg*>(response.data());
+ 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 UpdateManager::activatePackage()
+{
+ startTime = std::chrono::steady_clock::now();
+ for (const auto& [eid, deviceUpdaterPtr] : deviceUpdaterMap)
+ {
+ deviceUpdaterPtr->startFwUpdateFlow();
+ }
+}
+
+void UpdateManager::clearActivationInfo()
+{
+ activation.reset();
+ activationProgress.reset();
+ objPath.clear();
+
+ deviceUpdaterMap.clear();
+ deviceUpdateCompletionMap.clear();
+ parser.reset();
+ package.close();
+ std::filesystem::remove(fwPackageFilePath);
+ totalNumComponentUpdates = 0;
+ compUpdateCompletedCount = 0;
+}
+
+void UpdateManager::updateActivationProgress()
+{
+ compUpdateCompletedCount++;
+ auto progressPercent = static_cast<uint8_t>(std::floor(
+ (100 * compUpdateCompletedCount) / totalNumComponentUpdates));
+ activationProgress->progress(progressPercent);
+}
+
+} // namespace fw_update
+
+} // namespace pldm
\ No newline at end of file