blob: e43d09938bac215cbdfb96f68c45c66a1d8adfbf [file] [log] [blame]
#include "update_manager.hpp"
#include "activation.hpp"
#include "common/utils.hpp"
#include "package_parser.hpp"
#include <phosphor-logging/lg2.hpp>
#include <cassert>
#include <cmath>
#include <filesystem>
#include <fstream>
#include <string>
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 already in progress, PACKAGE_VERSION={PKG_VERS}",
"PKG_VERS", 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(
"Opening the PLDM FW update package failed, ERR={ERR}, PACKAGEFILE={PKG_FILE}",
"ERR", unsigned(errno), "PKG_FILE", packageFilePath.c_str());
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 less than the length of the package header information, PACKAGESIZE={PKG_SIZE}",
"PKG_SIZE", packageSize);
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)
{
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<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)
{
error("Invalid PLDM package header");
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())
{
error(
"No matching devices found with the PLDM firmware update package");
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, 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();
auto dur =
std::chrono::duration<double, std::milli>(endTime - startTime)
.count();
error("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<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