pldmd: Wire fw-update-legacy into pldmd The fw-update-legacy module provides the firmware update functionality ported from the legacy pldmd-google daemon. This change wires the legacy firmware manager into the pldmd-google-dev-common main execution path. The manager now inherits from MctpDiscoveryHandlerIntf and is registered with the MctpDiscovery handler. Relevant PLDM_FWUP messages are routed to it in the processRxMsg function, restoring the firmware update features. Upstream firmware update implementation requires a newer dbus API version. Updating gBMC to use the newer version is a lot much bigger effort as it is one of the fundamental dependencies influencing the whole system, so the quickest way to achieve feature parity between pldmd-google and pldmd-google-dev-common was doing this instead. Tested: Compiled and verified on platform31. The pldmd daemon successfully parses the endpoints and initiates GetFirmwareParameters requests, closely matching the behavior from the legacy pldmd-google logs. No firmware update was attempted just yet. Change-Id: I82044602e9519207fa7e0d8f41947b676bd4f7ab Google-Bug-Id: 458166668 Signed-off-by: Luka Strizic <lstrz@google.com>
diff --git a/fw-update-legacy/activation.hpp b/fw-update-legacy/activation.hpp new file mode 100644 index 0000000..3bda162 --- /dev/null +++ b/fw-update-legacy/activation.hpp
@@ -0,0 +1,151 @@ +#pragma once + +#include "fw-update-legacy/update_manager.hpp" + +#include <sdbusplus/bus.hpp> +#include <xyz/openbmc_project/Object/Delete/server.hpp> +#include <xyz/openbmc_project/Software/Activation/server.hpp> +#include <xyz/openbmc_project/Software/ActivationProgress/server.hpp> + +#include <string> + +namespace pldm +{ + +namespace fw_update +{ + +using ActivationIntf = sdbusplus::server::object_t< + sdbusplus::xyz::openbmc_project::Software::server::Activation>; +using ActivationProgressIntf = sdbusplus::server::object_t< + sdbusplus::xyz::openbmc_project::Software::server::ActivationProgress>; +using DeleteIntf = sdbusplus::server::object_t< + sdbusplus::xyz::openbmc_project::Object::server::Delete>; + +/** @class ActivationProgress + * + * Concrete implementation of xyz.openbmc_project.Software.ActivationProgress + * D-Bus interface + */ +class ActivationProgress : public ActivationProgressIntf +{ + public: + /** @brief Constructor + * + * @param[in] bus - Bus to attach to + * @param[in] objPath - D-Bus object path + */ + ActivationProgress(sdbusplus::bus_t& bus, const std::string& objPath) : + ActivationProgressIntf(bus, objPath.c_str(), + action::emit_interface_added) + { + progress(0); + } +}; + +/** @class Delete + * + * Concrete implementation of xyz.openbmc_project.Object.Delete D-Bus interface + */ +class Delete : public DeleteIntf +{ + public: + /** @brief Constructor + * + * @param[in] bus - Bus to attach to + * @param[in] objPath - D-Bus object path + * @param[in] updateManager - Reference to FW update manager + */ + Delete(sdbusplus::bus_t& bus, const std::string& objPath, + UpdateManager* updateManager) : + DeleteIntf(bus, objPath.c_str(), action::emit_interface_added), + updateManager(updateManager) + {} + + /** @brief Delete the Activation D-Bus object for the FW update package */ + void delete_() override + { + updateManager->clearActivationInfo(); + } + + private: + UpdateManager* updateManager; +}; + +/** @class Activation + * + * Concrete implementation of xyz.openbmc_project.Object.Activation D-Bus + * interface + */ +class Activation : public ActivationIntf +{ + public: + /** @brief Constructor + * + * @param[in] bus - Bus to attach to + * @param[in] objPath - D-Bus object path + * @param[in] updateManager - Reference to FW update manager + */ + Activation(sdbusplus::bus_t& bus, std::string objPath, + Activations activationStatus, UpdateManager* updateManager) : + ActivationIntf(bus, objPath.c_str(), + ActivationIntf::action::defer_emit), + bus(bus), objPath(objPath), updateManager(updateManager) + { + activation(activationStatus); + deleteImpl = std::make_unique<Delete>(bus, objPath, updateManager); + emit_object_added(); + } + + using sdbusplus::xyz::openbmc_project::Software::server::Activation:: + activation; + using sdbusplus::xyz::openbmc_project::Software::server::Activation:: + requestedActivation; + + /** @brief Overriding Activation property setter function + */ + Activations activation(Activations value) override + { + if (value == Activations::Activating) + { + deleteImpl.reset(); + updateManager->activatePackage(); + } + else if (value == Activations::Active || value == Activations::Failed) + { + if (!deleteImpl) + { + deleteImpl = + std::make_unique<Delete>(bus, objPath, updateManager); + } + } + + return ActivationIntf::activation(value); + } + + /** @brief Overriding RequestedActivations property setter function + */ + RequestedActivations requestedActivation( + RequestedActivations value) override + { + if ((value == RequestedActivations::Active) && + (requestedActivation() != RequestedActivations::Active)) + { + if ((ActivationIntf::activation() == Activations::Ready)) + { + activation(Activations::Activating); + } + } + return ActivationIntf::requestedActivation(value); + } + + private: + sdbusplus::bus_t& bus; + const std::string objPath; + UpdateManager* updateManager; + std::unique_ptr<Delete> deleteImpl; +}; + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/device_updater.cpp b/fw-update-legacy/device_updater.cpp new file mode 100644 index 0000000..5c6ff0f --- /dev/null +++ b/fw-update-legacy/device_updater.cpp
@@ -0,0 +1,785 @@ +#include "device_updater.hpp" + +#include "activation.hpp" +#include "update_manager.hpp" + +#include <libpldm/firmware_update.h> + +#include <phosphor-logging/lg2.hpp> + +#include <functional> + +PHOSPHOR_LOG2_USING; + +namespace pldm +{ + +namespace fw_update +{ + +void DeviceUpdater::startFwUpdateFlow() +{ + auto instanceIdResult = updateManager->instanceIdDb.next(eid); + if (!instanceIdResult) + { + error("Instance ID allocation failed for EID {EID}", "EID", + unsigned(eid)); + throw pldm::InstanceIdError(instanceIdResult.error()); + } + auto instanceId = instanceIdResult.value(); + // NumberOfComponents + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + // PackageDataLength + const auto& fwDevicePkgData = + std::get<FirmwareDevicePackageData>(fwDeviceIDRecord); + // ComponentImageSetVersionString + const auto& compImageSetVersion = + std::get<ComponentImageSetVersion>(fwDeviceIDRecord); + variable_field compImgSetVerStrInfo{}; + compImgSetVerStrInfo.ptr = + reinterpret_cast<const uint8_t*>(compImageSetVersion.data()); + compImgSetVerStrInfo.length = + static_cast<uint8_t>(compImageSetVersion.size()); + + Request request( + sizeof(pldm_msg_hdr) + sizeof(struct pldm_request_update_req) + + compImgSetVerStrInfo.length); + auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); + + auto rc = encode_request_update_req( + instanceId, maxTransferSize, applicableComponents.size(), + PLDM_FWUP_MIN_OUTSTANDING_REQ, fwDevicePkgData.size(), + PLDM_STR_TYPE_ASCII, compImgSetVerStrInfo.length, &compImgSetVerStrInfo, + requestMsg, + sizeof(struct pldm_request_update_req) + compImgSetVerStrInfo.length); + if (rc) + { + updateManager->instanceIdDb.free(eid, instanceId); + error("encode_request_update_req failed, EID = {EID}, RC = {RC}", "EID", + unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + rc = updateManager->handler.registerRequest( + eid, instanceId, PLDM_FWUP, PLDM_REQUEST_UPDATE, std::move(request), + std::move(std::bind_front(&DeviceUpdater::requestUpdate, this))); + if (rc) + { + error("Failed to send RequestUpdate request, EID = {EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + else + { + info("EID {EID}: Sent RequestUpdate to EID {EID}.", "EID", + unsigned(eid)); + } +} + +void DeviceUpdater::requestUpdate(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen) +{ + if (response == nullptr || !respMsgLen) + { + error("No response received for RequestUpdate, EID = {EID}", "EID", + unsigned(eid)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + uint8_t completionCode = 0; + uint16_t fdMetaDataLen = 0; + uint8_t fdWillSendPkgData = 0; + + auto rc = decode_request_update_resp(response, respMsgLen, &completionCode, + &fdMetaDataLen, &fdWillSendPkgData); + if (rc) + { + error("Decoding RequestUpdate response failed, EID = {EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + if (completionCode) + { + error( + "RequestUpdate response failed with error completion code, EID = {EID}, CC = {CC}", + "EID", unsigned(eid), "CC", unsigned(completionCode)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + else + { + info("EID {EID}: RequestUpdate succeeded.", "EID", unsigned(eid)); + } + + // Optional fields DeviceMetaData and GetPackageData not handled + pldmRequest = std::make_unique<sdeventplus::source::Defer>( + updateManager->event, + std::bind(&DeviceUpdater::sendPassCompTableRequest, this, + componentIndex)); +} + +void DeviceUpdater::sendPassCompTableRequest(size_t offset) +{ + pldmRequest.reset(); + + auto instanceIdResult = updateManager->instanceIdDb.next(eid); + if (!instanceIdResult) + { + error("Instance ID allocation failed for EID {EID}", "EID", + unsigned(eid)); + throw pldm::InstanceIdError(instanceIdResult.error()); + } + auto instanceId = instanceIdResult.value(); + // TransferFlag + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + uint8_t transferFlag = 0; + if (applicableComponents.size() == 1) + { + transferFlag = PLDM_START_AND_END; + } + else if (offset == 0) + { + transferFlag = PLDM_START; + } + else if (offset == applicableComponents.size() - 1) + { + transferFlag = PLDM_END; + } + else + { + transferFlag = PLDM_MIDDLE; + } + const auto& comp = compImageInfos[applicableComponents[offset]]; + // ComponentClassification + CompClassification compClassification = std::get<static_cast<size_t>( + ComponentImageInfoPos::CompClassificationPos)>(comp); + // ComponentIdentifier + CompIdentifier compIdentifier = + std::get<static_cast<size_t>(ComponentImageInfoPos::CompIdentifierPos)>( + comp); + // ComponentClassificationIndex + CompClassificationIndex compClassificationIndex{}; + auto compKey = std::make_pair(compClassification, compIdentifier); + if (compInfo.contains(compKey)) + { + auto search = compInfo.find(compKey); + compClassificationIndex = search->second; + } + else + { + error( + "Component not found in Component Info, EID = {EID}, COMP_CLASS = {COMP_CLS}, COMP_ID = {COMP_ID}", + "EID", unsigned(eid), "COMP_CLS", compClassification, "COMP_ID", + compIdentifier); + updateManager->updateDeviceCompletion(eid, false); + return; + } + // ComponentComparisonStamp + CompComparisonStamp compComparisonStamp = std::get<static_cast<size_t>( + ComponentImageInfoPos::CompComparisonStampPos)>(comp); + // ComponentVersionString + const auto& compVersion = + std::get<static_cast<size_t>(ComponentImageInfoPos::CompVersionPos)>( + comp); + variable_field compVerStrInfo{}; + compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVersion.data()); + compVerStrInfo.length = static_cast<uint8_t>(compVersion.size()); + + Request request( + sizeof(pldm_msg_hdr) + sizeof(struct pldm_pass_component_table_req) + + compVerStrInfo.length); + auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); + auto rc = encode_pass_component_table_req( + instanceId, transferFlag, compClassification, compIdentifier, + compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII, + compVerStrInfo.length, &compVerStrInfo, requestMsg, + sizeof(pldm_pass_component_table_req) + compVerStrInfo.length); + if (rc) + { + updateManager->instanceIdDb.free(eid, instanceId); + error("encode_pass_component_table_req failed, EID = {EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + rc = updateManager->handler.registerRequest( + eid, instanceId, PLDM_FWUP, PLDM_PASS_COMPONENT_TABLE, + std::move(request), + std::move(std::bind_front(&DeviceUpdater::passCompTable, this))); + if (rc) + { + error( + "Failed to send PassComponentTable request, EID = {EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + else + { + info("EID {EID}: Sent PassComponentTable to EID {EID}.", "EID", + unsigned(eid)); + } +} + +void DeviceUpdater::passCompTable(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen) +{ + if (response == nullptr || !respMsgLen) + { + error("No response received for PassComponentTable, EID = {EID}", "EID", + unsigned(eid)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + uint8_t completionCode = 0; + uint8_t compResponse = 0; + uint8_t compResponseCode = 0; + + auto rc = + decode_pass_component_table_resp(response, respMsgLen, &completionCode, + &compResponse, &compResponseCode); + if (rc) + { + error( + "Decoding PassComponentTable response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + if (completionCode) + { + error( + "PassComponentTable response failed with error completion code, EID = {EID}, CC = {CC}", + "EID", unsigned(eid), "CC", unsigned(completionCode)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + else + { + info("EID {EID}: PassComponentTable succeeded.", "EID", unsigned(eid)); + } + // Handle ComponentResponseCode + + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + if (componentIndex == applicableComponents.size() - 1) + { + componentIndex = 0; + pldmRequest = std::make_unique<sdeventplus::source::Defer>( + updateManager->event, + std::bind(&DeviceUpdater::sendUpdateComponentRequest, this, + componentIndex)); + } + else + { + componentIndex++; + pldmRequest = std::make_unique<sdeventplus::source::Defer>( + updateManager->event, + std::bind(&DeviceUpdater::sendPassCompTableRequest, this, + componentIndex)); + } +} + +void DeviceUpdater::sendUpdateComponentRequest(size_t offset) +{ + pldmRequest.reset(); + + auto instanceIdResult = updateManager->instanceIdDb.next(eid); + if (!instanceIdResult) + { + error("Instance ID allocation failed for EID {EID}", "EID", + unsigned(eid)); + throw pldm::InstanceIdError(instanceIdResult.error()); + } + auto instanceId = instanceIdResult.value(); + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + const auto& comp = compImageInfos[applicableComponents[offset]]; + // ComponentClassification + CompClassification compClassification = std::get<static_cast<size_t>( + ComponentImageInfoPos::CompClassificationPos)>(comp); + // ComponentIdentifier + CompIdentifier compIdentifier = + std::get<static_cast<size_t>(ComponentImageInfoPos::CompIdentifierPos)>( + comp); + // ComponentClassificationIndex + CompClassificationIndex compClassificationIndex{}; + auto compKey = std::make_pair(compClassification, compIdentifier); + if (compInfo.contains(compKey)) + { + auto search = compInfo.find(compKey); + compClassificationIndex = search->second; + } + else + { + error( + "Component not found in Component Info, EID = {EID}, COMP_CLASS = {COMP_CLS}, COMP_ID = {COMP_ID}", + "EID", unsigned(eid), "COMP_CLS", compClassification, "COMP_ID", + compIdentifier); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + // UpdateOptionFlags + bitfield32_t updateOptionFlags; + updateOptionFlags.bits.bit0 = std::get<3>(comp)[0]; + // ComponentVersion + const auto& compVersion = std::get<7>(comp); + variable_field compVerStrInfo{}; + compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVersion.data()); + compVerStrInfo.length = static_cast<uint8_t>(compVersion.size()); + + Request request( + sizeof(pldm_msg_hdr) + sizeof(struct pldm_update_component_req) + + compVerStrInfo.length); + auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); + + auto rc = encode_update_component_req( + instanceId, compClassification, compIdentifier, compClassificationIndex, + std::get<static_cast<size_t>( + ComponentImageInfoPos::CompComparisonStampPos)>(comp), + std::get<static_cast<size_t>(ComponentImageInfoPos::CompSizePos)>(comp), + updateOptionFlags, PLDM_STR_TYPE_ASCII, compVerStrInfo.length, + &compVerStrInfo, requestMsg, + sizeof(pldm_update_component_req) + compVerStrInfo.length); + if (rc) + { + updateManager->instanceIdDb.free(eid, instanceId); + error("encode_update_component_req failed, EID={EID}, RC = {RC}", "EID", + unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + rc = updateManager->handler.registerRequest( + eid, instanceId, PLDM_FWUP, PLDM_UPDATE_COMPONENT, std::move(request), + std::move(std::bind_front(&DeviceUpdater::updateComponent, this))); + if (rc) + { + error("Failed to send UpdateComponent request, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + else + { + info("EID {EID}: Sent UpdateComponent to EID {EID}.", "EID", + unsigned(eid)); + } +} + +void DeviceUpdater::updateComponent(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen) +{ + if (response == nullptr || !respMsgLen) + { + error("No response received for updateComponent, EID={EID}", "EID", + unsigned(eid)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + uint8_t completionCode = 0; + uint8_t compCompatibilityResp = 0; + uint8_t compCompatibilityRespCode = 0; + bitfield32_t updateOptionFlagsEnabled{}; + uint16_t timeBeforeReqFWData = 0; + + auto rc = decode_update_component_resp( + response, respMsgLen, &completionCode, &compCompatibilityResp, + &compCompatibilityRespCode, &updateOptionFlagsEnabled, + &timeBeforeReqFWData); + if (rc) + { + error("Decoding UpdateComponent response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + if (completionCode) + { + error( + "UpdateComponent response failed with error completion code, EID = {EID}, CC = {CC}", + "EID", unsigned(eid), "CC", unsigned(completionCode)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + info( + "EID {EID}: Got successful response for UpdateComponent, device will start to RequestFirmwareData...", + "EID", unsigned(eid)); +} + +Response DeviceUpdater::requestFwData(const pldm_msg* request, + size_t payloadLength) +{ + uint8_t completionCode = PLDM_SUCCESS; + uint32_t offset = 0; + uint32_t length = 0; + Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0); + auto responseMsg = reinterpret_cast<pldm_msg*>(response.data()); + auto rc = decode_request_firmware_data_req(request, payloadLength, &offset, + &length); + if (rc) + { + error( + "Decoding RequestFirmwareData request failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + rc = encode_request_firmware_data_resp( + request->hdr.instance_id, PLDM_ERROR_INVALID_DATA, responseMsg, + sizeof(completionCode)); + if (rc) + { + error( + "Encoding RequestFirmwareData response failed, EID = {EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + return response; + } + + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + const auto& comp = compImageInfos[applicableComponents[componentIndex]]; + auto compOffset = std::get<5>(comp); + auto compSize = std::get<6>(comp); + + // Print out progress around 10 times to prevent log spam + uint32_t chunk = (compSize >= 10) ? compSize / 10 : 1; + uint32_t boundary = offset / chunk * chunk; + if ((offset < boundary + length) && (offset >= boundary)) + { + info( + "EID {EID}: offset = {OFFSET}, length = {LEN}, boundary = {BOUNDARY}", + "EID", unsigned(eid), "OFFSET", unsigned(offset), "LEN", + unsigned(length), "BOUNDARY", boundary); + } + + if (length < PLDM_FWUP_BASELINE_TRANSFER_SIZE || length > maxTransferSize) + { + rc = encode_request_firmware_data_resp( + request->hdr.instance_id, PLDM_FWUP_INVALID_TRANSFER_LENGTH, + responseMsg, sizeof(completionCode)); + if (rc) + { + error( + "Encoding RequestFirmwareData response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + return response; + } + + if (offset + length > compSize + PLDM_FWUP_BASELINE_TRANSFER_SIZE) + { + rc = encode_request_firmware_data_resp( + request->hdr.instance_id, PLDM_FWUP_DATA_OUT_OF_RANGE, responseMsg, + sizeof(completionCode)); + if (rc) + { + error( + "Encoding RequestFirmwareData response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + return response; + } + + size_t padBytes = 0; + if (offset + length > compSize) + { + padBytes = offset + length - compSize; + } + + response.resize(sizeof(pldm_msg_hdr) + sizeof(completionCode) + length); + responseMsg = reinterpret_cast<pldm_msg*>(response.data()); + package.seekg(compOffset + offset); + package.read( + reinterpret_cast<char*>( + response.data() + sizeof(pldm_msg_hdr) + sizeof(completionCode)), + length - padBytes); + rc = encode_request_firmware_data_resp( + request->hdr.instance_id, completionCode, responseMsg, + sizeof(completionCode)); + if (rc) + { + error( + "Encoding RequestFirmwareData response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return response; + } + + return response; +} + +Response DeviceUpdater::transferComplete(const pldm_msg* request, + size_t payloadLength) +{ + uint8_t completionCode = PLDM_SUCCESS; + Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0); + auto responseMsg = reinterpret_cast<pldm_msg*>(response.data()); + + uint8_t transferResult = 0; + auto rc = + decode_transfer_complete_req(request, payloadLength, &transferResult); + if (rc) + { + error("Decoding TransferComplete request failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + rc = encode_transfer_complete_resp(request->hdr.instance_id, + PLDM_ERROR_INVALID_DATA, responseMsg, + sizeof(completionCode)); + if (rc) + { + error( + "Encoding TransferComplete response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + return response; + } + + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + const auto& comp = compImageInfos[applicableComponents[componentIndex]]; + const auto& compVersion = std::get<7>(comp); + + if (transferResult == PLDM_FWUP_TRANSFER_SUCCESS) + { + info( + "Component Transfer complete, EID = {EID}, COMPONENT_VERSION = {COMP_VERS}", + "EID", unsigned(eid), "COMP_VERS", compVersion); + } + else + { + error( + "Transfer of the component failed, EID={EID}, COMPONENT_VERSION = {COMP_VERS}, TRANSFER_RESULT = {TRANS_RES}", + "EID", unsigned(eid), "COMP_VERS", compVersion, "TRANS_RES", + unsigned(transferResult)); + } + + rc = encode_transfer_complete_resp(request->hdr.instance_id, completionCode, + responseMsg, sizeof(completionCode)); + if (rc) + { + error("Encoding TransferComplete response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return response; + } + + return response; +} + +Response DeviceUpdater::verifyComplete(const pldm_msg* request, + size_t payloadLength) +{ + uint8_t completionCode = PLDM_SUCCESS; + Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0); + auto responseMsg = reinterpret_cast<pldm_msg*>(response.data()); + + uint8_t verifyResult = 0; + auto rc = decode_verify_complete_req(request, payloadLength, &verifyResult); + if (rc) + { + error("Decoding VerifyComplete request failed, EID = {EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + rc = encode_verify_complete_resp(request->hdr.instance_id, + PLDM_ERROR_INVALID_DATA, responseMsg, + sizeof(completionCode)); + if (rc) + { + error( + "Encoding VerifyComplete response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + return response; + } + + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + const auto& comp = compImageInfos[applicableComponents[componentIndex]]; + const auto& compVersion = std::get<7>(comp); + + if (verifyResult == PLDM_FWUP_VERIFY_SUCCESS) + { + info( + "Component verification complete, EID={EID}, COMPONENT_VERSION={COMP_VERS}", + "EID", unsigned(eid), "COMP_VERS", compVersion); + } + else + { + error( + "Component verification failed, EID={EID}, COMPONENT_VERSION={COMP_VERS}, VERIFY_RESULT={VERIFY_RES}", + "EID", unsigned(eid), "COMP_VERS", compVersion, "VERIFY_RES", + unsigned(verifyResult)); + } + + rc = encode_verify_complete_resp(request->hdr.instance_id, completionCode, + responseMsg, sizeof(completionCode)); + if (rc) + { + error("Encoding VerifyComplete response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return response; + } + + return response; +} + +Response DeviceUpdater::applyComplete(const pldm_msg* request, + size_t payloadLength) +{ + uint8_t completionCode = PLDM_SUCCESS; + Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0); + auto responseMsg = reinterpret_cast<pldm_msg*>(response.data()); + + uint8_t applyResult = 0; + bitfield16_t compActivationModification{}; + auto rc = decode_apply_complete_req(request, payloadLength, &applyResult, + &compActivationModification); + if (rc) + { + error("Decoding ApplyComplete request failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + rc = encode_apply_complete_resp(request->hdr.instance_id, + PLDM_ERROR_INVALID_DATA, responseMsg, + sizeof(completionCode)); + if (rc) + { + error( + "Encoding ApplyComplete response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + return response; + } + + const auto& applicableComponents = + std::get<ApplicableComponents>(fwDeviceIDRecord); + const auto& comp = compImageInfos[applicableComponents[componentIndex]]; + const auto& compVersion = std::get<7>(comp); + + if (applyResult == PLDM_FWUP_APPLY_SUCCESS || + applyResult == PLDM_FWUP_APPLY_SUCCESS_WITH_ACTIVATION_METHOD) + { + info( + "Component apply complete, EID = {EID}, COMPONENT_VERSION = {COMP_VERS}", + "EID", unsigned(eid), "COMP_VERS", compVersion); + updateManager->updateActivationProgress(); + } + else + { + error( + "Component apply failed, EID = {EID}, COMPONENT_VERSION = {COMP_VERS}, APPLY_RESULT = {APPLY_RES}", + "EID", unsigned(eid), "COMP_VERS", compVersion, "APPLY_RES", + unsigned(applyResult)); + } + + rc = encode_apply_complete_resp(request->hdr.instance_id, completionCode, + responseMsg, sizeof(completionCode)); + if (rc) + { + error("Encoding ApplyComplete response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return response; + } + + if (componentIndex == applicableComponents.size() - 1) + { + componentIndex = 0; + pldmRequest = std::make_unique<sdeventplus::source::Defer>( + updateManager->event, + std::bind(&DeviceUpdater::sendActivateFirmwareRequest, this)); + } + else + { + componentIndex++; + pldmRequest = std::make_unique<sdeventplus::source::Defer>( + updateManager->event, + std::bind(&DeviceUpdater::sendUpdateComponentRequest, this, + componentIndex)); + } + + return response; +} + +void DeviceUpdater::sendActivateFirmwareRequest() +{ + pldmRequest.reset(); + auto instanceIdResult = updateManager->instanceIdDb.next(eid); + if (!instanceIdResult) + { + error("Instance ID allocation failed for EID {EID}", "EID", + unsigned(eid)); + throw pldm::InstanceIdError(instanceIdResult.error()); + } + auto instanceId = instanceIdResult.value(); + Request request( + sizeof(pldm_msg_hdr) + sizeof(struct pldm_activate_firmware_req)); + auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); + + auto rc = encode_activate_firmware_req( + instanceId, PLDM_NOT_ACTIVATE_SELF_CONTAINED_COMPONENTS, requestMsg, + sizeof(pldm_activate_firmware_req)); + if (rc) + { + updateManager->instanceIdDb.free(eid, instanceId); + error("encode_activate_firmware_req failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + rc = updateManager->handler.registerRequest( + eid, instanceId, PLDM_FWUP, PLDM_ACTIVATE_FIRMWARE, std::move(request), + std::move(std::bind_front(&DeviceUpdater::activateFirmware, this))); + if (rc) + { + error("Failed to send ActivateFirmware request, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } +} + +void DeviceUpdater::activateFirmware(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen) +{ + if (response == nullptr || !respMsgLen) + { + error("No response received for ActivateFirmware, EID={EID}", "EID", + unsigned(eid)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + uint8_t completionCode = 0; + uint16_t estimatedTimeForActivation = 0; + + auto rc = decode_activate_firmware_resp( + response, respMsgLen, &completionCode, &estimatedTimeForActivation); + if (rc) + { + error("Decoding ActivateFirmware response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + updateManager->updateDeviceCompletion(eid, false); + return; + } + if (completionCode) + { + error( + "ActivateFirmware response failed with error completion code, EID = {EID}, CC = {CC}", + "EID", unsigned(eid), "CC", unsigned(completionCode)); + updateManager->updateDeviceCompletion(eid, false); + return; + } + + updateManager->updateDeviceCompletion(eid, true); +} + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/device_updater.hpp b/fw-update-legacy/device_updater.hpp new file mode 100644 index 0000000..4613a7c --- /dev/null +++ b/fw-update-legacy/device_updater.hpp
@@ -0,0 +1,214 @@ +#pragma once + +#include "common/types.hpp" +#include "requester/handler.hpp" +#include "requester/request.hpp" + +#include <sdeventplus/event.hpp> +#include <sdeventplus/source/event.hpp> + +#include <fstream> + +namespace pldm +{ + +namespace fw_update +{ + +class UpdateManager; + +/** @class DeviceUpdater + * + * DeviceUpdater orchestrates the firmware update of the firmware device and + * updates the UpdateManager about the status once it is complete. + */ +class DeviceUpdater +{ + public: + DeviceUpdater() = delete; + DeviceUpdater(const DeviceUpdater&) = delete; + DeviceUpdater(DeviceUpdater&&) = default; + DeviceUpdater& operator=(const DeviceUpdater&) = delete; + DeviceUpdater& operator=(DeviceUpdater&&) = delete; + ~DeviceUpdater() = default; + + /** @brief Constructor + * + * @param[in] eid - Endpoint ID of the firmware device + * @param[in] package - File stream for firmware update package + * @param[in] fwDeviceIDRecord - FirmwareDeviceIDRecord in the fw update + * package that matches this firmware device + * @param[in] compImageInfos - Component image information for all the + * components in the fw update package + * @param[in] compInfo - Component info for the components in this FD + * derived from GetFirmwareParameters response + * @param[in] maxTransferSize - Maximum size in bytes of the variable + * payload allowed to be requested by the FD + * @param[in] updateManager - To update the status of fw update of the + * device + */ + explicit DeviceUpdater(mctp_eid_t eid, std::ifstream& package, + const FirmwareDeviceIDRecord& fwDeviceIDRecord, + const ComponentImageInfos& compImageInfos, + const ComponentInfo& compInfo, + uint32_t maxTransferSize, + UpdateManager* updateManager) : + eid(eid), package(package), fwDeviceIDRecord(fwDeviceIDRecord), + compImageInfos(compImageInfos), compInfo(compInfo), + maxTransferSize(maxTransferSize), updateManager(updateManager) + {} + + /** @brief Start the firmware update flow for the FD + * + * To start the update flow RequestUpdate command is sent to the FD. + * + */ + void startFwUpdateFlow(); + + /** @brief Handler for RequestUpdate command response + * + * The response of the RequestUpdate is processed and if the response + * is success, send PassComponentTable request to FD. + * + * @param[in] eid - Remote MCTP endpoint + * @param[in] response - PLDM response message + * @param[in] respMsgLen - Response message length + */ + void requestUpdate(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen); + + /** @brief Handler for PassComponentTable command response + * + * The response of the PassComponentTable is processed. If the response + * indicates component can be updated, continue with either a) or b). + * + * a. Send PassComponentTable request for the next component if + * applicable + * b. UpdateComponent command to request updating a specific + * firmware component + * + * If the response indicates component may be updateable, continue + * based on the policy in DeviceUpdateOptionFlags. + * + * @param[in] eid - Remote MCTP endpoint + * @param[in] response - PLDM response message + * @param[in] respMsgLen - Response message length + */ + void passCompTable(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen); + + /** @brief Handler for UpdateComponent command response + * + * The response of the UpdateComponent is processed and will wait for + * FD to request the firmware data. + * + * @param[in] eid - Remote MCTP endpoint + * @param[in] response - PLDM response message + * @param[in] respMsgLen - Response message length + */ + void updateComponent(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen); + + /** @brief Handler for RequestFirmwareData request + * + * @param[in] request - Request message + * @param[in] payload_length - Request message payload length + * @return Response - PLDM Response message + */ + Response requestFwData(const pldm_msg* request, size_t payloadLength); + + /** @brief Handler for TransferComplete request + * + * @param[in] request - Request message + * @param[in] payload_length - Request message payload length + * @return Response - PLDM Response message + */ + Response transferComplete(const pldm_msg* request, size_t payloadLength); + + /** @brief Handler for VerifyComplete request + * + * @param[in] request - Request message + * @param[in] payload_length - Request message payload length + * @return Response - PLDM Response message + */ + Response verifyComplete(const pldm_msg* request, size_t payloadLength); + + /** @brief Handler for ApplyComplete request + * + * @param[in] request - Request message + * @param[in] payload_length - Request message payload length + * @return Response - PLDM Response message + */ + Response applyComplete(const pldm_msg* request, size_t payloadLength); + + /** @brief Handler for ActivateFirmware command response + * + * The response of the ActivateFirmware is processed and will update the + * UpdateManager with the completion of the firmware update. + * + * @param[in] eid - Remote MCTP endpoint + * @param[in] response - PLDM response message + * @param[in] respMsgLen - Response message length + */ + void activateFirmware(mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen); + + private: + /** @brief Send PassComponentTable command request + * + * @param[in] compOffset - component offset in compImageInfos + */ + void sendPassCompTableRequest(size_t offset); + + /** @brief Send UpdateComponent command request + * + * @param[in] compOffset - component offset in compImageInfos + */ + void sendUpdateComponentRequest(size_t offset); + + /** @brief Send ActivateFirmware command request */ + void sendActivateFirmwareRequest(); + + /** @brief Endpoint ID of the firmware device */ + mctp_eid_t eid; + + /** @brief File stream for firmware update package */ + std::ifstream& package; + + /** @brief FirmwareDeviceIDRecord in the fw update package that matches this + * firmware device + */ + const FirmwareDeviceIDRecord& fwDeviceIDRecord; + + /** @brief Component image information for all the components in the fw + * update package + */ + const ComponentImageInfos& compImageInfos; + + /** @brief Component info for the components in this FD derived from + * GetFirmwareParameters response + */ + const ComponentInfo& compInfo; + + /** @brief Maximum size in bytes of the variable payload to be requested by + * the FD via RequestFirmwareData command + */ + uint32_t maxTransferSize; + + /** @brief To update the status of fw update of the FD */ + UpdateManager* updateManager; + + /** @brief Component index is used to track the current component being + * updated if multiple components are applicable for the FD. + * It is also used to keep track of the next component in + * PassComponentTable + */ + size_t componentIndex = 0; + + /** @brief To send a PLDM request after the current command handling */ + std::unique_ptr<sdeventplus::source::Defer> pldmRequest; +}; + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/inventory_manager.cpp b/fw-update-legacy/inventory_manager.cpp new file mode 100644 index 0000000..207f512 --- /dev/null +++ b/fw-update-legacy/inventory_manager.cpp
@@ -0,0 +1,340 @@ +#include "inventory_manager.hpp" + +#include "common/utils.hpp" +#include "xyz/openbmc_project/Software/Version/server.hpp" + +#include <libpldm/firmware_update.h> + +#include <phosphor-logging/lg2.hpp> + +#include <functional> + +PHOSPHOR_LOG2_USING; + +namespace pldm +{ +namespace fw_update +{ +void InventoryManager::discoverFDs(const std::vector<mctp_eid_t>& eids, + const int retriesLeft) +{ + for (const auto& eid : eids) + { + auto instanceIdResult = instanceIdDb.next(eid); + if (!instanceIdResult) + { + error("Instance ID allocation failed for EID {EID}", "EID", + unsigned(eid)); + throw pldm::InstanceIdError(instanceIdResult.error()); + } + auto instanceId = instanceIdResult.value(); + Request requestMsg( + sizeof(pldm_msg_hdr) + PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES); + auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); + auto rc = encode_query_device_identifiers_req( + instanceId, PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES, request); + if (rc) + { + instanceIdDb.free(eid, instanceId); + error( + "encode_query_device_identifiers_req failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + continue; + } + + rc = handler.registerRequest( + eid, instanceId, PLDM_FWUP, PLDM_QUERY_DEVICE_IDENTIFIERS, + std::move(requestMsg), + std::move(std::bind_front(&InventoryManager::queryDeviceIdentifiers, + this, retriesLeft))); + if (rc) + { + error( + "Failed to send QueryDeviceIdentifiers request, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + else + { + info( + "Sent QueryDeviceIdentifiers request, EID={EID}, retries remaining: {RETRY}", + "EID", unsigned(eid), "RETRY", unsigned(retriesLeft)); + } + } +} + +void InventoryManager::queryDeviceIdentifiers( + const int retriesLeft, mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen) +{ + if (response == nullptr || !respMsgLen) + { + if (retriesLeft > 0) + { + error("No response received for QueryDeviceIdentifiers, EID={EID}. " + "We really want this. Retries remaining: {RETRY}", + "EID", unsigned(eid), "RETRY", unsigned(retriesLeft)); + discoverFDs({eid}, retriesLeft - 1); + } + else + { + error( + "No response received for QueryDeviceIdentifiers, EID={EID}. No more retries remaining. Womp womp.", + "EID", unsigned(eid)); + } + return; + } + + uint8_t completionCode = PLDM_SUCCESS; + uint32_t deviceIdentifiersLen = 0; + uint8_t descriptorCount = 0; + uint8_t* descriptorPtr = nullptr; + + auto rc = decode_query_device_identifiers_resp( + response, respMsgLen, &completionCode, &deviceIdentifiersLen, + &descriptorCount, &descriptorPtr); + if (rc) + { + error( + "Decoding QueryDeviceIdentifiers response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return; + } + + if (completionCode) + { + error( + "QueryDeviceIdentifiers response failed with error completion code, EID={EID}, CC = {CC}", + "EID", unsigned(eid), "CC", unsigned(completionCode)); + return; + } + + Descriptors descriptors{}; + while (descriptorCount-- && (deviceIdentifiersLen > 0)) + { + uint16_t descriptorType = 0; + variable_field descriptorData{}; + + rc = decode_descriptor_type_length_value( + descriptorPtr, deviceIdentifiersLen, &descriptorType, + &descriptorData); + if (rc) + { + error( + "Decoding descriptor type, length and value failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return; + } + + if (descriptorType != PLDM_FWUP_VENDOR_DEFINED) + { + std::vector<uint8_t> descData( + descriptorData.ptr, descriptorData.ptr + descriptorData.length); + descriptors.emplace(descriptorType, std::move(descData)); + } + else + { + uint8_t descriptorTitleStrType = 0; + variable_field descriptorTitleStr{}; + variable_field vendorDefinedDescriptorData{}; + + rc = decode_vendor_defined_descriptor_value( + descriptorData.ptr, descriptorData.length, + &descriptorTitleStrType, &descriptorTitleStr, + &vendorDefinedDescriptorData); + if (rc) + { + error( + "Decoding Vendor-defined descriptor value failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return; + } + + auto vendorDefinedDescriptorTitleStr = + utils::toString(descriptorTitleStr); + std::vector<uint8_t> vendorDescData( + vendorDefinedDescriptorData.ptr, + vendorDefinedDescriptorData.ptr + + vendorDefinedDescriptorData.length); + descriptors.emplace(descriptorType, + std::make_tuple(vendorDefinedDescriptorTitleStr, + vendorDescData)); + } + auto nextDescriptorOffset = + sizeof(pldm_descriptor_tlv().descriptor_type) + + sizeof(pldm_descriptor_tlv().descriptor_length) + + descriptorData.length; + descriptorPtr += nextDescriptorOffset; + deviceIdentifiersLen -= nextDescriptorOffset; + } + + info("EID: {EID} Descriptors:", "EID", unsigned(eid)); + for (const auto& [descriptor_type, descriptor] : descriptors) + { + if (std::holds_alternative<DescriptorData>(descriptor)) + { + const auto desc = std::get<DescriptorData>(descriptor); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (const auto& byte : desc) + { + ss << std::setw(2) << static_cast<int>(byte); + } + info("Descriptor type: {TYPE}, descriptor: {DESC}", "TYPE", + descriptor_type, "DESC", ss.str()); + } + else + { + const auto [desc_title, desc] = + std::get<VendorDefinedDescriptorInfo>(descriptor); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (const auto& byte : desc) + { + ss << std::setw(2) << static_cast<int>(byte); + } + info("Descriptor type: {TYPE}, descriptor: {DESC_TITLE} {DESC}", + "TYPE", descriptor_type, "DESC_TITLE", desc_title, "DESC", + ss.str()); + } + } + descriptorMap.emplace(eid, std::move(descriptors)); + + // Send GetFirmwareParameters request + sendGetFirmwareParametersRequest(retriesLeft, eid); +} + +void InventoryManager::sendGetFirmwareParametersRequest(const int retriesLeft, + mctp_eid_t eid) +{ + auto instanceIdResult = instanceIdDb.next(eid); + if (!instanceIdResult) + { + error("Instance ID allocation failed for EID {EID}", "EID", + unsigned(eid)); + throw pldm::InstanceIdError(instanceIdResult.error()); + } + auto instanceId = instanceIdResult.value(); + Request requestMsg( + sizeof(pldm_msg_hdr) + PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES); + auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); + auto rc = encode_get_firmware_parameters_req( + instanceId, PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES, request); + if (rc) + { + instanceIdDb.free(eid, instanceId); + error("encode_get_firmware_parameters_req failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return; + } + + rc = handler.registerRequest( + eid, instanceId, PLDM_FWUP, PLDM_GET_FIRMWARE_PARAMETERS, + std::move(requestMsg), + std::move(std::bind_front(&InventoryManager::getFirmwareParameters, + this, retriesLeft))); + if (rc) + { + error( + "Failed to send GetFirmwareParameters request, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + } + else + { + info( + "Sent GetFirmwareParameters request, EID={EID}, retries remaining: {RETRY}", + "EID", unsigned(eid), "RETRY", unsigned(retriesLeft)); + } +} + +void InventoryManager::getFirmwareParameters( + const int retriesLeft, mctp_eid_t eid, const pldm_msg* response, + size_t respMsgLen) +{ + if (response == nullptr || !respMsgLen) + { + if (retriesLeft > 0) + { + error("No response received for GetFirmwareParameters, EID={EID}. " + "We really want this. Retries remaining: {RETRY}", + "EID", unsigned(eid), "RETRY", unsigned(retriesLeft)); + sendGetFirmwareParametersRequest(retriesLeft - 1, eid); + } + else + { + error( + "No response received for GetFirmwareParameters, EID={EID}. No more retries remaining. Womp womp.", + "EID", unsigned(eid)); + } + descriptorMap.erase(eid); + return; + } + + pldm_get_firmware_parameters_resp fwParams{}; + variable_field activeCompImageSetVerStr{}; + variable_field pendingCompImageSetVerStr{}; + variable_field compParamTable{}; + + auto rc = decode_get_firmware_parameters_resp( + response, respMsgLen, &fwParams, &activeCompImageSetVerStr, + &pendingCompImageSetVerStr, &compParamTable); + if (rc) + { + error( + "Decoding GetFirmwareParameters response failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return; + } + + if (fwParams.completion_code) + { + error( + "GetFirmwareParameters response failed with error completion code, EID={EID}, CC = {CC}", + "EID", unsigned(eid), "CC", unsigned(fwParams.completion_code)); + return; + } + + auto compParamPtr = compParamTable.ptr; + auto compParamTableLen = compParamTable.length; + pldm_component_parameter_entry compEntry{}; + variable_field activeCompVerStr{}; + variable_field pendingCompVerStr{}; + + ComponentInfo componentInfo{}; + while (fwParams.comp_count-- && (compParamTableLen > 0)) + { + auto rc = decode_get_firmware_parameters_resp_comp_entry( + compParamPtr, compParamTableLen, &compEntry, &activeCompVerStr, + &pendingCompVerStr); + if (rc) + { + error( + "Decoding component parameter table entry failed, EID={EID}, RC = {RC}", + "EID", unsigned(eid), "RC", rc); + return; + } + + auto compClassification = compEntry.comp_classification; + auto compIdentifier = compEntry.comp_identifier; + componentInfo.emplace( + std::make_pair(compClassification, compIdentifier), + compEntry.comp_classification_index); + compParamPtr += sizeof(pldm_component_parameter_entry) + + activeCompVerStr.length + pendingCompVerStr.length; + compParamTableLen -= sizeof(pldm_component_parameter_entry) + + activeCompVerStr.length + pendingCompVerStr.length; + } + info("EID: {EID} Components:", "EID", unsigned(eid)); + for (const auto& [comp_key, comp_class_index] : componentInfo) + { + const auto& [comp_class, comp_identifier] = comp_key; + info( + "Classification: {CLASS} Identifier: {IDENT} Classification index: {CLASS_INDEX}", + "CLASS", unsigned(comp_class), "IDENT", unsigned(comp_identifier), + "CLASS_INDEX", unsigned(comp_class_index)); + } + componentInfoMap.emplace(eid, std::move(componentInfo)); +} + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/inventory_manager.hpp b/fw-update-legacy/inventory_manager.hpp new file mode 100644 index 0000000..b879fa4 --- /dev/null +++ b/fw-update-legacy/inventory_manager.hpp
@@ -0,0 +1,109 @@ +#pragma once + +#include "common/instance_id.hpp" +#include "common/types.hpp" +#include "requester/handler.hpp" + +namespace pldm +{ + +namespace fw_update +{ + +/** @class InventoryManager + * + * InventoryManager class manages the software inventory of firmware devices + * managed by the BMC. It discovers the firmware identifiers and the component + * details of the FD. Firmware identifiers, component details and update + * capabilities of FD are populated by the InventoryManager and is used for the + * firmware update of the FDs. + */ +class InventoryManager +{ + public: + InventoryManager() = delete; + InventoryManager(const InventoryManager&) = delete; + InventoryManager(InventoryManager&&) = delete; + InventoryManager& operator=(const InventoryManager&) = delete; + InventoryManager& operator=(InventoryManager&&) = delete; + ~InventoryManager() = default; + + /** @brief Constructor + * + * @param[in] handler - PLDM request handler + * @param[in] instanceIdDb - Managing instance ID for PLDM requests + * @param[out] descriptorMap - Populate the firmware identifers for the + * FDs managed by the BMC. + * @param[out] componentInfoMap - Populate the component info for the FDs + * managed by the BMC. + */ + explicit InventoryManager( + pldm::requester::Handler<pldm::requester::Request>& handler, + InstanceIdDb& instanceIdDb, DescriptorMap& descriptorMap, + ComponentInfoMap& componentInfoMap) : + handler(handler), instanceIdDb(instanceIdDb), + descriptorMap(descriptorMap), componentInfoMap(componentInfoMap) + {} + + /** @brief Discover the firmware identifiers and component details of FDs + * + * Inventory commands QueryDeviceIdentifiers and GetFirmwareParmeters + * commands are sent to every FD and the response is used to populate + * the firmware identifiers and component details of the FDs. + * + * @param[in] eids - MCTP endpoint ID of the FDs + * @param[in] retriesLeft - How many retry attempts are left. + */ + void discoverFDs(const std::vector<mctp_eid_t>& eids, int retriesLeft); + + /** @brief Handler for QueryDeviceIdentifiers command response + * + * The response of the QueryDeviceIdentifiers is processed and firmware + * identifiers of the FD is updated. GetFirmwareParameters command request + * is sent to the FD. + * + * @param[in] retriesLeft - How many retry attempts are left. + * @param[in] eid - Remote MCTP endpoint + * @param[in] response - PLDM response message + * @param[in] respMsgLen - Response message length + */ + void queryDeviceIdentifiers(int retriesLeft, mctp_eid_t eid, + const pldm_msg* response, size_t respMsgLen); + + /** @brief Handler for GetFirmwareParameters command response + * + * Handling the response of GetFirmwareParameters command and create + * software version D-Bus objects. + * + * @param[in] retriesLeft - How many retry attempts are left. + * @param[in] eid - Remote MCTP endpoint + * @param[in] response - PLDM response message + * @param[in] respMsgLen - Response message length + */ + void getFirmwareParameters(int retriesLeft, mctp_eid_t eid, + const pldm_msg* response, size_t respMsgLen); + + private: + /** @brief Send GetFirmwareParameters command request + * + * @param[in] retriesLeft - How many retry attempts are left. + * @param[in] eid - Remote MCTP endpoint + */ + void sendGetFirmwareParametersRequest(int retriesLeft, mctp_eid_t eid); + + /** @brief PLDM request handler */ + pldm::requester::Handler<pldm::requester::Request>& handler; + + /** @brief Instance ID database for managing instance ID*/ + InstanceIdDb& instanceIdDb; + + /** @brief Device identifiers of the managed FDs */ + DescriptorMap& descriptorMap; + + /** @brief Component information needed for the update of the managed FDs */ + ComponentInfoMap& componentInfoMap; +}; + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/manager.hpp b/fw-update-legacy/manager.hpp new file mode 100644 index 0000000..649bf20 --- /dev/null +++ b/fw-update-legacy/manager.hpp
@@ -0,0 +1,158 @@ +#pragma once + +#include "config.h" + +#include "activation.hpp" +#include "common/instance_id.hpp" +#include "common/types.hpp" +#include "device_updater.hpp" +#include "inventory_manager.hpp" +#include "requester/handler.hpp" +#include "requester/mctp_endpoint_discovery.hpp" +#include "update_manager.hpp" + +#include <unordered_map> +#include <vector> + +namespace pldm +{ + +namespace fw_update +{ + +constexpr uint8_t FW_UPDATE_DISCOVERY_RETRIES = 2; + +/** @class Manager + * + * This class handles all the aspects of the PLDM FW update specification for + * the MCTP devices + */ +class Manager : public pldm::MctpDiscoveryHandlerIntf +{ + public: + Manager() = delete; + Manager(const Manager&) = delete; + Manager(Manager&&) = delete; + Manager& operator=(const Manager&) = delete; + Manager& operator=(Manager&&) = delete; + ~Manager() = default; + + /** @brief Constructor + * + * @param[in] handler - PLDM request handler + */ + explicit Manager(Event& event, + requester::Handler<requester::Request>& handler, + pldm::InstanceIdDb& instanceIdDb) : + handler(handler), + inventoryMgr(handler, instanceIdDb, descriptorMap, componentInfoMap), + updateManager(event, handler, instanceIdDb, descriptorMap, + componentInfoMap) + {} + + /** @brief Helper function to invoke registered handlers for + * the added MCTP endpoints + * + * @param[in] mctpInfos - information of discovered MCTP endpoints + */ + void handleMctpEndpoints(const TerminusInfos& mctpInfos) override + { + std::vector<mctp_eid_t> eids; + for (const auto& [tid, mctpInfo] : mctpInfos) + { + auto eid = std::get<pldm::eid>(mctpInfo); + auto network = std::get<NetworkId>(mctpInfo); + + if (auto* transport = handler.getTransport()) + { + transport->mapTid(tid, network, eid); + } + eids.push_back(eid); + } + inventoryMgr.discoverFDs(eids, FW_UPDATE_DISCOVERY_RETRIES); + } + + /** @brief Helper function to invoke registered handlers for + * the removed MCTP endpoints + * + * @param[in] mctpInfos - information of removed MCTP endpoints + */ + void handleRemovedMctpEndpoints(const TerminusInfos& mctpInfos) override + { + for (const auto& [tid, mctpInfo] : mctpInfos) + { + if (auto* transport = handler.getTransport()) + { + transport->unmapTid(tid); + } + } + } + + /** @brief Helper function to invoke registered handlers for + * updating the availability status of the MCTP endpoint + * + * @param[in] mctpInfo - information of the target endpoint + * @param[in] availability - new availability status + */ + void updateMctpEndpointAvailability(const MctpInfo&, Availability) override + { + return; + } + + /** @brief Get Active EIDs. + * + * @param[in] addr - MCTP address of terminus + * @param[in] terminiNames - MCTP terminus name + */ + std::optional<mctp_eid_t> getActiveEidByName(const std::string&) override + { + return std::nullopt; + } + + std::optional<pldm_tid_t> allocateOrGetTid( + const MctpInfo& /*mctpInfo*/) override + { + // FW update doesn't manage TIDs + return std::nullopt; + } + + PldmTransport* getTransport() override + { + return handler.getTransport(); + } + + /** @brief Handle PLDM request for the commands in the FW update + * specification + * + * @param[in] eid - Remote MCTP Endpoint ID + * @param[in] command - PLDM command code + * @param[in] request - PLDM request message + * @param[in] requestLen - PLDM request message length + * @return PLDM response message + */ + Response handleRequest(mctp_eid_t eid, Command command, + const pldm_msg* request, size_t reqMsgLen) + { + return updateManager.handleRequest(eid, command, request, reqMsgLen); + } + + private: + /** @brief Reference to the PLDM request handler */ + requester::Handler<requester::Request>& handler; + + /** Descriptor information of all the discovered MCTP endpoints */ + DescriptorMap descriptorMap; + + /** Component information of all the discovered MCTP endpoints */ + ComponentInfoMap componentInfoMap; + + /** @brief PLDM firmware inventory manager */ + InventoryManager inventoryMgr; + + /** @brief PLDM firmware update manager */ + UpdateManager updateManager; +}; + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/package_parser.cpp b/fw-update-legacy/package_parser.cpp new file mode 100644 index 0000000..8381acc --- /dev/null +++ b/fw-update-legacy/package_parser.cpp
@@ -0,0 +1,325 @@ +#include "package_parser.hpp" + +#include "common/utils.hpp" + +#include <libpldm/edac.h> +#include <libpldm/firmware_update.h> + +#include <phosphor-logging/lg2.hpp> +#include <xyz/openbmc_project/Common/error.hpp> + +#include <iostream> +#include <memory> + +PHOSPHOR_LOG2_USING; + +namespace pldm +{ + +namespace fw_update +{ + +using InternalFailure = + sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; + +size_t PackageParser::parseFDIdentificationArea( + DeviceIDRecordCount deviceIdRecCount, const std::vector<uint8_t>& pkgHdr, + size_t offset) +{ + size_t pkgHdrRemainingSize = pkgHdr.size() - offset; + + while (deviceIdRecCount-- && (pkgHdrRemainingSize > 0)) + { + pldm_firmware_device_id_record deviceIdRecHeader{}; + variable_field applicableComponents{}; + variable_field compImageSetVersionStr{}; + variable_field recordDescriptors{}; + variable_field fwDevicePkgData{}; + + auto rc = decode_firmware_device_id_record( + pkgHdr.data() + offset, pkgHdrRemainingSize, + componentBitmapBitLength, &deviceIdRecHeader, &applicableComponents, + &compImageSetVersionStr, &recordDescriptors, &fwDevicePkgData); + if (rc) + { + error("Decoding firmware device ID record failed, RC={RC}", "RC", + rc); + throw InternalFailure(); + } + + Descriptors descriptors{}; + while (deviceIdRecHeader.descriptor_count-- && + (recordDescriptors.length > 0)) + { + uint16_t descriptorType = 0; + variable_field descriptorData{}; + + rc = decode_descriptor_type_length_value( + recordDescriptors.ptr, recordDescriptors.length, + &descriptorType, &descriptorData); + if (rc) + { + error( + "Decoding descriptor type, length and value failed, RC={RC}", + "RC", rc); + throw InternalFailure(); + } + + if (descriptorType != PLDM_FWUP_VENDOR_DEFINED) + { + descriptors.emplace( + descriptorType, + DescriptorData{descriptorData.ptr, + descriptorData.ptr + descriptorData.length}); + } + else + { + uint8_t descTitleStrType = 0; + variable_field descTitleStr{}; + variable_field vendorDefinedDescData{}; + + rc = decode_vendor_defined_descriptor_value( + descriptorData.ptr, descriptorData.length, + &descTitleStrType, &descTitleStr, &vendorDefinedDescData); + if (rc) + { + error( + "Decoding Vendor-defined descriptor value failed, RC={RC}", + "RC", rc); + throw InternalFailure(); + } + + descriptors.emplace( + descriptorType, + std::make_tuple(utils::toString(descTitleStr), + VendorDefinedDescriptorData{ + vendorDefinedDescData.ptr, + vendorDefinedDescData.ptr + + vendorDefinedDescData.length})); + } + + auto nextDescriptorOffset = + sizeof(pldm_descriptor_tlv().descriptor_type) + + sizeof(pldm_descriptor_tlv().descriptor_length) + + descriptorData.length; + recordDescriptors.ptr += nextDescriptorOffset; + recordDescriptors.length -= nextDescriptorOffset; + } + + DeviceUpdateOptionFlags deviceUpdateOptionFlags = + deviceIdRecHeader.device_update_option_flags.value; + + ApplicableComponents componentsList; + + for (size_t varBitfieldIdx = 0; + varBitfieldIdx < applicableComponents.length; varBitfieldIdx++) + { + std::bitset<8> entry{*(applicableComponents.ptr + varBitfieldIdx)}; + for (size_t idx = 0; idx < entry.size(); idx++) + { + if (entry[idx]) + { + componentsList.emplace_back( + idx + (varBitfieldIdx * entry.size())); + } + } + } + + fwDeviceIDRecords.emplace_back(std::make_tuple( + deviceUpdateOptionFlags, componentsList, + utils::toString(compImageSetVersionStr), std::move(descriptors), + FirmwareDevicePackageData{ + fwDevicePkgData.ptr, + fwDevicePkgData.ptr + fwDevicePkgData.length})); + offset += deviceIdRecHeader.record_length; + pkgHdrRemainingSize -= deviceIdRecHeader.record_length; + } + + return offset; +} + +size_t PackageParser::parseCompImageInfoArea(ComponentImageCount compImageCount, + const std::vector<uint8_t>& pkgHdr, + size_t offset) +{ + size_t pkgHdrRemainingSize = pkgHdr.size() - offset; + + while (compImageCount-- && (pkgHdrRemainingSize > 0)) + { + pldm_component_image_information compImageInfo{}; + variable_field compVersion{}; + + auto rc = decode_pldm_comp_image_info( + pkgHdr.data() + offset, pkgHdrRemainingSize, &compImageInfo, + &compVersion); + if (rc) + { + error("Decoding component image information failed, RC={RC}", "RC", + rc); + throw InternalFailure(); + } + + CompClassification compClassification = + compImageInfo.comp_classification; + CompIdentifier compIdentifier = compImageInfo.comp_identifier; + CompComparisonStamp compComparisonTime = + compImageInfo.comp_comparison_stamp; + CompOptions compOptions = compImageInfo.comp_options.value; + ReqCompActivationMethod reqCompActivationMethod = + compImageInfo.requested_comp_activation_method.value; + CompLocationOffset compLocationOffset = + compImageInfo.comp_location_offset; + CompSize compSize = compImageInfo.comp_size; + + componentImageInfos.emplace_back(std::make_tuple( + compClassification, compIdentifier, compComparisonTime, compOptions, + reqCompActivationMethod, compLocationOffset, compSize, + utils::toString(compVersion))); + offset += sizeof(pldm_component_image_information) + + compImageInfo.comp_version_string_length; + pkgHdrRemainingSize -= sizeof(pldm_component_image_information) + + compImageInfo.comp_version_string_length; + } + + return offset; +} + +void PackageParser::validatePkgTotalSize(uintmax_t pkgSize) +{ + uintmax_t calcPkgSize = pkgHeaderSize; + for (const auto& componentImageInfo : componentImageInfos) + { + CompLocationOffset compLocOffset = std::get<static_cast<size_t>( + ComponentImageInfoPos::CompLocationOffsetPos)>(componentImageInfo); + CompSize compSize = + std::get<static_cast<size_t>(ComponentImageInfoPos::CompSizePos)>( + componentImageInfo); + + if (compLocOffset != calcPkgSize) + { + auto cmpVersion = std::get<static_cast<size_t>( + ComponentImageInfoPos::CompVersionPos)>(componentImageInfo); + error( + "Validating the component location offset failed, COMP_VERSION={COMP_VERS}", + "COMP_VERS", cmpVersion); + throw InternalFailure(); + } + + calcPkgSize += compSize; + } + + if (calcPkgSize != pkgSize) + { + error( + "Package size does not match calculated package size, PKG_SIZE={PKG_SIZE}, CALC_PKG_SIZE={CAL_PKG_SIZE}", + "PKG_SIZE", pkgSize, "CAL_PKG_SIZE", calcPkgSize); + throw InternalFailure(); + } +} + +void PackageParserV1::parse(const std::vector<uint8_t>& pkgHdr, + uintmax_t pkgSize) +{ + if (pkgHeaderSize != pkgHdr.size()) + { + error("Package header size is invalid, PKG_HDR_SIZE={PKG_HDR_SIZE}", + "PKG_HDR_SIZE", pkgHeaderSize); + throw InternalFailure(); + } + + size_t offset = sizeof(pldm_package_header_information) + pkgVersion.size(); + if (offset + sizeof(DeviceIDRecordCount) >= pkgHeaderSize) + { + error("Parsing package header failed, PKG_HDR_SIZE={PKG_HDR_SIZE}", + "PKG_HDR_SIZE", pkgHeaderSize); + throw InternalFailure(); + } + + auto deviceIdRecCount = static_cast<DeviceIDRecordCount>(pkgHdr[offset]); + offset += sizeof(DeviceIDRecordCount); + + offset = parseFDIdentificationArea(deviceIdRecCount, pkgHdr, offset); + if (deviceIdRecCount != fwDeviceIDRecords.size()) + { + error( + "DeviceIDRecordCount entries not found, DEVICE_ID_REC_COUNT={DREC_CNT}", + "DREC_CNT", deviceIdRecCount); + throw InternalFailure(); + } + if (offset + sizeof(ComponentImageCount) >= pkgHeaderSize) + { + error("Parsing package header failed, PKG_HDR_SIZE={PKG_HDR_SIZE}", + "PKG_HDR_SIZE", pkgHeaderSize); + throw InternalFailure(); + } + + auto compImageCount = static_cast<ComponentImageCount>( + le16toh(pkgHdr[offset] | (pkgHdr[offset + 1] << 8))); + offset += sizeof(ComponentImageCount); + + offset = parseCompImageInfoArea(compImageCount, pkgHdr, offset); + if (compImageCount != componentImageInfos.size()) + { + error( + "ComponentImageCount entries not found, COMP_IMAGE_COUNT={COMP_IMG_CNT}", + "COMP_IMG_CNT", compImageCount); + throw InternalFailure(); + } + + if (offset + sizeof(PackageHeaderChecksum) != pkgHeaderSize) + { + error("Parsing package header failed, PKG_HDR_SIZE={PKG_HDR_SIZE}", + "PKG_HDR_SIZE", pkgHeaderSize); + throw InternalFailure(); + } + + auto calcChecksum = pldm_edac_crc32(pkgHdr.data(), offset); + auto checksum = static_cast<PackageHeaderChecksum>( + le32toh(pkgHdr[offset] | (pkgHdr[offset + 1] << 8) | + (pkgHdr[offset + 2] << 16) | (pkgHdr[offset + 3] << 24))); + if (calcChecksum != checksum) + { + error( + "Parsing package header failed, CALC_CHECKSUM={CHK_SUM}, PKG_HDR_CHECKSUM={PKG_HDR_CHK_SUM}", + "CHK_SUM", calcChecksum, "PKG_HDR_CHK_SUM", checksum); + throw InternalFailure(); + } + + validatePkgTotalSize(pkgSize); +} + +std::unique_ptr<PackageParser> parsePkgHeader(std::vector<uint8_t>& pkgData) +{ + constexpr std::array<uint8_t, PLDM_FWUP_UUID_LENGTH> hdrIdentifierv1{ + 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, + 0x98, 0x00, 0xA0, 0x2F, 0x05, 0x9A, 0xCA, 0x02}; + constexpr uint8_t pkgHdrVersion1 = 0x01; + + pldm_package_header_information pkgHeader{}; + variable_field pkgVersion{}; + auto rc = decode_pldm_package_header_info(pkgData.data(), pkgData.size(), + &pkgHeader, &pkgVersion); + if (rc) + { + error("Decoding PLDM package header information failed, RC={RC}", "RC", + rc); + return nullptr; + } + + if (std::equal(pkgHeader.uuid, pkgHeader.uuid + PLDM_FWUP_UUID_LENGTH, + hdrIdentifierv1.begin(), hdrIdentifierv1.end()) && + (pkgHeader.package_header_format_version == pkgHdrVersion1)) + { + PackageHeaderSize pkgHdrSize = pkgHeader.package_header_size; + ComponentBitmapBitLength componentBitmapBitLength = + pkgHeader.component_bitmap_bit_length; + return std::make_unique<PackageParserV1>( + pkgHdrSize, utils::toString(pkgVersion), componentBitmapBitLength); + } + + return nullptr; +} + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/package_parser.hpp b/fw-update-legacy/package_parser.hpp new file mode 100644 index 0000000..5578916 --- /dev/null +++ b/fw-update-legacy/package_parser.hpp
@@ -0,0 +1,187 @@ +#pragma once + +#include "common/types.hpp" + +#include <libpldm/firmware_update.h> + +#include <array> +#include <cstdint> +#include <memory> +#include <tuple> +#include <vector> + +namespace pldm +{ + +namespace fw_update +{ + +/** @class PackageParser + * + * PackageParser is the abstract base class for parsing the PLDM firmware + * update package. The PLDM firmware update contains two major sections; the + * firmware package header, and the firmware package payload. Each package + * header version will have a concrete implementation of the PackageParser. + * The concrete implementation understands the format of the package header and + * will implement the parse API. + */ +class PackageParser +{ + public: + PackageParser() = delete; + PackageParser(const PackageParser&) = delete; + PackageParser(PackageParser&&) = default; + PackageParser& operator=(const PackageParser&) = delete; + PackageParser& operator=(PackageParser&&) = delete; + virtual ~PackageParser() = default; + + /** @brief Constructor + * + * @param[in] pkgHeaderSize - Size of package header section + * @param[in] pkgVersion - Package version + * @param[in] componentBitmapBitLength - The number of bits used to + * represent the bitmap in the + * ApplicableComponents field for a + * matching device. + */ + explicit PackageParser(PackageHeaderSize pkgHeaderSize, + const PackageVersion& pkgVersion, + ComponentBitmapBitLength componentBitmapBitLength) : + pkgHeaderSize(pkgHeaderSize), pkgVersion(pkgVersion), + componentBitmapBitLength(componentBitmapBitLength) + {} + + /** @brief Parse the firmware update package header + * + * @param[in] pkgHdr - Package header + * @param[in] pkgSize - Size of the firmware update package + * + * @note Throws exception is parsing fails + */ + virtual void parse(const std::vector<uint8_t>& pkgHdr, + uintmax_t pkgSize) = 0; + + /** @brief Get firmware device ID records from the package + * + * @return if parsing the package is successful, return firmware device ID + * records + */ + const FirmwareDeviceIDRecords& getFwDeviceIDRecords() const + { + return fwDeviceIDRecords; + } + + /** @brief Get component image information from the package + * + * @return if parsing the package is successful, return component image + * information + */ + const ComponentImageInfos& getComponentImageInfos() const + { + return componentImageInfos; + } + + /** @brief Device identifiers of the managed FDs */ + const PackageHeaderSize pkgHeaderSize; + + /** @brief Package version string */ + const PackageVersion pkgVersion; + + protected: + /** @brief Parse the firmware device identification area + * + * @param[in] deviceIdRecCount - count of firmware device ID records + * @param[in] pkgHdr - firmware package header + * @param[in] offset - offset in package header which is the start of the + * firmware device identification area + * + * @return On success return the offset which is the end of the firmware + * device identification area, on error throw exception. + */ + size_t parseFDIdentificationArea(DeviceIDRecordCount deviceIdRecCount, + const std::vector<uint8_t>& pkgHdr, + size_t offset); + + /** @brief Parse the component image information area + * + * @param[in] compImageCount - component image count + * @param[in] pkgHdr - firmware package header + * @param[in] offset - offset in package header which is the start of the + * component image information area + * + * @return On success return the offset which is the end of the component + * image information area, on error throw exception. + */ + size_t parseCompImageInfoArea(ComponentImageCount compImageCount, + const std::vector<uint8_t>& pkgHdr, + size_t offset); + + /** @brief Validate the total size of the package + * + * Verify the total size of the package is the sum of package header and + * the size of each component. + * + * @param[in] pkgSize - firmware update package size + * + * @note Throws exception if validation fails + */ + void validatePkgTotalSize(uintmax_t pkgSize); + + /** @brief Firmware Device ID Records in the package */ + FirmwareDeviceIDRecords fwDeviceIDRecords; + + /** @brief Component Image Information in the package */ + ComponentImageInfos componentImageInfos; + + /** @brief The number of bits that will be used to represent the bitmap in + * the ApplicableComponents field for matching device. The value + * shall be a multiple of 8 and be large enough to contain a bit + * for each component in the package. + */ + const ComponentBitmapBitLength componentBitmapBitLength; +}; + +/** @class PackageParserV1 + * + * This class implements the package parser for the header format version 0x01 + */ +class PackageParserV1 final : public PackageParser +{ + public: + PackageParserV1() = delete; + PackageParserV1(const PackageParserV1&) = delete; + PackageParserV1(PackageParserV1&&) = default; + PackageParserV1& operator=(const PackageParserV1&) = delete; + PackageParserV1& operator=(PackageParserV1&&) = delete; + ~PackageParserV1() = default; + + /** @brief Constructor + * + * @param[in] pkgHeaderSize - Size of package header section + * @param[in] pkgVersion - Package version + * @param[in] componentBitmapBitLength - The number of bits used to + * represent the bitmap in the + * ApplicableComponents field for a + * matching device. + */ + explicit PackageParserV1( + PackageHeaderSize pkgHeaderSize, const PackageVersion& pkgVersion, + ComponentBitmapBitLength componentBitmapBitLength) : + PackageParser(pkgHeaderSize, pkgVersion, componentBitmapBitLength) + {} + + virtual void parse(const std::vector<uint8_t>& pkgHdr, uintmax_t pkgSize); +}; + +/** @brief Parse the package header information + * + * @param[in] pkgHdrInfo - package header information section in the package + * + * @return On success return the PackageParser for the header format version + * on failure return nullptr + */ +std::unique_ptr<PackageParser> parsePkgHeader(std::vector<uint8_t>& pkgHdrInfo); + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/test/device_updater_test.cpp b/fw-update-legacy/test/device_updater_test.cpp new file mode 100644 index 0000000..f4aac0b --- /dev/null +++ b/fw-update-legacy/test/device_updater_test.cpp
@@ -0,0 +1,152 @@ +#include "common/instance_id.hpp" +#include "common/utils.hpp" +#include "fw-update-legacy/device_updater.hpp" +#include "fw-update-legacy/package_parser.hpp" +#include "requester/handler.hpp" + +#include <libpldm/firmware_update.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +using namespace pldm; +using namespace pldm::fw_update; + +class DeviceUpdaterTest : public testing::Test +{ + protected: + DeviceUpdaterTest() : + package("./test_pkg", std::ios::binary | std::ios::in | std::ios::ate) + { + fwDeviceIDRecord = { + 1, + {0x00}, + "VersionString2", + {{PLDM_FWUP_UUID, + std::vector<uint8_t>{0x16, 0x20, 0x23, 0xC9, 0x3E, 0xC5, 0x41, + 0x15, 0x95, 0xF4, 0x48, 0x70, 0x1D, 0x49, + 0xD6, 0x75}}}, + {}}; + compImageInfos = { + {10, 100, 0xFFFFFFFF, 0, 0, 139, 1024, "VersionString3"}}; + compInfo = {{std::make_pair(10, 100), 1}}; + } + + int fd = -1; + std::ifstream package; + FirmwareDeviceIDRecord fwDeviceIDRecord; + ComponentImageInfos compImageInfos; + ComponentInfo compInfo; +}; + +TEST_F(DeviceUpdaterTest, validatePackage) +{ + constexpr uintmax_t testPkgSize = 1163; + uintmax_t packageSize = package.tellg(); + EXPECT_EQ(packageSize, testPkgSize); + + 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); + + auto parser = parsePkgHeader(packageHeader); + EXPECT_NE(parser, nullptr); + + package.seekg(0); + packageHeader.resize(parser->pkgHeaderSize); + package.read(reinterpret_cast<char*>(packageHeader.data()), + parser->pkgHeaderSize); + + parser->parse(packageHeader, packageSize); + const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords(); + const auto& testPkgCompImageInfos = parser->getComponentImageInfos(); + + EXPECT_EQ(fwDeviceIDRecords.size(), 1); + EXPECT_EQ(compImageInfos.size(), 1); + EXPECT_EQ(fwDeviceIDRecords[0], fwDeviceIDRecord); + EXPECT_EQ(testPkgCompImageInfos, compImageInfos); +} + +TEST_F(DeviceUpdaterTest, ReadPackage512B) +{ + DeviceUpdater deviceUpdater(0, package, fwDeviceIDRecord, compImageInfos, + compInfo, 512, nullptr); + + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + + sizeof(pldm_request_firmware_data_req)> + reqFwDataReq{0x8A, 0x05, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00}; + constexpr uint8_t instanceId = 0x0A; + constexpr uint8_t completionCode = PLDM_SUCCESS; + constexpr uint32_t length = 512; + auto requestMsg = reinterpret_cast<const pldm_msg*>(reqFwDataReq.data()); + auto response = deviceUpdater.requestFwData( + requestMsg, sizeof(pldm_request_firmware_data_req)); + + EXPECT_EQ(response.size(), + sizeof(pldm_msg_hdr) + sizeof(completionCode) + length); + auto responeMsg = reinterpret_cast<const pldm_msg*>(response.data()); + EXPECT_EQ(responeMsg->hdr.request, PLDM_RESPONSE); + EXPECT_EQ(responeMsg->hdr.instance_id, instanceId); + EXPECT_EQ(responeMsg->hdr.type, PLDM_FWUP); + EXPECT_EQ(responeMsg->hdr.command, PLDM_REQUEST_FIRMWARE_DATA); + EXPECT_EQ(response[sizeof(pldm_msg_hdr)], completionCode); + + const std::vector<uint8_t> compFirst512B{ + 0x0A, 0x05, 0x15, 0x00, 0x48, 0xD2, 0x1E, 0x80, 0x2E, 0x77, 0x71, 0x2C, + 0x8E, 0xE3, 0x1F, 0x6F, 0x30, 0x76, 0x65, 0x08, 0xB8, 0x1B, 0x4B, 0x03, + 0x7E, 0x96, 0xD9, 0x2A, 0x36, 0x3A, 0xA2, 0xEE, 0x8A, 0x30, 0x21, 0x33, + 0xFC, 0x27, 0xE7, 0x3E, 0x56, 0x79, 0x0E, 0xBD, 0xED, 0x44, 0x96, 0x2F, + 0x84, 0xB5, 0xED, 0x19, 0x3A, 0x5E, 0x62, 0x2A, 0x6E, 0x41, 0x7E, 0xDC, + 0x2E, 0xBB, 0x87, 0x41, 0x7F, 0xCE, 0xF0, 0xD7, 0xE4, 0x0F, 0x95, 0x33, + 0x3B, 0xF9, 0x04, 0xF8, 0x1A, 0x92, 0x54, 0xFD, 0x33, 0xBA, 0xCD, 0xA6, + 0x08, 0x0D, 0x32, 0x2C, 0xEB, 0x75, 0xDC, 0xEA, 0xBA, 0x30, 0x94, 0x78, + 0x8C, 0x61, 0x58, 0xD0, 0x59, 0xF3, 0x29, 0x6D, 0x67, 0xD3, 0x26, 0x08, + 0x25, 0x1E, 0x69, 0xBB, 0x28, 0xB0, 0x61, 0xFB, 0x96, 0xA3, 0x8C, 0xBF, + 0x01, 0x94, 0xEB, 0x3A, 0x63, 0x6F, 0xC8, 0x0F, 0x42, 0x7F, 0xEB, 0x3D, + 0xA7, 0x8B, 0xE5, 0xD2, 0xFB, 0xB8, 0xD3, 0x15, 0xAA, 0xDF, 0x86, 0xAB, + 0x6E, 0x29, 0xB3, 0x12, 0x96, 0xB7, 0x86, 0xDA, 0xF9, 0xD7, 0x70, 0xAD, + 0xB6, 0x1A, 0x29, 0xB1, 0xA4, 0x2B, 0x6F, 0x63, 0xEE, 0x05, 0x9F, 0x35, + 0x49, 0xA1, 0xAB, 0xA2, 0x6F, 0x7C, 0xFC, 0x23, 0x09, 0x55, 0xED, 0xF7, + 0x35, 0xD8, 0x2F, 0x8F, 0xD2, 0xBD, 0x77, 0xED, 0x0C, 0x7A, 0xE9, 0xD3, + 0xF7, 0x90, 0xA7, 0x45, 0x97, 0xAA, 0x3A, 0x79, 0xC4, 0xF8, 0xD2, 0xFE, + 0xFB, 0xB3, 0x25, 0x86, 0x98, 0x6B, 0x98, 0x10, 0x15, 0xB3, 0xDD, 0x43, + 0x0B, 0x20, 0x5F, 0xE4, 0x62, 0xC8, 0xA1, 0x3E, 0x9C, 0xF3, 0xD8, 0xEA, + 0x15, 0xA1, 0x24, 0x94, 0x1C, 0xF5, 0xB4, 0x86, 0x04, 0x30, 0x2C, 0x84, + 0xB6, 0x29, 0xF6, 0x9D, 0x76, 0x6E, 0xD4, 0x0C, 0x1C, 0xBD, 0xF9, 0x95, + 0x7E, 0xAF, 0x62, 0x80, 0x14, 0xE6, 0x1C, 0x43, 0x51, 0x5C, 0xCA, 0x50, + 0xE1, 0x73, 0x3D, 0x75, 0x66, 0x52, 0x9E, 0xB6, 0x15, 0x7E, 0xF7, 0xE5, + 0xE2, 0xAF, 0x54, 0x75, 0x82, 0x3D, 0x55, 0xC7, 0x59, 0xD7, 0xBD, 0x8C, + 0x4B, 0x74, 0xD1, 0x3F, 0xA8, 0x1B, 0x0A, 0xF0, 0x5A, 0x32, 0x2B, 0xA7, + 0xA4, 0xBE, 0x38, 0x18, 0xAE, 0x69, 0xDC, 0x54, 0x7C, 0x60, 0xEF, 0x4F, + 0x0F, 0x7F, 0x5A, 0xA6, 0xC8, 0x3E, 0x59, 0xFD, 0xF5, 0x98, 0x26, 0x71, + 0xD0, 0xEF, 0x54, 0x47, 0x38, 0x1F, 0x18, 0x9D, 0x37, 0x9D, 0xF0, 0xCD, + 0x00, 0x73, 0x30, 0xD4, 0xB7, 0xDA, 0x2D, 0x36, 0xA1, 0xA9, 0xAD, 0x4F, + 0x9F, 0x17, 0xA5, 0xA1, 0x62, 0x18, 0x21, 0xDD, 0x0E, 0xB6, 0x72, 0xDE, + 0x17, 0xF0, 0x71, 0x94, 0xA9, 0x67, 0xB4, 0x75, 0xDB, 0x64, 0xF0, 0x6E, + 0x3D, 0x4E, 0x29, 0x45, 0x42, 0xC3, 0xDA, 0x1F, 0x9E, 0x31, 0x4D, 0x1B, + 0xA7, 0x9D, 0x07, 0xD9, 0x10, 0x75, 0x27, 0x92, 0x16, 0x35, 0xF5, 0x51, + 0x3E, 0x14, 0x00, 0xB4, 0xBD, 0x21, 0xAF, 0x90, 0xC5, 0xE5, 0xEE, 0xD0, + 0xB3, 0x7F, 0x61, 0xA5, 0x1B, 0x91, 0xD5, 0x66, 0x08, 0xB5, 0x16, 0x25, + 0xC2, 0x16, 0x53, 0xDC, 0xB5, 0xF1, 0xDD, 0xCF, 0x28, 0xDD, 0x57, 0x90, + 0x66, 0x33, 0x7B, 0x75, 0xF4, 0x8A, 0x19, 0xAC, 0x1F, 0x44, 0xC2, 0xF6, + 0x21, 0x07, 0xE9, 0xCC, 0xDD, 0xCF, 0x4A, 0x34, 0xA1, 0x24, 0x82, 0xF8, + 0xA1, 0x1D, 0x06, 0x90, 0x4B, 0x97, 0xB8, 0x10, 0xF2, 0x6A, 0x55, 0x30, + 0xD9, 0x4F, 0x94, 0xE7, 0x7C, 0xBB, 0x73, 0xA3, 0x5F, 0xC6, 0xF1, 0xDB, + 0x84, 0x3D, 0x29, 0x72, 0xD1, 0xAD, 0x2D, 0x77, 0x3F, 0x36, 0x24, 0x0F, + 0xC4, 0x12, 0xD7, 0x3C, 0x65, 0x6C, 0xE1, 0x5A, 0x32, 0xAA, 0x0B, 0xA3, + 0xA2, 0x72, 0x33, 0x00, 0x3C, 0x7E, 0x28, 0x36, 0x10, 0x90, 0x38, 0xFB}; + EXPECT_EQ(response, compFirst512B); +}
diff --git a/fw-update-legacy/test/inventory_manager_test.cpp b/fw-update-legacy/test/inventory_manager_test.cpp new file mode 100644 index 0000000..024a0c0 --- /dev/null +++ b/fw-update-legacy/test/inventory_manager_test.cpp
@@ -0,0 +1,187 @@ +#include "common/utils.hpp" +#include "fw-update-legacy/inventory_manager.hpp" +#include "requester/test/mock_request.hpp" +#include "test/test_instance_id.hpp" + +#include <libpldm/firmware_update.h> + +#include <gtest/gtest.h> + +using namespace pldm; +using namespace std::chrono; +using namespace pldm::fw_update; + +class InventoryManagerTest : public testing::Test +{ + protected: + InventoryManagerTest() : + event(sdeventplus::Event::get_default()), instanceIdDb(), + reqHandler(nullptr, event, instanceIdDb, false, seconds(1), 2, + milliseconds(100)), + inventoryManager(reqHandler, instanceIdDb, outDescriptorMap, + outComponentInfoMap) + {} + + int fd = -1; + sdeventplus::Event event; + TestInstanceIdDb instanceIdDb; + requester::Handler<requester::Request> reqHandler; + InventoryManager inventoryManager; + DescriptorMap outDescriptorMap{}; + ComponentInfoMap outComponentInfoMap{}; +}; + +TEST_F(InventoryManagerTest, handleQueryDeviceIdentifiersResponse) +{ + constexpr size_t respPayloadLength1 = 49; + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + respPayloadLength1> + queryDeviceIdentifiersResp1{ + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, + 0x04, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x02, 0x00, 0x10, 0x00, 0x12, + 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18, 0xa0, 0x30, 0xfc, 0x8a, + 0x56, 0x58, 0x7d, 0x5b, 0xFF, 0xFF, 0x0B, 0x00, 0x01, 0x07, 0x4f, + 0x70, 0x65, 0x6e, 0x42, 0x4d, 0x43, 0x01, 0x02}; + auto responseMsg1 = + reinterpret_cast<const pldm_msg*>(queryDeviceIdentifiersResp1.data()); + inventoryManager.queryDeviceIdentifiers(1, responseMsg1, + respPayloadLength1); + + DescriptorMap descriptorMap1{ + {0x01, + {{PLDM_FWUP_IANA_ENTERPRISE_ID, + std::vector<uint8_t>{0x0a, 0x0b, 0x0c, 0xd}}, + {PLDM_FWUP_UUID, + std::vector<uint8_t>{0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18, + 0xa0, 0x30, 0xfc, 0x8a, 0x56, 0x58, 0x7d, + 0x5b}}, + {PLDM_FWUP_VENDOR_DEFINED, + std::make_tuple("OpenBMC", std::vector<uint8_t>{0x01, 0x02})}}}}; + + EXPECT_EQ(outDescriptorMap.size(), descriptorMap1.size()); + EXPECT_EQ(outDescriptorMap, descriptorMap1); + + constexpr size_t respPayloadLength2 = 26; + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + respPayloadLength2> + queryDeviceIdentifiersResp2{ + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x00, 0x10, 0x00, 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, + 0x43, 0x98, 0x00, 0xA0, 0x2F, 0x59, 0x9A, 0xCA, 0x02}; + auto responseMsg2 = + reinterpret_cast<const pldm_msg*>(queryDeviceIdentifiersResp2.data()); + inventoryManager.queryDeviceIdentifiers(2, responseMsg2, + respPayloadLength2); + DescriptorMap descriptorMap2{ + {0x01, + {{PLDM_FWUP_IANA_ENTERPRISE_ID, + std::vector<uint8_t>{0x0a, 0x0b, 0x0c, 0xd}}, + {PLDM_FWUP_UUID, + std::vector<uint8_t>{0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18, + 0xa0, 0x30, 0xfc, 0x8a, 0x56, 0x58, 0x7d, + 0x5b}}, + {PLDM_FWUP_VENDOR_DEFINED, + std::make_tuple("OpenBMC", std::vector<uint8_t>{0x01, 0x02})}}}, + {0x02, + {{PLDM_FWUP_UUID, + std::vector<uint8_t>{0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, + 0x98, 0x00, 0xA0, 0x2F, 0x59, 0x9A, 0xCA, + 0x02}}}}}; + EXPECT_EQ(outDescriptorMap.size(), descriptorMap2.size()); + EXPECT_EQ(outDescriptorMap, descriptorMap2); +} + +TEST_F(InventoryManagerTest, handleQueryDeviceIdentifiersResponseErrorCC) +{ + constexpr size_t respPayloadLength = 1; + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + respPayloadLength> + queryDeviceIdentifiersResp{0x00, 0x00, 0x00, 0x01}; + auto responseMsg = + reinterpret_cast<const pldm_msg*>(queryDeviceIdentifiersResp.data()); + inventoryManager.queryDeviceIdentifiers(1, responseMsg, respPayloadLength); + EXPECT_EQ(outDescriptorMap.size(), 0); +} + +TEST_F(InventoryManagerTest, getFirmwareParametersResponse) +{ + // constexpr uint16_t compCount = 2; + // constexpr std::string_view activeCompImageSetVersion{"DeviceVer1.0"}; + // constexpr std::string_view activeCompVersion1{"Comp1v2.0"}; + // constexpr std::string_view activeCompVersion2{"Comp2v3.0"}; + constexpr uint16_t compClassification1 = 10; + constexpr uint16_t compIdentifier1 = 300; + constexpr uint8_t compClassificationIndex1 = 20; + constexpr uint16_t compClassification2 = 16; + constexpr uint16_t compIdentifier2 = 301; + constexpr uint8_t compClassificationIndex2 = 30; + + constexpr size_t respPayloadLength1 = 119; + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + respPayloadLength1> + getFirmwareParametersResp1{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x0c, 0x00, 0x00, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x31, 0x2e, 0x30, 0x0a, 0x00, 0x2c, 0x01, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, + 0x6f, 0x6d, 0x70, 0x31, 0x76, 0x32, 0x2e, 0x30, 0x10, 0x00, 0x2d, + 0x01, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6d, 0x70, 0x32, 0x76, 0x33, 0x2e, + 0x30}; + auto responseMsg1 = + reinterpret_cast<const pldm_msg*>(getFirmwareParametersResp1.data()); + inventoryManager.getFirmwareParameters(1, responseMsg1, respPayloadLength1); + + ComponentInfoMap componentInfoMap1{ + {1, + {{std::make_pair(compClassification1, compIdentifier1), + compClassificationIndex1}, + {std::make_pair(compClassification2, compIdentifier2), + compClassificationIndex2}}}}; + EXPECT_EQ(outComponentInfoMap.size(), componentInfoMap1.size()); + EXPECT_EQ(outComponentInfoMap, componentInfoMap1); + + // constexpr uint16_t compCount = 1; + // constexpr std::string_view activeCompImageSetVersion{"DeviceVer2.0"}; + // constexpr std::string_view activeCompVersion1{"Comp3v4.0"}; + constexpr uint16_t compClassification3 = 2; + constexpr uint16_t compIdentifier3 = 302; + constexpr uint8_t compClassificationIndex3 = 40; + + constexpr size_t respPayloadLength2 = 119; + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + respPayloadLength2> + getFirmwareParametersResp2{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x0c, 0x00, 0x00, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x32, 0x2e, 0x30, 0x02, 0x00, 0x2e, 0x01, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, + 0x6f, 0x6d, 0x70, 0x33, 0x76, 0x34, 0x2e, 0x30}; + auto responseMsg2 = + reinterpret_cast<const pldm_msg*>(getFirmwareParametersResp2.data()); + inventoryManager.getFirmwareParameters(2, responseMsg2, respPayloadLength2); + + ComponentInfoMap componentInfoMap2{ + {1, + {{std::make_pair(compClassification1, compIdentifier1), + compClassificationIndex1}, + {std::make_pair(compClassification2, compIdentifier2), + compClassificationIndex2}}}, + {2, + {{std::make_pair(compClassification3, compIdentifier3), + compClassificationIndex3}}}}; + EXPECT_EQ(outComponentInfoMap.size(), componentInfoMap2.size()); + EXPECT_EQ(outComponentInfoMap, componentInfoMap2); +} + +TEST_F(InventoryManagerTest, getFirmwareParametersResponseErrorCC) +{ + constexpr size_t respPayloadLength = 1; + constexpr std::array<uint8_t, sizeof(pldm_msg_hdr) + respPayloadLength> + getFirmwareParametersResp{0x00, 0x00, 0x00, 0x01}; + auto responseMsg = + reinterpret_cast<const pldm_msg*>(getFirmwareParametersResp.data()); + inventoryManager.getFirmwareParameters(1, responseMsg, respPayloadLength); + EXPECT_EQ(outComponentInfoMap.size(), 0); +}
diff --git a/fw-update-legacy/test/meson.build b/fw-update-legacy/test/meson.build new file mode 100644 index 0000000..ee1bdb1 --- /dev/null +++ b/fw-update-legacy/test/meson.build
@@ -0,0 +1,34 @@ +fw_update_test_src = declare_dependency( + sources: [ + '../inventory_manager.cpp', + '../package_parser.cpp', + '../device_updater.cpp', + '../update_manager.cpp', + '../../common/utils.cpp', + ]) + +tests = [ + 'inventory_manager_test', + 'package_parser_test', + 'device_updater_test' +] + +foreach t : tests + test(t, executable(t.underscorify(), t + '.cpp', + implicit_include_directories: false, + include_directories: '../../pldmd', + link_args: dynamic_linker, + build_rpath: get_option('oe-sdk').enabled() ? rpath : '', + dependencies: [ + fw_update_test_src, + gmock, + gtest, + libpldm_dep, + libpldmutils, + nlohmann_json, + phosphor_dbus_interfaces, + phosphor_logging_dep, + sdbusplus, + sdeventplus]), + workdir: meson.current_source_dir()) +endforeach
diff --git a/fw-update-legacy/test/package_parser_test.cpp b/fw-update-legacy/test/package_parser_test.cpp new file mode 100644 index 0000000..111b31e --- /dev/null +++ b/fw-update-legacy/test/package_parser_test.cpp
@@ -0,0 +1,183 @@ +#include "fw-update-legacy/package_parser.hpp" + +#include <typeinfo> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +using namespace pldm::fw_update; + +TEST(PackageParser, ValidPkgSingleDescriptorSingleComponent) +{ + std::vector<uint8_t> fwPkgHdr{ + 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, 0x98, 0x00, 0xA0, 0x2F, + 0x05, 0x9A, 0xCA, 0x02, 0x01, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0x0C, 0xE5, 0x07, 0x00, 0x08, 0x00, 0x01, 0x0E, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x31, 0x01, 0x2E, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, + 0x00, 0x00, 0x01, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, + 0x72, 0x69, 0x6E, 0x67, 0x32, 0x02, 0x00, 0x10, 0x00, 0x16, 0x20, 0x23, + 0xC9, 0x3E, 0xC5, 0x41, 0x15, 0x95, 0xF4, 0x48, 0x70, 0x1D, 0x49, 0xD6, + 0x75, 0x01, 0x00, 0x0A, 0x00, 0x64, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, + 0x0E, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, + 0x6E, 0x67, 0x33, 0x4F, 0x96, 0xAE, 0x56}; + + constexpr uintmax_t pkgSize = 166; + constexpr std::string_view pkgVersion{"VersionString1"}; + auto parser = parsePkgHeader(fwPkgHdr); + auto obj = parser.get(); + EXPECT_EQ(typeid(*obj).name(), typeid(PackageParserV1).name()); + EXPECT_EQ(parser->pkgHeaderSize, fwPkgHdr.size()); + EXPECT_EQ(parser->pkgVersion, pkgVersion); + + parser->parse(fwPkgHdr, pkgSize); + auto outfwDeviceIDRecords = parser->getFwDeviceIDRecords(); + FirmwareDeviceIDRecords fwDeviceIDRecords{ + {1, + {0}, + "VersionString2", + {{PLDM_FWUP_UUID, + std::vector<uint8_t>{0x16, 0x20, 0x23, 0xC9, 0x3E, 0xC5, 0x41, 0x15, + 0x95, 0xF4, 0x48, 0x70, 0x1D, 0x49, 0xD6, + 0x75}}}, + {}}, + }; + EXPECT_EQ(outfwDeviceIDRecords, fwDeviceIDRecords); + + auto outCompImageInfos = parser->getComponentImageInfos(); + ComponentImageInfos compImageInfos{ + {10, 100, 0xFFFFFFFF, 0, 0, 139, 27, "VersionString3"}}; + EXPECT_EQ(outCompImageInfos, compImageInfos); +} + +TEST(PackageParser, ValidPkgMultipleDescriptorsMultipleComponents) +{ + std::vector<uint8_t> fwPkgHdr{ + 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, 0x98, 0x00, 0xA0, 0x2F, + 0x05, 0x9A, 0xCA, 0x02, 0x01, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0x0C, 0xE5, 0x07, 0x00, 0x08, 0x00, 0x01, 0x0E, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x31, 0x03, 0x45, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, + 0x00, 0x00, 0x03, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, + 0x72, 0x69, 0x6E, 0x67, 0x32, 0x02, 0x00, 0x10, 0x00, 0x12, 0x44, 0xD2, + 0x64, 0x8D, 0x7D, 0x47, 0x18, 0xA0, 0x30, 0xFC, 0x8A, 0x56, 0x58, 0x7D, + 0x5B, 0x01, 0x00, 0x04, 0x00, 0x47, 0x16, 0x00, 0x00, 0xFF, 0xFF, 0x0B, + 0x00, 0x01, 0x07, 0x4F, 0x70, 0x65, 0x6E, 0x42, 0x4D, 0x43, 0x12, 0x34, + 0x2E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x07, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x33, 0x02, 0x00, 0x10, 0x00, 0x12, 0x44, 0xD2, 0x64, 0x8D, 0x7D, + 0x47, 0x18, 0xA0, 0x30, 0xFC, 0x8A, 0x56, 0x58, 0x7D, 0x5C, 0x2E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x01, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x34, + 0x02, 0x00, 0x10, 0x00, 0x12, 0x44, 0xD2, 0x64, 0x8D, 0x7D, 0x47, 0x18, + 0xA0, 0x30, 0xFC, 0x8A, 0x56, 0x58, 0x7D, 0x5D, 0x03, 0x00, 0x0A, 0x00, + 0x64, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x46, 0x01, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x35, 0x0A, 0x00, + 0xC8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x00, 0x61, 0x01, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x36, 0x10, 0x00, + 0x2C, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x0C, 0x00, 0x7C, 0x01, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x37, 0xF1, 0x90, + 0x9C, 0x71}; + + constexpr uintmax_t pkgSize = 407; + constexpr std::string_view pkgVersion{"VersionString1"}; + auto parser = parsePkgHeader(fwPkgHdr); + auto obj = parser.get(); + EXPECT_EQ(typeid(*obj).name(), typeid(PackageParserV1).name()); + EXPECT_EQ(parser->pkgHeaderSize, fwPkgHdr.size()); + EXPECT_EQ(parser->pkgVersion, pkgVersion); + + parser->parse(fwPkgHdr, pkgSize); + auto outfwDeviceIDRecords = parser->getFwDeviceIDRecords(); + FirmwareDeviceIDRecords fwDeviceIDRecords{ + {1, + {0, 1}, + "VersionString2", + {{PLDM_FWUP_UUID, + std::vector<uint8_t>{0x12, 0x44, 0xD2, 0x64, 0x8D, 0x7D, 0x47, 0x18, + 0xA0, 0x30, 0xFC, 0x8A, 0x56, 0x58, 0x7D, + 0x5B}}, + {PLDM_FWUP_IANA_ENTERPRISE_ID, + std::vector<uint8_t>{0x47, 0x16, 0x00, 0x00}}, + {PLDM_FWUP_VENDOR_DEFINED, + std::make_tuple("OpenBMC", std::vector<uint8_t>{0x12, 0x34})}}, + {}}, + {0, + {0, 1, 2}, + "VersionString3", + {{PLDM_FWUP_UUID, + std::vector<uint8_t>{0x12, 0x44, 0xD2, 0x64, 0x8D, 0x7D, 0x47, 0x18, + 0xA0, 0x30, 0xFC, 0x8A, 0x56, 0x58, 0x7D, + 0x5C}}}, + {}}, + {0, + {0}, + "VersionString4", + {{PLDM_FWUP_UUID, + std::vector<uint8_t>{0x12, 0x44, 0xD2, 0x64, 0x8D, 0x7D, 0x47, 0x18, + 0xA0, 0x30, 0xFC, 0x8A, 0x56, 0x58, 0x7D, + 0x5D}}}, + {}}, + }; + EXPECT_EQ(outfwDeviceIDRecords, fwDeviceIDRecords); + + auto outCompImageInfos = parser->getComponentImageInfos(); + ComponentImageInfos compImageInfos{ + {10, 100, 0xFFFFFFFF, 0, 0, 326, 27, "VersionString5"}, + {10, 200, 0xFFFFFFFF, 0, 1, 353, 27, "VersionString6"}, + {16, 300, 0xFFFFFFFF, 1, 12, 380, 27, "VersionString7"}}; + EXPECT_EQ(outCompImageInfos, compImageInfos); +} + +TEST(PackageParser, InvalidPkgHeaderInfoIncomplete) +{ + std::vector<uint8_t> fwPkgHdr{0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, + 0x49, 0x43, 0x98, 0x00, 0xA0, 0x2F, + 0x05, 0x9A, 0xCA, 0x02}; + + auto parser = parsePkgHeader(fwPkgHdr); + EXPECT_EQ(parser, nullptr); +} + +TEST(PackageParser, InvalidPkgNotSupportedHeaderFormat) +{ + std::vector<uint8_t> fwPkgHdr{ + 0x12, 0x44, 0xD2, 0x64, 0x8D, 0x7D, 0x47, 0x18, 0xA0, 0x30, + 0xFC, 0x8A, 0x56, 0x58, 0x7D, 0x5B, 0x02, 0x8B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x0C, 0xE5, + 0x07, 0x00, 0x08, 0x00, 0x01, 0x0E, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x31}; + + auto parser = parsePkgHeader(fwPkgHdr); + EXPECT_EQ(parser, nullptr); +} + +TEST(PackageParser, InvalidPkgBadChecksum) +{ + std::vector<uint8_t> fwPkgHdr{ + 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43, 0x98, 0x00, 0xA0, 0x2F, + 0x05, 0x9A, 0xCA, 0x02, 0x01, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0x0C, 0xE5, 0x07, 0x00, 0x08, 0x00, 0x01, 0x0E, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x31, 0x01, 0x2E, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, + 0x00, 0x00, 0x01, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, + 0x72, 0x69, 0x6E, 0x67, 0x32, 0x02, 0x00, 0x10, 0x00, 0x16, 0x20, 0x23, + 0xC9, 0x3E, 0xC5, 0x41, 0x15, 0x95, 0xF4, 0x48, 0x70, 0x1D, 0x49, 0xD6, + 0x75, 0x01, 0x00, 0x0A, 0x00, 0x64, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, + 0x0E, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x53, 0x74, 0x72, 0x69, + 0x6E, 0x67, 0x33, 0x4F, 0x96, 0xAE, 0x57}; + + constexpr uintmax_t pkgSize = 166; + constexpr std::string_view pkgVersion{"VersionString1"}; + auto parser = parsePkgHeader(fwPkgHdr); + auto obj = parser.get(); + EXPECT_EQ(typeid(*obj).name(), typeid(PackageParserV1).name()); + EXPECT_EQ(parser->pkgHeaderSize, fwPkgHdr.size()); + EXPECT_EQ(parser->pkgVersion, pkgVersion); + EXPECT_THROW(parser->parse(fwPkgHdr, pkgSize), std::exception); +}
diff --git a/fw-update-legacy/test/test_pkg b/fw-update-legacy/test/test_pkg new file mode 100644 index 0000000..ccf9568 --- /dev/null +++ b/fw-update-legacy/test/test_pkg Binary files differ
diff --git a/fw-update-legacy/update_manager.cpp b/fw-update-legacy/update_manager.cpp new file mode 100644 index 0000000..6cdf3d9 --- /dev/null +++ b/fw-update-legacy/update_manager.cpp
@@ -0,0 +1,347 @@ +#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; + } + else + { + info("Found {COUNT} devices for firmware update:", "COUNT", + deviceUpdaterInfos.size()); + for (const auto& [eid, _] : deviceUpdaterInfos) + { + info(" EID {EID}", "EID", eid); + } + } + + 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) +{ + info("Firmware update package descriptors:"); + for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index) + { + const auto& fw_update_descriptor = + std::get<Descriptors>(fwDeviceIDRecords[index]); + for (const auto& [descriptor_type, descriptor] : fw_update_descriptor) + { + if (std::holds_alternative<DescriptorData>(descriptor)) + { + const auto desc = std::get<DescriptorData>(descriptor); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (const auto& byte : desc) + { + ss << std::setw(2) << static_cast<int>(byte); + } + info("Index {IND} Descriptor type: {TYPE}, descriptor: {DESC}", + "IND", index, "TYPE", descriptor_type, "DESC", ss.str()); + } + else + { + const auto [desc_title, desc] = + std::get<VendorDefinedDescriptorInfo>(descriptor); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (const auto& byte : desc) + { + ss << std::setw(2) << static_cast<int>(byte); + } + info( + "Index {IND} Descriptor type: {TYPE}, descriptor: {DESC_TITLE} {DESC}", + "IND", index, "TYPE", descriptor_type, "DESC_TITLE", + desc_title, "DESC", ss.str()); + } + } + } + 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(); + info( + "Matched component for firmware update: EID {EID} and index {IND}", + "EID", eid, "IND", index); + } + } + } + return deviceUpdaterInfos; +} + +void UpdateManager::updateDeviceCompletion(mctp_eid_t eid, bool status) +{ + deviceUpdateCompletionMap.emplace(eid, status); + info("Completed device EID {EID} firmware update with status {STATUS}", + "EID", eid, "STATUS", + status ? std::string("SUCCESS") : std::string("FAILURE")); + 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(); + info("Starting firmware update for {COUNT} devices.", "COUNT", + deviceUpdaterMap.size()); + 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
diff --git a/fw-update-legacy/update_manager.hpp b/fw-update-legacy/update_manager.hpp new file mode 100644 index 0000000..b89cbcc --- /dev/null +++ b/fw-update-legacy/update_manager.hpp
@@ -0,0 +1,134 @@ +#pragma once + +#include "common/instance_id.hpp" +#include "common/types.hpp" +#include "device_updater.hpp" +#include "package_parser.hpp" +#include "requester/handler.hpp" +#include "watch.hpp" + +#include <libpldm/base.h> + +#include <chrono> +#include <filesystem> +#include <fstream> +#include <tuple> +#include <unordered_map> + +namespace pldm +{ + +namespace fw_update +{ + +using namespace sdeventplus; +using namespace sdeventplus::source; +using namespace pldm; + +using DeviceIDRecordOffset = size_t; +using DeviceUpdaterInfo = std::pair<mctp_eid_t, DeviceIDRecordOffset>; +using DeviceUpdaterInfos = std::vector<DeviceUpdaterInfo>; +using TotalComponentUpdates = size_t; + +class Activation; +class ActivationProgress; + +class UpdateManager +{ + public: + UpdateManager() = delete; + UpdateManager(const UpdateManager&) = delete; + UpdateManager(UpdateManager&&) = delete; + UpdateManager& operator=(const UpdateManager&) = delete; + UpdateManager& operator=(UpdateManager&&) = delete; + ~UpdateManager() = default; + + explicit UpdateManager( + Event& event, + pldm::requester::Handler<pldm::requester::Request>& handler, + InstanceIdDb& instanceIdDb, const DescriptorMap& descriptorMap, + const ComponentInfoMap& componentInfoMap) : + event(event), handler(handler), instanceIdDb(instanceIdDb), + descriptorMap(descriptorMap), componentInfoMap(componentInfoMap), + watch(event.get(), + std::bind_front(&UpdateManager::processPackage, this)), + totalNumComponentUpdates(0), compUpdateCompletedCount(0) + {} + + /** @brief Handle PLDM request for the commands in the FW update + * specification + * + * @param[in] eid - Remote MCTP Endpoint ID + * @param[in] command - PLDM command code + * @param[in] request - PLDM request message + * @param[in] requestLen - PLDM request message length + * + * @return PLDM response message + */ + Response handleRequest(mctp_eid_t eid, uint8_t command, + const pldm_msg* request, size_t reqMsgLen); + + int processPackage(const std::filesystem::path& packageFilePath); + + void updateDeviceCompletion(mctp_eid_t eid, bool status); + + void updateActivationProgress(); + + /** @brief Callback function that will be invoked when the + * RequestedActivation will be set to active in the Activation + * interface + */ + void activatePackage(); + + void clearActivationInfo(); + + /** @brief + * + */ + DeviceUpdaterInfos associatePkgToDevices( + const FirmwareDeviceIDRecords& fwDeviceIDRecords, + const DescriptorMap& descriptorMap, + TotalComponentUpdates& totalNumComponentUpdates); + + 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 + + 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; + Watch watch; + + std::unique_ptr<Activation> activation; + std::unique_ptr<ActivationProgress> activationProgress; + std::string objPath; + + std::filesystem::path fwPackageFilePath; + std::unique_ptr<PackageParser> parser; + std::ifstream package; + + std::unordered_map<mctp_eid_t, std::unique_ptr<DeviceUpdater>> + deviceUpdaterMap; + std::unordered_map<mctp_eid_t, bool> deviceUpdateCompletionMap; + + /** @brief Total number of component updates to calculate the progress of + * the Firmware activation + */ + size_t totalNumComponentUpdates; + + /** @brief FW update package can contain updates for multiple firmware + * devices and each device can have multiple components. Once + * each component is updated (Transfer completed, Verified and + * Applied) ActivationProgress is updated. + */ + size_t compUpdateCompletedCount; + decltype(std::chrono::steady_clock::now()) startTime; +}; + +} // namespace fw_update + +} // namespace pldm
diff --git a/fw-update-legacy/watch.cpp b/fw-update-legacy/watch.cpp new file mode 100644 index 0000000..4ad6bc5 --- /dev/null +++ b/fw-update-legacy/watch.cpp
@@ -0,0 +1,146 @@ +#include "watch.hpp" + +#include <sys/inotify.h> +#include <unistd.h> + +#include <phosphor-logging/lg2.hpp> + +#include <cstddef> +#include <cstring> +#include <filesystem> +#include <stdexcept> +#include <string> + +PHOSPHOR_LOG2_USING; + +namespace pldm +{ + +namespace fw_update +{ + +using namespace std::string_literals; +namespace fs = std::filesystem; + +Watch::Watch(sd_event* loop, std::function<int(std::string&)> imageCallback) : + imageCallback(imageCallback) +{ + fd = inotify_init1(IN_NONBLOCK); + if (fd == -1) + { + // Store a copy of errno, because the string creation below will + // invalidate errno due to one more system calls. + const auto ec = errno; + error("Error {EC} {ERR} when calling inotify_init1.", "EC", ec, "ERR", + std::strerror(ec)); + throw std::runtime_error( + "inotify_init1 failed, errno="s + std::strerror(ec)); + } + + const auto rc = sd_event_add_io(loop, nullptr, fd, EPOLLIN, callback, this); + if (rc < 0) + { + error("Error {EC} calling sd_event_add_io.", "EC", rc); + throw std::runtime_error( + "failed to add to event loop, rc="s + std::strerror(-rc)); + } + + setupWatch(); +} + +void Watch::setupWatch() +{ + // Check if FIRMWARE_PACKAGE_STAGING_DIR exists and create it if not. + fs::path imgDirPath(FIRMWARE_PACKAGE_STAGING_DIR); + if (!fs::is_directory(imgDirPath)) + { + fs::create_directories(imgDirPath); + info("Directory {DIR} wasn't found, so it was created.", "DIR", + std::string(FIRMWARE_PACKAGE_STAGING_DIR)); + } + + wd = inotify_add_watch(fd, FIRMWARE_PACKAGE_STAGING_DIR, + IN_CLOSE_WRITE | IN_DELETE_SELF); + if (wd == -1) + { + const auto ec = errno; + close(fd); + error("Error {EC} {ERR} calling inotify_add_watch.", "EC", ec, "ERR", + std::strerror(ec)); + throw std::runtime_error( + "inotify_add_watch failed, errno="s + std::strerror(ec)); + } + info("Set up filesystem watch for {DIR}", "DIR", + std::string(FIRMWARE_PACKAGE_STAGING_DIR)); +} + +Watch::~Watch() +{ + if (-1 != fd) + { + if (-1 != wd) + { + inotify_rm_watch(fd, wd); + } + close(fd); + } +} + +int Watch::callback(sd_event_source* /* s */, int fd, uint32_t revents, + void* userdata) +{ + if (!(revents & EPOLLIN)) + { + return 0; + } + + constexpr auto maxBytes = 1024; + uint8_t buffer[maxBytes]; + auto bytes = read(fd, buffer, maxBytes); + if (0 > bytes) + { + auto ec = errno; + error("Error {EC} {ERR} reading inotify event.", "EC", ec, "ERR", + std::strerror(ec)); + throw std::runtime_error( + "failed to read inotify event, errno="s + std::strerror(ec)); + } + + auto offset = 0; + while (offset < bytes) + { + auto event = reinterpret_cast<inotify_event*>(&buffer[offset]); + if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR)) + { + info("Received IN_CLOSE_WRITE event on {DIR} for {FILE}", "DIR", + std::string(FIRMWARE_PACKAGE_STAGING_DIR), "FILE", + event->name); + auto tarballPath = + std::string{FIRMWARE_PACKAGE_STAGING_DIR} + '/' + event->name; + auto rc = static_cast<Watch*>(userdata)->imageCallback(tarballPath); + if (rc < 0) + { + error("Error ({EC}) processing image {IMAGE_PATH}", "EC", rc, + "IMAGE_PATH", tarballPath.c_str()); + } + } + else if (event->mask & IN_DELETE_SELF) + { + info("Received IN_DELETE_SELF event on {DIR}", "DIR", + std::string(FIRMWARE_PACKAGE_STAGING_DIR)); + auto* const watch = reinterpret_cast<Watch*>(userdata); + watch->wd = -1; + // IN_DELETE_SELF event automatically cleans up the filesystem + // watch, so we can just go ahead and set it up again without + // cleaning. + watch->setupWatch(); + } + + offset += offsetof(inotify_event, name) + event->len; + } + + return 0; +} + +} // namespace fw_update +} // namespace pldm
diff --git a/fw-update-legacy/watch.hpp b/fw-update-legacy/watch.hpp new file mode 100644 index 0000000..be6c0e5 --- /dev/null +++ b/fw-update-legacy/watch.hpp
@@ -0,0 +1,71 @@ +#pragma once + +#include <systemd/sd-event.h> + +#include <functional> +#include <string> + +namespace pldm +{ + +namespace fw_update +{ + +/** @class Watch + * + * @brief Adds inotify watch on software image upload directory + * + * The inotify watch is hooked up with sd-event, so that on call back, + * appropriate actions related to a software image upload can be taken. + */ +class Watch +{ + public: + /** @brief ctor - hook inotify watch with sd-event + * + * @param[in] loop - sd-event object + * @param[in] imageCallback - The callback function for processing + * the image + */ + Watch(sd_event* loop, std::function<int(std::string&)> imageCallback); + + Watch(const Watch&) = delete; + Watch& operator=(const Watch&) = delete; + Watch(Watch&&) = delete; + Watch& operator=(Watch&&) = delete; + + /** @brief dtor - remove inotify watch and close fd's + */ + ~Watch(); + + private: + /** @brief Sets up the filesystem watch to monitor + * FIRMWARE_PACKAGE_STAGING_DIR for file creations, when imageCallback is + * invoked, and deletion of the dir itself, so that the watch can be + * recreated. + */ + void setupWatch(); + + /** @brief sd-event callback + * + * @param[in] s - event source, floating (unused) in our case + * @param[in] fd - inotify fd + * @param[in] revents - events that matched for fd + * @param[in] userdata - pointer to Watch object + * @returns 0 on success, -1 on fail + */ + static int callback(sd_event_source* s, int fd, uint32_t revents, + void* userdata); + + /** @brief image upload directory watch descriptor */ + int wd = -1; + + /** @brief inotify file descriptor */ + int fd = -1; + + /** @brief The callback function for processing the image. */ + std::function<int(std::string&)> imageCallback; +}; + +} // namespace fw_update +} // namespace pldm
diff --git a/meson.build b/meson.build index afd02cf..a5c83ec 100644 --- a/meson.build +++ b/meson.build
@@ -147,6 +147,8 @@ conf_data.set('SENSOR_POLLING_TIME', get_option('sensor-polling-time')) conf_data.set('UPDATE_TIMEOUT_SECONDS', get_option('update-timeout-seconds')) +conf_data.set_quoted('FIRMWARE_PACKAGE_STAGING_DIR', get_option('firmware-package-staging-dir')) + # Firmware update inotify option if get_option('fw-update-pkg-inotify').allowed() conf_data.set('FW_UPDATE_INOTIFY_ENABLED', 1) @@ -304,11 +306,20 @@ fw_update_sources = [] +fw_update_legacy_sources = [ + 'fw-update-legacy/inventory_manager.cpp', + 'fw-update-legacy/package_parser.cpp', + 'fw-update-legacy/device_updater.cpp', + 'fw-update-legacy/watch.cpp', + 'fw-update-legacy/update_manager.cpp', +] + executable( 'pldmd', 'pldmd/pldmd.cpp', 'pldmd/dbus_impl_pdr.cpp', fw_update_sources, + fw_update_legacy_sources, 'platform-mc/dbus_impl_fru.cpp', 'platform-mc/terminus_manager.cpp', 'platform-mc/terminus.cpp',
diff --git a/meson.options b/meson.options index fe7d272..8d90a00 100644 --- a/meson.options +++ b/meson.options
@@ -249,6 +249,13 @@ ) # Firmware Update configuration parameters option( + 'firmware-package-staging-dir', + type: 'string', + value: '/tmp/pldm-images', + description: 'Firmware package staging directory for PLDM packages.', +) + +option( 'fw-update-pkg-inotify', type: 'feature', value: 'disabled',
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp index e6054cc..a32f6dc 100644 --- a/pldmd/pldmd.cpp +++ b/pldmd/pldmd.cpp
@@ -3,6 +3,7 @@ #include "common/instance_id.hpp" #include "common/transport.hpp" #include "common/utils.hpp" +#include "fw-update-legacy/manager.hpp" #include "invoker.hpp" #include "platform-mc/dbus_to_terminus_effecters.hpp" #include "platform-mc/manager.hpp" @@ -113,7 +114,8 @@ static std::optional<Response> processRxMsg( const std::vector<uint8_t>& requestMsg, Invoker& invoker, - requester::Handler<requester::Request>& handler, pldm_tid_t tid) + requester::Handler<requester::Request>& handler, + fw_update::Manager* fwManager, pldm_tid_t tid) { uint8_t eid = tid; @@ -132,8 +134,17 @@ size_t requestLen = requestMsg.size() - sizeof(struct pldm_msg_hdr); try { - response = invoker.handle(tid, hdrFields.pldm_type, - hdrFields.command, request, requestLen); + if (hdrFields.pldm_type != PLDM_FWUP) + { + response = + invoker.handle(tid, hdrFields.pldm_type, hdrFields.command, + request, requestLen); + } + else + { + response = fwManager->handleRequest(eid, hdrFields.command, + request, requestLen); + } } catch (const std::out_of_range& e) { @@ -213,6 +224,8 @@ std::unique_ptr<platform_mc::Manager> platformManager = std::make_unique<platform_mc::Manager>(event, reqHandler, instanceIdDb); + std::unique_ptr<fw_update::Manager> fwManager = + std::make_unique<fw_update::Manager>(event, reqHandler, instanceIdDb); pldm::host_effecters::HostEffecterParser hostEffecterParser( &instanceIdDb, pldmTransport.getEventSource(), pdrRepo.get(), @@ -362,9 +375,9 @@ #else MctpDiscovery mctpDiscoveryHandler( bus, std::initializer_list<MctpDiscoveryHandlerIntf*>{ - platformManager.get()}); + platformManager.get(), fwManager.get()}); #endif - auto callback = [verbose, &invoker, &reqHandler, &pldmTransport, + auto callback = [verbose, &invoker, &reqHandler, &fwManager, &pldmTransport, TID](IO& io, int fd, uint32_t revents) mutable { if (revents & (POLLHUP | POLLERR)) { @@ -398,8 +411,8 @@ printBuffer(Rx, requestMsgVec); } // process message and send response - auto response = - processRxMsg(requestMsgVec, invoker, reqHandler, TID); + auto response = processRxMsg(requestMsgVec, invoker, reqHandler, + fwManager.get(), TID); if (response.has_value()) { FlightRecorder::GetInstance().saveRecord(*response, true);