#include "update_manager.hpp" #include "activation.hpp" #include "common/utils.hpp" #include "package_parser.hpp" #include #include #include #include #include #include PHOSPHOR_LOG2_USING; 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) { error( "Activation of PLDM fw update package for version '{VERSION}' already in progress.", "VERSION", parser->pkgVersion); std::filesystem::remove(packageFilePath); return -1; } else { clearActivationInfo(); } } package.open(packageFilePath, std::ios::binary | std::ios::in | std::ios::ate); if (!package.good()) { error( "Failed to open the PLDM fw update package file '{FILE}', error - {ERROR}.", "ERROR", errno, "FILE", packageFilePath); package.close(); std::filesystem::remove(packageFilePath); return -1; } uintmax_t packageSize = package.tellg(); if (packageSize < 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", packageSize, "PACKAGE_HEADER_INFO_SIZE", sizeof(pldm_package_header_information)); package.close(); std::filesystem::remove(packageFilePath); return -1; } package.seekg(0); std::vector packageHeader(sizeof(pldm_package_header_information)); package.read(reinterpret_cast(packageHeader.data()), sizeof(pldm_package_header_information)); auto pkgHeaderInfo = reinterpret_cast( 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(packageHeader.data()), pkgHeaderInfoSize); parser = parsePkgHeader(packageHeader); if (parser == nullptr) { error("Invalid PLDM package header information"); package.close(); std::filesystem::remove(packageFilePath); return -1; } // Populate object path with the hash of the package version size_t versionHash = std::hash{}(parser->pkgVersion); objPath = swRootPath + std::to_string(versionHash); package.seekg(0); packageHeader.resize(parser->pkgHeaderSize); package.read(reinterpret_cast(packageHeader.data()), parser->pkgHeaderSize); try { parser->parse(packageHeader, packageSize); } catch (const std::exception& e) { error("Invalid PLDM package header, error - {ERROR}", "ERROR", e); activation = std::make_unique( 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()) { error( "No matching devices found with the PLDM firmware update package"); activation = std::make_unique( 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( deviceUpdaterInfo.first, package, fwDeviceIDRecord, compImageInfos, search->second, MAXIMUM_TRANSFER_SIZE, this)); } fwPackageFilePath = packageFilePath; activation = std::make_unique( pldm::utils::DBusHandler::getBus(), objPath, software::Activation::Activations::Ready, this); activationProgress = std::make_unique( 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(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(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(); auto dur = std::chrono::duration(endTime - startTime) .count(); info("Firmware update time: {DURATION}ms", "DURATION", dur); 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(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(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(std::floor( (100 * compUpdateCompletedCount) / totalNumComponentUpdates)); activationProgress->progress(progressPercent); } } // namespace fw_update } // namespace pldm