pldmtool: Add support for UpdateComponent
Tested:
pldmtool fw_update UpdateComponent --component_classification ..
{
"CompletionCode": "SUCCESS",
"ComponentCompatibilityResponse": "Component can be updated",
"ComponentCompatibilityResponseCode": "0x00",
"UpdateOptionFlagsEnabled": "1",
"EstimatedTimeBeforeSendingRequestFirmwareData": "0s"
}
Change-Id: I4bbea500514c551fcbb75a06e109f1a9bfb7b7e4
Signed-off-by: Tom Joseph <rushtotom@gmail.com>
diff --git a/pldmtool/pldm_fw_update_cmd.cpp b/pldmtool/pldm_fw_update_cmd.cpp
index d550fa6..4e923a5 100644
--- a/pldmtool/pldm_fw_update_cmd.cpp
+++ b/pldmtool/pldm_fw_update_cmd.cpp
@@ -958,6 +958,195 @@
std::string compVerStr;
};
+class UpdateComponent : public CommandInterface
+{
+ public:
+ ~UpdateComponent() = default;
+ UpdateComponent() = delete;
+ UpdateComponent(const UpdateComponent&) = delete;
+ UpdateComponent(UpdateComponent&&) = delete;
+ UpdateComponent& operator=(const UpdateComponent&) = delete;
+ UpdateComponent& operator=(UpdateComponent&&) = delete;
+
+ explicit UpdateComponent(const char* type, const char* name,
+ CLI::App* app) : CommandInterface(type, name, app)
+ {
+ app->add_option(
+ "--component_classification", componentClassification,
+ "Classification value provided by the firmware package header information for\n"
+ "the component to be transferred.\n"
+ "Special values: 0x0000, 0xFFFF = reserved")
+ ->required();
+
+ app->add_option(
+ "--component_identifier", componentIdentifier,
+ "FD vendor selected unique value to distinguish between component images")
+ ->required();
+
+ app->add_option(
+ "--component_classification_index", componentClassificationIndex,
+ "The component classification index which was obtained from the GetFirmwareParameters\n"
+ "command to indicate which firmware component the information contained within this\n"
+ "command is applicable for")
+ ->required();
+
+ app->add_option(
+ "--component_comparison_stamp", componentComparisonStamp,
+ "FD vendor selected value to use as a comparison value in determining if a firmware\n"
+ "component is down-level or up-level. For the same component identifier, the greater\n"
+ "of two component comparison stamps is considered up-level compared to the other\n"
+ "when performing an unsigned integer comparison")
+ ->required();
+
+ app->add_option("--component_image_size", componentImageSize,
+ "Size in bytes of the component image")
+ ->required();
+
+ app->add_option(
+ "--update_option_flags", strUpdateOptionFlags,
+ "32-bit field, where each non-reserved bit represents an update option that can be\n"
+ "requested by the UA to be enabled for the transfer of this component image.\n"
+ "[2] Security Revision Number Delayed Update\n"
+ "[1] Component Opaque Data\n"
+ "[0] Request Force Update of component")
+ ->required();
+
+ app->add_option(
+ "--component_version_string_type", componentVersionStringType,
+ "The type of strings used in the ComponentVersionString\n"
+ "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("--component_version_string_length",
+ componentVersionStringLength,
+ "The length, in bytes, of the ComponentVersionString")
+ ->required();
+
+ app->add_option(
+ "--component_version_string", componentVersionString,
+ "Firmware component version information up to 255 bytes.\n"
+ "Contains a variable type string describing the component version")
+ ->required();
+ }
+
+ std::pair<int, std::vector<uint8_t>> createRequestMsg() override
+ {
+ variable_field componentVersionStringInfo{};
+
+ componentVersionStringInfo.ptr =
+ reinterpret_cast<const uint8_t*>(componentVersionString.data());
+ componentVersionStringInfo.length =
+ static_cast<uint8_t>(componentVersionString.size());
+
+ std::vector<uint8_t> requestMsg(
+ sizeof(pldm_msg_hdr) + sizeof(struct pldm_update_component_req) +
+ componentVersionStringInfo.length);
+
+ auto request = new (requestMsg.data()) pldm_msg;
+
+ bitfield32_t updateOptionFlags;
+ std::stringstream ss(strUpdateOptionFlags);
+ ss >> std::hex >> updateOptionFlags.value;
+ if (ss.fail())
+ {
+ std::cerr << "Failed to parse update option flags: "
+ << strUpdateOptionFlags << "\n";
+ return {PLDM_ERROR_INVALID_DATA, std::vector<uint8_t>()};
+ }
+
+ auto rc = encode_update_component_req(
+ instanceId, componentClassification, componentIdentifier,
+ componentClassificationIndex, componentComparisonStamp,
+ componentImageSize, updateOptionFlags,
+ convertStringTypeToUInt8(componentVersionStringType),
+ componentVersionStringLength, &componentVersionStringInfo, request,
+ sizeof(pldm_update_component_req) +
+ componentVersionStringInfo.length);
+
+ return {rc, requestMsg};
+ }
+
+ void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
+ {
+ uint8_t cc = 0;
+ uint8_t componentCompatibilityResp = 0;
+ uint8_t componentCompatibilityRespCode = 0;
+ bitfield32_t updateOptionFlagsEnabled{};
+ uint16_t timeBeforeReqFWData = 0;
+
+ auto rc = decode_update_component_resp(
+ responsePtr, payloadLength, &cc, &componentCompatibilityResp,
+ &componentCompatibilityRespCode, &updateOptionFlagsEnabled,
+ &timeBeforeReqFWData);
+
+ if (rc != PLDM_SUCCESS)
+ {
+ std::cerr << "Parsing UpdateComponent response failed: "
+ << "rc=" << rc << ",cc=" << static_cast<int>(cc) << "\n";
+ return;
+ }
+
+ ordered_json data;
+ fillCompletionCode(cc, data, PLDM_FWUP);
+
+ if (cc == PLDM_SUCCESS)
+ {
+ // Possible values:
+ // 0 – Component can be updated,
+ // 1 – Component will not be updated
+ data["ComponentCompatibilityResponse"] =
+ componentCompatibilityResp ? "Component will not be updated"
+ : "Component can be updated";
+
+ data["ComponentCompatibilityResponseCode"] =
+ std::format("0x{:02X}", componentCompatibilityRespCode);
+ data["UpdateOptionFlagsEnabled"] =
+ std::to_string(updateOptionFlagsEnabled.value);
+ data["EstimatedTimeBeforeSendingRequestFirmwareData"] =
+ std::to_string(timeBeforeReqFWData) + "s";
+ }
+
+ pldmtool::helper::DisplayInJson(data);
+ }
+
+ private:
+ uint16_t componentClassification;
+ uint16_t componentIdentifier;
+ uint8_t componentClassificationIndex;
+ uint32_t componentComparisonStamp;
+ uint32_t componentImageSize;
+ std::string strUpdateOptionFlags;
+ std::string componentVersionStringType;
+ uint8_t componentVersionStringLength;
+ std::string componentVersionString;
+};
+
void registerCommand(CLI::App& app)
{
auto fwUpdate =
@@ -987,6 +1176,11 @@
"To pass component table");
commands.push_back(std::make_unique<PassComponentTable>(
"fw_update", "PassComponentTable", passCompTable));
+
+ auto updateComp = fwUpdate->add_subcommand(
+ "UpdateComponent", "To request updating a specific firmware component");
+ commands.push_back(std::make_unique<UpdateComponent>(
+ "fw_update", "UpdateComponent", updateComp));
}
} // namespace fw_update