blob: 4c740d52f3c884d8e9342d05eef481af0eaf806a [file] [log] [blame]
#include "pldm_fw_update_cmd.hpp"
#include "common/utils.hpp"
#include "pldm_cmd_helper.hpp"
#include <libpldm/firmware_update.h>
#include <format>
namespace pldmtool
{
namespace fw_update
{
namespace
{
using namespace pldmtool::helper;
using namespace pldm::fw_update;
std::vector<std::unique_ptr<CommandInterface>> commands;
} // namespace
const std::map<uint8_t, std::string> fdStateMachine{
{PLDM_FD_STATE_IDLE, "IDLE"},
{PLDM_FD_STATE_LEARN_COMPONENTS, "LEARN COMPONENTS"},
{PLDM_FD_STATE_READY_XFER, "READY XFER"},
{PLDM_FD_STATE_DOWNLOAD, "DOWNLOAD"},
{PLDM_FD_STATE_VERIFY, "VERIFY"},
{PLDM_FD_STATE_APPLY, "APPLY"},
{PLDM_FD_STATE_ACTIVATE, "ACTIVATE"}};
const std::map<uint8_t, const char*> fdAuxState{
{PLDM_FD_OPERATION_IN_PROGRESS, "Operation in progress"},
{PLDM_FD_OPERATION_SUCCESSFUL, "Operation successful"},
{PLDM_FD_OPERATION_FAILED, "Operation Failed"},
{PLDM_FD_IDLE_LEARN_COMPONENTS_READ_XFER,
"Not applicable in current state"}};
const std::map<uint8_t, const char*> fdAuxStateStatus{
{PLDM_FD_AUX_STATE_IN_PROGRESS_OR_SUCCESS,
"AuxState is In Progress or Success"},
{PLDM_FD_TIMEOUT, "Timeout occurred while performing action"},
{PLDM_FD_GENERIC_ERROR, "Generic Error has occurred"}};
const std::map<uint8_t, const char*> fdReasonCode{
{PLDM_FD_INITIALIZATION, "Initialization of firmware device has occurred"},
{PLDM_FD_ACTIVATE_FW, "ActivateFirmware command was received"},
{PLDM_FD_CANCEL_UPDATE, "CancelUpdate command was received"},
{PLDM_FD_TIMEOUT_LEARN_COMPONENT,
"Timeout occurred when in LEARN COMPONENT state"},
{PLDM_FD_TIMEOUT_READY_XFER, "Timeout occurred when in READY XFER state"},
{PLDM_FD_TIMEOUT_DOWNLOAD, "Timeout occurred when in DOWNLOAD state"},
{PLDM_FD_TIMEOUT_VERIFY, "Timeout occurred when in VERIFY state"},
{PLDM_FD_TIMEOUT_APPLY, "Timeout occurred when in APPLY state"}};
/**
* @brief descriptor type to name mapping
*
*/
const std::map<DescriptorType, const char*> descriptorName{
{PLDM_FWUP_PCI_VENDOR_ID, "PCI Vendor ID"},
{PLDM_FWUP_IANA_ENTERPRISE_ID, "IANA Enterprise ID"},
{PLDM_FWUP_UUID, "UUID"},
{PLDM_FWUP_PNP_VENDOR_ID, "PnP Vendor ID"},
{PLDM_FWUP_ACPI_VENDOR_ID, "ACPI Vendor ID"},
{PLDM_FWUP_PCI_DEVICE_ID, "PCI Device ID"},
{PLDM_FWUP_PCI_SUBSYSTEM_VENDOR_ID, "PCI Subsystem Vendor ID"},
{PLDM_FWUP_PCI_SUBSYSTEM_ID, "PCI Subsystem ID"},
{PLDM_FWUP_PCI_REVISION_ID, "PCI Revision ID"},
{PLDM_FWUP_PNP_PRODUCT_IDENTIFIER, "PnP Product Identifier"},
{PLDM_FWUP_ACPI_PRODUCT_IDENTIFIER, "ACPI Product Identifier"},
{PLDM_FWUP_VENDOR_DEFINED, "Vendor Defined"}};
/*
* Convert PLDM Firmware String Type to uint8_t
*
* @param[in] compImgVerStrType - the component version string
*
* @return - the component version string converted to a numeric value.
*/
uint8_t convertStringTypeToUInt8(std::string compImgVerStrType)
{
static const std::map<std::string, pldm_firmware_update_string_type>
pldmFirmwareUpdateStringType{
{"UNKNOWN", PLDM_STR_TYPE_UNKNOWN},
{"ASCII", PLDM_STR_TYPE_ASCII},
{"UTF_8", PLDM_STR_TYPE_UTF_8},
{"UTF_16", PLDM_STR_TYPE_UTF_16},
{"UTF_16LE", PLDM_STR_TYPE_UTF_16LE},
{"UTF_16BE", PLDM_STR_TYPE_UTF_16BE},
};
if (pldmFirmwareUpdateStringType.contains(compImgVerStrType))
{
return pldmFirmwareUpdateStringType.at(compImgVerStrType);
}
else
{
return static_cast<uint8_t>(std::stoi(compImgVerStrType));
}
}
class GetStatus : public CommandInterface
{
public:
~GetStatus() = default;
GetStatus() = delete;
GetStatus(const GetStatus&) = delete;
GetStatus(GetStatus&&) = default;
GetStatus& operator=(const GetStatus&) = delete;
GetStatus& operator=(GetStatus&&) = delete;
using CommandInterface::CommandInterface;
std::pair<int, std::vector<uint8_t>> createRequestMsg() override
{
std::vector<uint8_t> requestMsg(
sizeof(pldm_msg_hdr) + PLDM_GET_STATUS_REQ_BYTES);
auto request = new (requestMsg.data()) pldm_msg;
auto rc = encode_get_status_req(instanceId, request,
PLDM_GET_STATUS_REQ_BYTES);
return {rc, requestMsg};
}
void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
{
uint8_t completionCode = 0;
uint8_t currentState = 0;
uint8_t previousState = 0;
uint8_t auxState = 0;
uint8_t auxStateStatus = 0;
uint8_t progressPercent = 0;
uint8_t reasonCode = 0;
bitfield32_t updateOptionFlagsEnabled{0};
auto rc = decode_get_status_resp(
responsePtr, payloadLength, &completionCode, &currentState,
&previousState, &auxState, &auxStateStatus, &progressPercent,
&reasonCode, &updateOptionFlagsEnabled);
if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
{
std::cerr << "Response Message Error: "
<< "rc=" << rc << ",cc=" << (int)completionCode << "\n";
return;
}
ordered_json data;
data["CurrentState"] = fdStateMachine.at(currentState);
data["PreviousState"] = fdStateMachine.at(previousState);
data["AuxState"] = fdAuxState.at(auxState);
if (auxStateStatus >= PLDM_FD_VENDOR_DEFINED_STATUS_CODE_START &&
auxStateStatus <= PLDM_FD_VENDOR_DEFINED_STATUS_CODE_END)
{
data["AuxStateStatus"] = auxStateStatus;
}
else
{
data["AuxStateStatus"] = fdAuxStateStatus.at(auxStateStatus);
}
data["ProgressPercent"] = progressPercent;
if (reasonCode >= PLDM_FD_STATUS_VENDOR_DEFINED_MIN &&
reasonCode <= PLDM_FD_STATUS_VENDOR_DEFINED_MAX)
{
data["ReasonCode"] = reasonCode;
}
else
{
data["ReasonCode"] = fdReasonCode.at(reasonCode);
}
data["UpdateOptionFlagsEnabled"] = updateOptionFlagsEnabled.value;
pldmtool::helper::DisplayInJson(data);
}
};
const std::map<uint16_t, std::string> componentClassification{
{PLDM_COMP_UNKNOWN, "Unknown"},
{PLDM_COMP_OTHER, "Other"},
{PLDM_COMP_DRIVER, "Driver"},
{PLDM_COMP_CONFIGURATION_SOFTWARE, "Configuration Software"},
{PLDM_COMP_APPLICATION_SOFTWARE, "Application Software"},
{PLDM_COMP_INSTRUMENTATION, "Instrumentation"},
{PLDM_COMP_FIRMWARE_OR_BIOS, "Firmware/BIOS"},
{PLDM_COMP_DIAGNOSTIC_SOFTWARE, "Diagnostic Software"},
{PLDM_COMP_OPERATING_SYSTEM, "Operating System"},
{PLDM_COMP_MIDDLEWARE, "Middleware"},
{PLDM_COMP_FIRMWARE, "Firmware"},
{PLDM_COMP_BIOS_OR_FCODE, "BIOS/FCode"},
{PLDM_COMP_SUPPORT_OR_SERVICEPACK, "Support/Service Pack"},
{PLDM_COMP_SOFTWARE_BUNDLE, "Software Bundle"},
{PLDM_COMP_DOWNSTREAM_DEVICE, "Downstream Device"}};
class GetFwParams : public CommandInterface
{
public:
~GetFwParams() = default;
GetFwParams() = delete;
GetFwParams(const GetFwParams&) = delete;
GetFwParams(GetFwParams&&) = default;
GetFwParams& operator=(const GetFwParams&) = delete;
GetFwParams& operator=(GetFwParams&&) = delete;
using CommandInterface::CommandInterface;
std::pair<int, std::vector<uint8_t>> createRequestMsg() override
{
std::vector<uint8_t> requestMsg(
sizeof(pldm_msg_hdr) + PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES);
auto request = new (requestMsg.data()) pldm_msg;
auto rc = encode_get_firmware_parameters_req(
instanceId, PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES, request);
return {rc, requestMsg};
}
void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
{
pldm_get_firmware_parameters_resp fwParams{};
variable_field activeCompImageSetVersion{};
variable_field pendingCompImageSetVersion{};
variable_field compParameterTable{};
auto rc = decode_get_firmware_parameters_resp(
responsePtr, payloadLength, &fwParams, &activeCompImageSetVersion,
&pendingCompImageSetVersion, &compParameterTable);
if (rc != PLDM_SUCCESS || fwParams.completion_code != PLDM_SUCCESS)
{
std::cerr << "Response Message Error: "
<< "rc=" << rc << ",cc=" << (int)fwParams.completion_code
<< "\n";
return;
}
ordered_json capabilitiesDuringUpdate;
if (fwParams.capabilities_during_update.bits.bit0)
{
capabilitiesDuringUpdate
["Component Update Failure Recovery Capability"] =
"Device will not revert to previous component image upon failure, timeout or cancellation of the transfer.";
}
else
{
capabilitiesDuringUpdate
["Component Update Failure Recovery Capability"] =
"Device will revert to previous component image upon failure, timeout or cancellation of the transfer.";
}
if (fwParams.capabilities_during_update.bits.bit1)
{
capabilitiesDuringUpdate["Component Update Failure Retry Capability"] =
"Device will not be able to update component again unless it exits update mode and the UA sends a new Request Update command.";
}
else
{
capabilitiesDuringUpdate["Component Update Failure Retry Capability"] =
" Device can have component updated again without exiting update mode and restarting transfer via RequestUpdate command.";
}
if (fwParams.capabilities_during_update.bits.bit2)
{
capabilitiesDuringUpdate["Firmware Device Partial Updates"] =
"Firmware Device can support a partial update, whereby a package which contains a component image set that is a subset of all components currently residing on the FD, can be transferred.";
}
else
{
capabilitiesDuringUpdate["Firmware Device Partial Updates"] =
"Firmware Device cannot accept a partial update and all components present on the FD shall be updated.";
}
if (fwParams.capabilities_during_update.bits.bit3)
{
capabilitiesDuringUpdate
["Firmware Device Host Functionality during Firmware Update"] =
"Device will not revert to previous component image upon failure, timeout or cancellation of the transfer";
}
else
{
capabilitiesDuringUpdate
["Firmware Device Host Functionality during Firmware Update"] =
"Device will revert to previous component image upon failure, timeout or cancellation of the transfer";
}
if (fwParams.capabilities_during_update.bits.bit4)
{
capabilitiesDuringUpdate["Firmware Device Update Mode Restrictions"] =
"Firmware device unable to enter update mode if host OS environment is active.";
}
else
{
capabilitiesDuringUpdate
["Firmware Device Update Mode Restrictions"] =
"No host OS environment restriction for update mode";
}
ordered_json data;
data["CapabilitiesDuringUpdate"] = capabilitiesDuringUpdate;
data["ComponentCount"] = static_cast<uint16_t>(fwParams.comp_count);
data["ActiveComponentImageSetVersionString"] =
pldm::utils::toString(activeCompImageSetVersion);
data["PendingComponentImageSetVersionString"] =
pldm::utils::toString(pendingCompImageSetVersion);
auto compParamPtr = compParameterTable.ptr;
auto compParamTableLen = compParameterTable.length;
pldm_component_parameter_entry compEntry{};
variable_field activeCompVerStr{};
variable_field pendingCompVerStr{};
ordered_json compDataEntries;
while (fwParams.comp_count-- && (compParamTableLen > 0))
{
ordered_json compData;
auto rc = decode_get_firmware_parameters_resp_comp_entry(
compParamPtr, compParamTableLen, &compEntry, &activeCompVerStr,
&pendingCompVerStr);
if (rc)
{
std::cerr
<< "Decoding component parameter table entry failed, RC="
<< rc << "\n";
return;
}
if (componentClassification.contains(compEntry.comp_classification))
{
compData["ComponentClassification"] =
componentClassification.at(compEntry.comp_classification);
}
else
{
compData["ComponentClassification"] =
static_cast<uint16_t>(compEntry.comp_classification);
}
compData["ComponentIdentifier"] =
static_cast<uint16_t>(compEntry.comp_identifier);
compData["ComponentClassificationIndex"] =
static_cast<uint8_t>(compEntry.comp_classification_index);
compData["ActiveComponentComparisonStamp"] =
static_cast<uint32_t>(compEntry.active_comp_comparison_stamp);
// ActiveComponentReleaseData
std::array<uint8_t, 8> noReleaseData{0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
if (std::equal(noReleaseData.begin(), noReleaseData.end(),
compEntry.active_comp_release_date))
{
compData["ActiveComponentReleaseDate"] = "";
}
else
{
std::string activeComponentReleaseDate(
reinterpret_cast<const char*>(
compEntry.active_comp_release_date),
sizeof(compEntry.active_comp_release_date));
compData["ActiveComponentReleaseDate"] =
activeComponentReleaseDate;
}
compData["PendingComponentComparisonStamp"] =
static_cast<uint32_t>(compEntry.pending_comp_comparison_stamp);
// PendingComponentReleaseData
if (std::equal(noReleaseData.begin(), noReleaseData.end(),
compEntry.pending_comp_release_date))
{
compData["PendingComponentReleaseDate"] = "";
}
else
{
std::string pendingComponentReleaseDate(
reinterpret_cast<const char*>(
compEntry.pending_comp_release_date),
sizeof(compEntry.pending_comp_release_date));
compData["PendingComponentReleaseDate"] =
pendingComponentReleaseDate;
}
// ComponentActivationMethods
ordered_json componentActivationMethods;
if (compEntry.comp_activation_methods.bits.bit0)
{
componentActivationMethods.push_back("Automatic");
}
else if (compEntry.comp_activation_methods.bits.bit1)
{
componentActivationMethods.push_back("Self-Contained");
}
else if (compEntry.comp_activation_methods.bits.bit2)
{
componentActivationMethods.push_back("Medium-specific reset");
}
else if (compEntry.comp_activation_methods.bits.bit3)
{
componentActivationMethods.push_back("System reboot");
}
else if (compEntry.comp_activation_methods.bits.bit4)
{
componentActivationMethods.push_back("DC power cycel");
}
else if (compEntry.comp_activation_methods.bits.bit5)
{
componentActivationMethods.push_back("AC power cycle");
}
compData["ComponentActivationMethods"] = componentActivationMethods;
// CapabilitiesDuringUpdate
ordered_json compCapabilitiesDuringUpdate;
if (compEntry.capabilities_during_update.bits.bit0)
{
compCapabilitiesDuringUpdate
["Firmware Device apply state functionality"] =
"Firmware Device performs an auto-apply during transfer phase and apply step will be completed immediately.";
}
else
{
compCapabilitiesDuringUpdate
["Firmware Device apply state functionality"] =
" Firmware Device will execute an operation during the APPLY state which will include migrating the new component image to its final non-volatile storage destination.";
}
compData["CapabilitiesDuringUpdate"] = compCapabilitiesDuringUpdate;
compData["ActiveComponentVersionString"] =
pldm::utils::toString(activeCompVerStr);
compData["PendingComponentVersionString"] =
pldm::utils::toString(pendingCompVerStr);
compParamPtr += sizeof(pldm_component_parameter_entry) +
activeCompVerStr.length + pendingCompVerStr.length;
compParamTableLen -=
sizeof(pldm_component_parameter_entry) +
activeCompVerStr.length + pendingCompVerStr.length;
compDataEntries.push_back(compData);
}
data["ComponentParameterEntries"] = compDataEntries;
pldmtool::helper::DisplayInJson(data);
}
};
class QueryDeviceIdentifiers : public CommandInterface
{
public:
~QueryDeviceIdentifiers() = default;
QueryDeviceIdentifiers() = delete;
QueryDeviceIdentifiers(const QueryDeviceIdentifiers&) = delete;
QueryDeviceIdentifiers(QueryDeviceIdentifiers&&) = default;
QueryDeviceIdentifiers& operator=(const QueryDeviceIdentifiers&) = delete;
QueryDeviceIdentifiers& operator=(QueryDeviceIdentifiers&&) = delete;
/**
* @brief Implementation of createRequestMsg for QueryDeviceIdentifiers
*
* @return std::pair<int, std::vector<uint8_t>>
*/
std::pair<int, std::vector<uint8_t>> createRequestMsg() override;
/**
* @brief Implementation of parseResponseMsg for QueryDeviceIdentifiers
*
* @param[in] responsePtr
* @param[in] payloadLength
*/
void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override;
using CommandInterface::CommandInterface;
private:
/**
* @brief Method to update QueryDeviceIdentifiers json response in a user
* friendly format
*
* @param[in] descriptors - descriptor json response
* @param[in] descriptorType - descriptor type
* @param[in] descriptorVal - descriptor value
*/
void updateDescriptor(
ordered_json& descriptors, const DescriptorType& descriptorType,
const std::variant<DescriptorData, VendorDefinedDescriptorInfo>&
descriptorVal);
};
void QueryDeviceIdentifiers::updateDescriptor(
ordered_json& descriptors, const DescriptorType& descriptorType,
const std::variant<DescriptorData, VendorDefinedDescriptorInfo>&
descriptorVal)
{
std::ostringstream descDataStream;
DescriptorData descData;
if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
{
descData = std::get<DescriptorData>(descriptorVal);
}
else
{
descData = std::get<VendorDefinedDescriptorData>(
std::get<VendorDefinedDescriptorInfo>(descriptorVal));
}
for (int byte : descData)
{
descDataStream << std::setfill('0') << std::setw(2) << std::hex << byte;
}
if (descriptorName.contains(descriptorType))
{
// Update the existing json response if entry is already present
for (auto& descriptor : descriptors)
{
if (descriptor["Type"] == descriptorName.at(descriptorType))
{
if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
{
descriptor["Value"].emplace_back(descDataStream.str());
}
else
{
ordered_json vendorDefinedVal;
vendorDefinedVal[std::get<VendorDefinedDescriptorTitle>(
std::get<VendorDefinedDescriptorInfo>(descriptorVal))] =
descDataStream.str();
descriptor["Value"].emplace_back(vendorDefinedVal);
}
return;
}
}
// Entry is not present, add type and value to json response
ordered_json descriptor = ordered_json::object(
{{"Type", descriptorName.at(descriptorType)},
{"Value", ordered_json::array()}});
if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
{
descriptor["Value"].emplace_back(descDataStream.str());
}
else
{
ordered_json vendorDefinedVal;
vendorDefinedVal[std::get<VendorDefinedDescriptorTitle>(
std::get<VendorDefinedDescriptorInfo>(descriptorVal))] =
descDataStream.str();
descriptor["Value"].emplace_back(vendorDefinedVal);
}
descriptors.emplace_back(descriptor);
}
else
{
std::cerr << "Unknown descriptor type, type=" << descriptorType << "\n";
}
}
std::pair<int, std::vector<uint8_t>> QueryDeviceIdentifiers::createRequestMsg()
{
std::vector<uint8_t> requestMsg(
sizeof(pldm_msg_hdr) + PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES);
auto request = new (requestMsg.data()) pldm_msg;
auto rc = encode_query_device_identifiers_req(
instanceId, PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES, request);
return {rc, requestMsg};
}
void QueryDeviceIdentifiers::parseResponseMsg(pldm_msg* responsePtr,
size_t payloadLength)
{
uint8_t completionCode = PLDM_SUCCESS;
uint32_t deviceIdentifiersLen = 0;
uint8_t descriptorCount = 0;
uint8_t* descriptorPtr = nullptr;
uint8_t eid = getMCTPEID();
auto rc = decode_query_device_identifiers_resp(
responsePtr, payloadLength, &completionCode, &deviceIdentifiersLen,
&descriptorCount, &descriptorPtr);
if (rc)
{
std::cerr << "Decoding QueryDeviceIdentifiers response failed,EID="
<< unsigned(eid) << ", RC=" << rc << "\n";
return;
}
if (completionCode)
{
std::cerr << "QueryDeviceIdentifiers response failed with error "
"completion code, EID="
<< unsigned(eid) << ", CC=" << unsigned(completionCode)
<< "\n";
return;
}
ordered_json data;
data["EID"] = eid;
ordered_json descriptors;
while (descriptorCount-- && (deviceIdentifiersLen > 0))
{
DescriptorType descriptorType = 0;
variable_field descriptorData{};
rc = decode_descriptor_type_length_value(
descriptorPtr, deviceIdentifiersLen, &descriptorType,
&descriptorData);
if (rc)
{
std::cerr << "Decoding descriptor type, length and value failed,"
<< "EID=" << unsigned(eid) << ",RC=" << rc << "\n ";
return;
}
if (descriptorType != PLDM_FWUP_VENDOR_DEFINED)
{
std::vector<uint8_t> descData(
descriptorData.ptr, descriptorData.ptr + descriptorData.length);
updateDescriptor(descriptors, descriptorType, 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)
{
std::cerr << "Decoding Vendor-defined descriptor value"
<< "failed EID=" << unsigned(eid) << ", RC=" << rc
<< "\n ";
return;
}
auto vendorDescTitle = pldm::utils::toString(descriptorTitleStr);
std::vector<uint8_t> vendorDescData(
vendorDefinedDescriptorData.ptr,
vendorDefinedDescriptorData.ptr +
vendorDefinedDescriptorData.length);
updateDescriptor(descriptors, descriptorType,
std::make_tuple(vendorDescTitle, vendorDescData));
}
auto nextDescriptorOffset =
sizeof(pldm_descriptor_tlv().descriptor_type) +
sizeof(pldm_descriptor_tlv().descriptor_length) +
descriptorData.length;
descriptorPtr += nextDescriptorOffset;
deviceIdentifiersLen -= nextDescriptorOffset;
}
data["Descriptors"] = descriptors;
pldmtool::helper::DisplayInJson(data);
}
class RequestUpdate : public CommandInterface
{
public:
~RequestUpdate() = default;
RequestUpdate() = delete;
RequestUpdate(const RequestUpdate&) = delete;
RequestUpdate(RequestUpdate&&) = delete;
RequestUpdate& operator=(const RequestUpdate&) = delete;
RequestUpdate& operator=(RequestUpdate&&) = delete;
explicit RequestUpdate(const char* type, const char* name, CLI::App* app) :
CommandInterface(type, name, app)
{
app->add_option(
"--max_transfer_size", maxTransferSize,
"Specifies the maximum size, in bytes, of the variable payload allowed to\n"
"be requested by the FD via the RequestFirmwareData command that is contained\n"
"within a PLDM message. This value shall be equal to or greater than firmware update\n"
"baseline transfer size.")
->required();
app->add_option(
"--num_comps", numComps,
"Specifies the number of components that will be passed to the FD during the update.\n"
"The FD can use this value to compare against the number of PassComponentTable\n"
"commands received.")
->required();
app->add_option(
"--max_transfer_reqs", maxTransferReqs,
"Specifies the number of outstanding RequestFirmwareData commands that can be\n"
"sent by the FD. The minimum required value is '1' which the UA shall support.\n"
"It is optional for the UA to support a value higher than '1' for this field.")
->required();
app->add_option(
"--package_data_length", packageDataLength,
"This field shall be set to the value contained within\n"
"the FirmwareDevicePackageDataLength field that was provided in\n"
"the firmware package header. If no firmware package data was\n"
"provided in the firmware update package then this length field\n"
"shall be set to 0x0000.")
->required();
app->add_option(
"--comp_img_ver_str_type", compImgVerStrType,
"The type of string used in the ComponentImageSetVersionString\n"
"field. Possible values\n"
"{UNKNOWN->0, ASCII->1, UTF_8->2, UTF_16->3, UTF_16LE->4, UTF_16BE->5}\n"
"OR {0,1,2,3,4,5}")
->required()
->check([](const std::string& value) -> std::string {
static const std::set<std::string> validStrings{
"UNKNOWN", "ASCII", "UTF_8",
"UTF_16", "UTF_16LE", "UTF_16BE"};
if (validStrings.contains(value))
{
return "";
}
try
{
int intValue = std::stoi(value);
if (intValue >= 0 && intValue <= 255)
{
return "";
}
return "Invalid value. Must be one of UNKNOWN, ASCII, UTF_8, UTF_16, UTF_16LE, UTF_16BE, or a number between 0 and 255";
}
catch (const std::exception&)
{
return "Invalid value. Must be one of UNKNOWN, ASCII, UTF_8, UTF_16, UTF_16LE, UTF_16BE, or a number between 0 and 255";
}
});
app->add_option(
"--comp_img_ver_str_len", compImgVerStrLen,
"The length, in bytes, of the ComponentImageSetVersionString.")
->required();
app->add_option(
"--comp_img_set_ver_str", compImgSetVerStr,
"Component Image Set version information, up to 255 bytes.")
->required();
}
std::pair<int, std::vector<uint8_t>> createRequestMsg() override
{
variable_field compImgSetVerStrInfo{};
compImgSetVerStrInfo.ptr =
reinterpret_cast<const uint8_t*>(compImgSetVerStr.data());
compImgSetVerStrInfo.length =
static_cast<uint8_t>(compImgSetVerStr.size());
std::vector<uint8_t> requestMsg(
sizeof(pldm_msg_hdr) + sizeof(struct pldm_request_update_req) +
compImgSetVerStrInfo.length);
auto request = new (requestMsg.data()) pldm_msg;
auto rc = encode_request_update_req(
instanceId, maxTransferSize, numComps, maxTransferReqs,
packageDataLength, convertStringTypeToUInt8(compImgVerStrType),
compImgVerStrLen, &compImgSetVerStrInfo, request,
sizeof(struct pldm_request_update_req) +
compImgSetVerStrInfo.length);
return {rc, requestMsg};
}
void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
{
uint8_t cc = 0;
uint16_t fdMetaDataLen = 0;
uint8_t fdWillSendPkgData = 0;
auto rc =
decode_request_update_resp(responsePtr, payloadLength, &cc,
&fdMetaDataLen, &fdWillSendPkgData);
if (rc)
{
std::cerr << "Response Message Error: "
<< "rc=" << rc << ",cc=" << (int)cc << "\n";
return;
}
ordered_json data;
fillCompletionCode(cc, data, PLDM_FWUP);
if (cc == PLDM_SUCCESS)
{
data["FirmwareDeviceMetaDataLength"] = fdMetaDataLen;
data["FDWillSendGetPackageDataCommand"] =
std::format("0x{:02X}", fdWillSendPkgData);
}
pldmtool::helper::DisplayInJson(data);
}
private:
uint32_t maxTransferSize;
uint16_t numComps;
uint8_t maxTransferReqs;
uint16_t packageDataLength;
std::string compImgVerStrType;
uint8_t compImgVerStrLen;
std::string compImgSetVerStr;
};
void registerCommand(CLI::App& app)
{
auto fwUpdate =
app.add_subcommand("fw_update", "firmware update type commands");
fwUpdate->require_subcommand(1);
auto getStatus = fwUpdate->add_subcommand("GetStatus", "Status of the FD");
commands.push_back(
std::make_unique<GetStatus>("fw_update", "GetStatus", getStatus));
auto getFwParams = fwUpdate->add_subcommand(
"GetFwParams", "To get the component details of the FD");
commands.push_back(
std::make_unique<GetFwParams>("fw_update", "GetFwParams", getFwParams));
auto queryDeviceIdentifiers = fwUpdate->add_subcommand(
"QueryDeviceIdentifiers", "To query device identifiers of the FD");
commands.push_back(std::make_unique<QueryDeviceIdentifiers>(
"fw_update", "QueryDeviceIdentifiers", queryDeviceIdentifiers));
auto requestUpdate = fwUpdate->add_subcommand(
"RequestUpdate", "To initiate a firmware update");
commands.push_back(std::make_unique<RequestUpdate>(
"fw_update", "RequestUpdate", requestUpdate));
}
} // namespace fw_update
} // namespace pldmtool